2009-02-17 2 views
16

A previous question एक स्ट्रिंग को प्रिंट करने का एक अच्छा तरीका दिखाया गया है। जवाब शामिल va_copy:va_copy - दृश्य सी ++ को पोर्टिंग?

std::string format (const char *fmt, ...); 
{ 
    va_list ap; 
    va_start (ap, fmt); 
    std::string buf = vformat (fmt, ap); 
    va_end (ap); 
    return buf; 
} 


std::string vformat (const char *fmt, va_list ap) 
{ 
    // Allocate a buffer on the stack that's big enough for us almost 
    // all the time. 
    s ize_t size = 1024; 
    char buf[size]; 

    // Try to vsnprintf into our buffer. 
    va_list apcopy; 
    va_copy (apcopy, ap); 
    int needed = vsnprintf (&buf[0], size, fmt, ap); 

    if (needed <= size) { 
     // It fit fine the first time, we're done. 
     return std::string (&buf[0]); 
    } else { 
     // vsnprintf reported that it wanted to write more characters 
     // than we allotted. So do a malloc of the right size and try again. 
     // This doesn't happen very often if we chose our initial size 
     // well. 
     std::vector <char> buf; 
     size = needed; 
     buf.resize (size); 
     needed = vsnprintf (&buf[0], size, fmt, apcopy); 
     return std::string (&buf[0]); 
    } 

}

समस्या मैं आ रही है कि इसके बाद के संस्करण कोड विज़ुअल सी के बंदरगाह नहीं है ++ है क्योंकि यह va_copy (या यहां तक ​​__va_copy) प्रदान नहीं करता है। तो, क्या किसी को पता है कि उपरोक्त कोड को सुरक्षित रूप से कैसे बंद किया जाए? संभवतः, मुझे va_copy प्रतिलिपि करने की आवश्यकता है क्योंकि बनामप्रिंटफ उत्तीर्ण रूप से उत्तीर्ण va_list को संशोधित करता है।

+0

मैंने वीसी ++ में समान चीजों को लागू किया है और कभी भी 'va_copy() 'का उपयोग करने की आवश्यकता नहीं है। जब आप कॉपी का उपयोग किये बिना कोशिश करते हैं तो क्या होता है? –

+1

कौन जानता है ... यह काम पर दिखाई दे सकता है। यहां तक ​​कि अगर ऐसा होता है, तो इसका मतलब यह सुरक्षित नहीं है। – user48956

+1

स्पष्ट रूप से va_copy() एक सी 99 चीज है। वीसी ++ के लिए, आप एक प्रतिलिपि के बारे में चिंता किए बिना मूल va_list का उपयोग करके ठीक से ठीक होंगे। बनामप्रिंटफ पास की गई सूची को संशोधित करने की कोशिश नहीं करेगा। –

उत्तर

12

तुम सिर्फ एक नियमित रूप से काम कर रही है के साथ भाग प्राप्त करने में सक्षम होना चाहिए:

va_list apcopy = ap; 

यह तकनीकी रूप से गैर-पोर्टेबल और अपरिभाषित व्यवहार है, लेकिन यह सबसे संकलनकर्ता और आर्किटेक्चर के साथ काम करेंगे। X86 कॉलिंग सम्मेलन में, va_list एस केवल ढेर में पॉइंटर्स हैं और कॉपी करने के लिए सुरक्षित हैं।

+1

सच - va_copy को आमतौर पर "#define va_copy (d, s) ((d) = (s)) ", तो यह" #ifndef va_copy " –

+1

द्वारा संरक्षित 'पोर्टेबिलिटी' हेडर में फेंकने के लिए सबसे अच्छा हो सकता है यह जीसीसी के साथ एएमडी 64 पर टूटता है। –

+1

@ एलेक्स बी: फिर 'va_copy' का उपयोग करें, जो जीसीसी का समर्थन करता है। यह सवाल विशेष रूप से विजुअल सी ++ के बारे में था। –

5

एक बात आप कर सकते हैं अगर आप अन्यथा, vformat() समारोह की जरूरत नहीं है format() समारोह (untested) में इसके कार्यान्वयन के लिए कदम है:

#include <stdarg.h> 
#include <string.h> 
#include <assert.h> 
#include <string> 
#include <vector> 


std::string format(const char *fmt, ...) 
{ 
    va_list ap; 

    enum {size = 1024}; 

    // if you want a buffer on the stack for the 99% of the time case 
    // for efficiency or whatever), I suggest something like 
    // STLSoft's auto_buffer<> template. 
    // 
    // http://www.synesis.com.au/software/stlsoft/doc-1.9/classstlsoft_1_1auto__buffer.html 
    // 
    std::vector<char> buf(size); 

    // 
    // where you get a proper vsnprintf() for MSVC is another problem 
    // maybe look at http://www.jhweiss.de/software/snprintf.html 
    // 

    // note that vsnprintf() might use the passed ap with the 
    // va_arg() macro. This would invalidate ap here, so we 
    // we va_end() it here, and have to redo the va_start() 
    // if we want to use it again. From the C standard: 
    // 
    //  The object ap may be passed as an argument to 
    //  another function; if that function invokes the 
    //  va_arg macro with parameter ap, the value of ap 
    //  in the calling function is indeterminate and 
    //  shall be passed to the va_end macro prior to 
    //  any further reference to ap. 
    // 
    // Thanks to Rob Kennedy for pointing that out. 
    // 
    va_start (ap, fmt); 
    int needed = vsnprintf (&buf[0], buf.size(), fmt, ap); 
    va_end(ap); 

    if (needed >= size) { 
     // vsnprintf reported that it wanted to write more characters 
     // than we allotted. So do a malloc of the right size and try again. 
     // This doesn't happen very often if we chose our initial size 
     // well. 
     buf.resize(needed + 1); 

     va_start (ap, fmt); 
     needed = vsnprintf (&buf[0], buf.size(), fmt, ap); 
     va_end(ap); 

     assert(needed < buf.size()); 
    } 

    return std::string(&buf[0]); 
} 
+0

आपको vn_end और va_start को दूसरे कॉल से पहले vn_end और va_start को कॉल करना होगा, ' टी आप? –

+0

हम्म - ऐसा लगता है कि आप सही हैं। मुझे इससे पहले कभी एहसास नहीं हुआ। - फिक्स्ड (मुझे लगता है)। –

+0

@ जेनिकोडर: 'auto_buffer <> 'एसटीएल का हिस्सा नहीं है। –

8

Windows के लिए, आप बस अपने आप को va_copy परिभाषित कर सकते हैं:

#define va_copy(dest, src) (dest = src) 
+0

यदि va_copy पहले से परिभाषित नहीं है तो मैं इस समाधान के साथ जाऊंगा। एमएसवीसी केवल 2013 के लिए इसे परिभाषित करता है। कार्यान्वयन एक स्टैक पॉइंटर है जो एमएसवीसी करता है, लेकिन जीबीसी में 64 बिट आर्किटेक्चर के साथ समस्या पैदा करेगा। Http://www.bailopan.net/blog/?p=30 देखें –

1

va_copy() सीधे Visual Studio 2013 में शुरू होने का समर्थन किया जाता है। तो यदि आप उस उपलब्ध होने पर भरोसा कर सकते हैं, तो आपको कुछ भी करने की आवश्यकता नहीं है।