2013-05-15 26 views
11

आम तौर पर, कार्यों के साथ सी ++ 11 वैरिएडिक टेम्पलेट सुविधा का उपयोग करने के लिए फ़ंक्शन तर्क सूची में अंतिम होने के लिए विविधता-आधारित फ़ंक्शन तर्कों की आवश्यकता होती है। एक अपवाद है; यदि वे सी-स्तरीय विविधता तर्क हैं, तो वे अंतिम-अंतिम-अंतिम तर्क हैं, जो अंतिम रूप से मृत होना चाहिए।सी ++ और सी वैरिएडिक तर्कों का एक साथ उपयोग कैसे किया जा सकता है?

template < typename ...Args > 
int super_printf(Something x, Args &&...a, ...); 

मैं कभी कभी बेतरतीब ढंग से सी ++ के बारे में सोचते हैं, और मैं कैसे इस तरह के एक समारोह लागू किया जा सकता सोचा। मैंने पहली बार से तर्कों की सामान्य रिकर्सिव छीलने के बारे में सोचा, तो मुझे याद आया कि सी-स्तरीय varargs कैस्केड नहीं है। मुझे उन्हें तुरंत एक निश्चित va_list में बदलना है।

template < typename ...Args > 
int super_vaprintf(Something x, std::va_list &aa, Args &&...a); 
// Note that "aa" is passed by reference. 

template < typename ...Args > 
int super_printf(Something x, Args &&...a, ...) 
{ 
    std::va_list args2; 
    int   result; 

    va_start(args2, XXX); // (A) 
    try { 
     result = super_vaprintf(x, args2, std::forward<Args>(a)...); 
    } catch (...) { 
     va_end(args2); // (1) 
     throw; 
    } 
    va_end(args2); // (2) 
    return result; 

    // Can (1) and (2) be compacted with RAII using a custom deleter lambda 
    // in std::unique_ptr or something? Remember that "va_end" is a macro! 
} 

सामान्य सी ++ variadic पुनरावर्ती छीलने super_vaprintf कॉल में होता है। लाइन (ए) पर, XXX, "ए" या "ए ..." के स्थान पर क्या होता है? क्या होता है यदि खाली है, तो x इसके बजाय वहां जाता है? यदि वह अंतिम प्रश्न सत्य है, तो क्या हम x पर कोई खराब नहीं हैं; कि भिन्नता के अलावा कोई तर्क नहीं है? (और अगर यह सच है, हम कैसे कोड अन्यथा एक्स जब एक खाली है, और एक उपयोग करने के लिए conditionalize है?)

...

मैं बस की मेरी नकल को देखा यहां किसी भी सहायता के लिए सी ++ 11 मानक। ऐसा कोई प्रतीत नहीं होता है। यह सी ++ कमेटी के लिए इसे ठीक करने के लिए वापस आने का अनुरोध करेगा, लेकिन मुझे यकीन नहीं है कि सी ++ varargs सबकुछ लेने के बिना ऐसा कोई फ़ंक्शन कहलाया जा सकता है। क्या मै गलत हु; सी ++ और सी varargs दोनों का उपयोग करने के लिए एक समारोह कॉल किया जा सकता है? या बेवकूफ (टेम्पलेट) इंस्टेंटेशन ट्रिक्स के संदर्भ में घोषणाओं के लिए केवल उपयोगी मिश्रण कर रहा है?

+0

बस इसे पोस्ट करने के बाद, मुझे एहसास हुआ कि शायद "सुपर_प्रिंटफ (कुछ {}, 1, 2, 3, 4)" कुछ कुछ तर्कों को सी-लेवल ("3" और "4" इस मामले में)। – CTMacUser

+1

वे ऑर्थोगोनल हैं, क्योंकि सी वीए रन-टाइम पर काम करता है, और सी ++ वैरैडिक टेम्पलेट संकलन समय –

+0

@CTMacUser पर काम करते हैं: हाँ, यह काम करेगा। सवाल अभी भी बना रहता है कि यह कैसे उपयोगी है, और मैं मेटाप्रोग्रामिंग में कहूंगा । हो सकता है कि आप विविध टेम्पलेट प्रस्तावों को पढ़ने में अधिक अंतर्दृष्टि पा सकें, वे बता सकते हैं कि यह क्यों संभव है। – PlasmaHH

उत्तर

0

मैंने जीसीसी 4.8 के साथ एक संकलित वेबसाइट (कोलिरू) पर इस कोड को आजमाया है, और परिणाम उदास दिखते हैं। मुझे नहीं पता कि यह विशेष रूप से जीसीसी है, या यदि वहां के सभी अन्य कंपाइलर्स कुछ समान करते हैं। तो क्या अन्य कंपाइलर्स (क्लैंग, विजुअल सी ++, इंटेल इत्यादि) वाले लोग इसे आजमा सकते हैं?

#include <cstdarg> 
#include <iostream> 
#include <ostream> 
#include <utility> 

template < typename ...Args > 
int super_vaprintf(long, std::va_list &, Args &&...) 
{ 
    return 17; 
} 

template < typename ...Args > 
int super_printf(long x, Args &&...a, ...) 
{ 
    std::va_list args2; 
    int   result; 

    va_start(args2, a); // (A) 
    try { 
     result = super_vaprintf(x, args2, std::forward<Args>(a)...); 
    } catch (...) { 
     va_end(args2); 
     throw; 
    } 
    va_end(args2); 
    return result; 
} 

int main() { 
    std::cout << super_printf<int, int>(0L, 1, 2, 3, 4, 5) << std::endl; // (B) 
    return 0; 
} 

लाइन (बी) पर super_printf करने के लिए कॉल स्पष्ट रूप से सी सेट ++ दो int प्रविष्टियों के लिए varargs। इससे फ़ंक्शन का उपयोग तर्क 1 और 2 सी ++ varargs और बाद वाले तीन सी varargs के रूप में करेगा।

लाइन (ए) पर, कंपाइलर जोर देता है कि इसमें a वाला कोड "..." कहीं है। तो मैं इसे बदलता हूं:

va_start(args2, a...); // (A) 

मुझे तर्कों की गलत संख्या होने के बारे में एक और त्रुटि मिलती है। यह समझ में आता है क्योंकि a दो तर्कों तक फैलता है। यदि मैं लाइन (बी) को एक सी ++ वैरगा में बदलता हूं:

std::cout << super_printf<int>(0L, 1, 2, 3, 4, 5) << std::endl; // (B) 

यह ठीक काम करता है। अगर मैं हटाने सी ++ पूरी तरह varargs:

std::cout << super_printf<>(0L, 1, 2, 3, 4, 5) << std::endl; // (B) 

हम गलत संख्या या तर्क त्रुटि फिर से मिलता है, क्योंकि a शून्य की लंबाई है)।हम ऐसा करते हैं तो जब a खाली है:

va_start(args2, x /*a...*/); // (A) 

कोड फिर से काम करता है, हालांकि वहाँ एक चेतावनी के बारे में x पिछले नामित पैरामीटर नहीं किया जा रहा है।

हम उदाहरण को दूसरे तरीके से देख सकते हैं। आइए रीसेट करें:

va_start(args2, a...); // (A) 
//... 
std::cout << super_printf(0L, 1, 2, 3, 4, 5) << std::endl; // (B) 

जहां पहले के बाद सभी तर्कों को सी ++ varargs के रूप में समूहीकृत किया जाता है। हम निश्चित रूप से va_start में भी बहुत-बहस-त्रुटि त्रुटि प्राप्त करते हैं। मैं प्रगतिशील रूप से पिछली तर्कों पर टिप्पणी करता हूं। यह तब काम करता है जब वास्तव में दो तर्क शेष होते हैं (जो a को बिल्कुल एक तर्क देता है)।

एक त्रुटि भी होती है जब केवल एक तर्क छोड़ दिया जाता है, लेकिन त्रुटि संदेश स्पष्ट रूप से "गलत राशि" के बजाय "बहुत कम" तर्क कहता है। पहले की तरह, मैंने लाइन (ए) में "x" के लिए "a..." स्विच किया, और कोड स्वीकार कर लिया गया था, लेकिन कोई चेतावनी नहीं थी। तो ऐसा लगता है कि जब मैं स्पष्ट रूप से super_printf के लिए super_printf के लिए "<Whatever>" को स्पष्ट रूप से शामिल करता हूं (बी) मुझे उनमें शामिल नहीं होने पर एक अलग पार्सर त्रुटि पथ मिलता है, हालांकि दोनों पथ एक ही निष्कर्ष पर जाते हैं।

समिति कि वे कुछ ....

+0

आपके अंतिम अनुच्छेद के बारे में ... नहीं, वास्तव में नहीं। –

+0

@ लाइटनेसरेसेसिन ऑर्बिट, वास्तव में [हाँ] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1790)। – CTMacUser

+0

: पी बीटीडब्ल्यू कोलिरु क्लेंग का समर्थन करता है –

4

अनदेखी जब आप एक समारोह जिसका आखिरी पैरामीटर एक पैकेट है कहते हैं, सभी तर्क है कि पैक का हिस्सा बन बताने के लिए समय। va_args के लिए कुछ भी नहीं बचा है। स्पष्ट टेम्पलेट तर्कों का आपका उपयोग भ्रामक है क्योंकि वे अनन्य नहीं हैं; वे बस निहित तर्क से पहले।

(& super_printf<int, int>) (0L, 1, 2, 3, 4, 5) 

यह काफी काल्पनिक है, लेकिन अब आप va_start को पारित करने के लिए कुछ भी नहीं है की समस्या है:

कटौती को हराने के लिए, आप एक संदर्भ की जरूरत है।

उपयोगकर्ताओं को उचित इंटरफ़ेस प्रदान करने के लिए, बस दो सूचियों के बीच एक पैरामीटर जोड़ें।

struct va_separator {}; // Empty; ABI may elide allocation. 

template < typename ...Args > 
int super_printf(Something x, Args &&...a, va_separator, ...); 

यह super_printf पैक और एक स्पष्ट विभाजक तर्क को परिभाषित करने के लिए दोनों स्पष्ट तर्क की आवश्यकता होगी। लेकिन आप वैकल्पिक रूप से एक सार्वजनिक फ़ंक्शन प्रदान कर सकते हैं जो पैक द्वारा अपने सभी तर्क प्राप्त करता है, फिर विभाजक से आगे पैक तत्वों सहित एक स्पष्ट तर्क सूची का उपयोग करके विभाजक और आगे super_printf पर पाता है।

संबंधित मुद्दे

 संबंधित मुद्दे