2008-11-19 16 views
14

मैं उस मैक्रो मेरे जैसे कुछ करने के लिए अनुमति होगी लिखने के लिए कोशिश कर रहा हूँ: FORMAT(a << "b" << c << d) है, और परिणाम एक स्ट्रिंग होगा - एक ostringstream बनाने, a...d डालने, और .str() लौटने के रूप में ही । कुछ ऐसा:सी ++ प्रारूप मैक्रो/इनलाइन ostringstream

string f(){ 
    ostringstream o; 
    o << a << "b" << c << d; 
    return o.str() 
} 

अनिवार्य रूप से, FORMAT(a << "b" << c << d) == f()

पहले, मैं करने की कोशिश की:

1: #define FORMAT(items)             \ 
    ((std::ostringstream&)(std::ostringstream() << items)).str() 

तो बहुत पहले आइटम एक सी स्ट्रिंग (const char *), यह हेक्स में तार का पता प्रिंट होगा, और अगले आइटम ठीक प्रिंट होगा है। यदि पहला आइटम std::string है, तो यह संकलित करने में विफल रहेगा (कोई मिलान करने वाला ऑपरेटर <<)।

यह:

2: #define FORMAT(items)             \ 
    ((std::ostringstream&)(std::ostringstream() << 0 << '\b' << items)).str() 

देता है कि क्या सही उत्पादन की तरह लगता है, लेकिन 0 और \b निश्चित रूप से स्ट्रिंग में मौजूद हैं।

निम्नलिखित काम करने के लिए लगता है, लेकिन चेतावनी (अस्थायी का पता लेने) के साथ संकलित:

3: #define FORMAT(items)             \ 
    ((std::ostringstream&)(*((std::ostream*)(&std::ostringstream())) << items)).str() 

क्या किसी को पता है कि क्यों 1 प्रिंट सी स्ट्रिंग के पते और std::string साथ संकलित करने के लिए विफल रहता है? 1 और 3 अनिवार्य रूप से वही नहीं हैं?

मुझे संदेह है कि सी ++ 0x वैरैडिक टेम्पलेट format(a, "b", c, d) संभव बना देगा। लेकिन क्या अब इसे हल करने का कोई तरीका है?

उत्तर

19

आप सभी काफी पहले से ही इस किसी न किसी है। लेकिन यह पालन करने के लिए थोड़ा चुनौतीपूर्ण है। तो मुझे का सारांश है कि तुम क्या कहा है पर एक चाकू ले जाने ...


यहाँ कठिनाइयां हैं कि:

  • हम एक अस्थायी ostringstream वस्तु के साथ खेल रहे हैं तो पतों लेने विपरीत है -indicated।

  • क्योंकि यह अस्थायी है, हम छोटे से ostream ऑब्जेक्ट को कास्टिंग के माध्यम से परिवर्तित नहीं कर सकते हैं।

  • दोनों कन्स्ट्रक्टर [स्पष्ट रूप से] और str() कक्षा ostringstream विधियां हैं। (हाँ, हम .str() उपयोग करने के लिए ostringstream वस्तु का उपयोग करते हुए सीधे ios::operator void*() लागू हवा होता है, एक सूचक की तरह अच्छा/बुरा मूल्य और नहीं एक स्ट्रिंग वस्तु लौटने की जरूरत है।।)

  • operator<<(...) मौजूद है के रूप में दोनों ostream तरीकों विरासत में मिला और वैश्विक कार्य सभी मामलों में यह ostream& संदर्भ देता है।

  • ostringstream()<<"foo" के लिए यहां विकल्प विरासत विधि ostream::operator<<(void*) और वैश्विक फ़ंक्शन operator<<(ostream&,const char*) हैं। विरासत में ostream::operator<<(void*) जीत गया क्योंकि हम वैश्विक फ़ंक्शन को आमंत्रित करने के लिए ostream ऑब्जेक्ट संदर्भ में कनवर्ट नहीं कर सकते हैं। [को कुडोस coppro!]


तो, यह बंद खींचने के लिए, हम की जरूरत है:

  • एक अस्थायी ostringstream आवंटित करें।
  • इसे ostream पर कनवर्ट करें।
  • डेटा संलग्न करें।
  • इसे वापस ostringstream पर कनवर्ट करें।
  • और str() का आह्वान करें।

आवंटन:ostringstream()

कनवर्ट करना: कई विकल्प हैं। दूसरों का सुझाव दिया है:

  • ostringstream() << std::string() // Kudos to *David Norman*
  • ostringstream() << std::dec // Kudos to *cadabra*

या क्या हम इस्तेमाल कर सकते हैं:

हम उपयोग नहीं कर सकते:

  • operator<<(ostringstream(), "")
  • (ostream &) ostringstream()

संलग्न: अब सीधे।

वापस परिवर्तित: हम सिर्फ (ostringstream&) इस्तेमाल कर सकते हैं। लेकिन dynamic_cast सुरक्षित होगा। संभावित घटना में dynamic_castNULL लौटा (यह नहीं होना चाहिए), निम्नलिखित .str() एक coredump ट्रिगर करेगा।

लागू str(): अनुमान।


इसे सभी एक साथ रखकर।

#define FORMAT(ITEMS)            \ 
    ((dynamic_cast<ostringstream &> (       \ 
     ostringstream() . seekp(0, ios_base::cur) << ITEMS) \ 
    ) . str()) 

संदर्भ:

4

आपके पास जो समस्या है, वह इस तथ्य से संबंधित है कि operator << (ostream&, char*) ओस्ट्रीम का सदस्य नहीं है, और आपका अस्थायी ओस्ट्रीम इंस्टेंस गैर-const संदर्भ से जुड़ नहीं सकता है। इसके बजाए, यह void* ओवरलोड चुनता है, जो ओस्ट्रीम का सदस्य है, और इस प्रकार उस प्रतिबंध नहीं है।

सबसे अच्छा (लेकिन सबसे आसान या सबसे सुरुचिपूर्ण, कल्पना की किसी भी खिंचाव से!) बूस्ट प्रीप्रोसेसर का उपयोग बड़ी संख्या में फ़ंक्शन ओवरलोड उत्पन्न करने के लिए करना होगा, प्रत्येक बड़ी संख्या में ऑब्जेक्ट्स पर टेम्पलेट किया गया है (इसमें छोड़ा गया है और using namespace std; कल्पना करते हुए):

#define MAKE_OUTPUT(z, n, data) \ 
    BOOST_PP_TUPLE_ELEM(2, 0, data) << BOOST_PP_CAT(BOOST_PP_TUPLE_ELEM(2, 1, data), n); 

#define MAKE_FORMAT(z, n, data) \ 
    template <BOOST_PP_ENUM_PARAMS_Z(z, BOOST_PP_INC(n), typename T)> \ 
    inline string format(BOOST_PP_ENUM_BINARY_PARAMS_Z(z, BOOST_PP_INC(n), T, p)) \ 
    { \ 
     ostringstream s; \ 
     BOOST_PP_REPEAT_##z(z, n, MAKE_OUTPUT, (s, p)); \ 
     return s.str(); \ 
    } 

यह बिल्कुल (परीक्षण के बिना यह लिखा था) काम करने के लिए गारंटी नहीं है, लेकिन यह मूल रूप से विचार है। इसके बाद आप BOOST_PP_REPEAT(N, MAKE_FORMAT,()) पर एन पैरामीटर तक ले जाने वाले कार्यों की एक श्रृंखला बनाने के लिए कॉल करते हैं जो आपकी स्ट्रिंग को प्रारूपित करेगा जैसा कि आप चाहते हैं (पसंद के पूर्णांक के साथ एन को प्रतिस्थापित करें। उच्च मान संकलन समय को नकारात्मक रूप से प्रभावित कर सकते हैं)। यह तब तक पर्याप्त होना चाहिए जब तक आप विविध टेम्पलेट्स के साथ एक कंपाइलर प्राप्त न करें। आपको बूस्ट प्रीप्रोसेसर दस्तावेज को पढ़ना चाहिए, इस तरह की चीजों के लिए इसमें बहुत शक्तिशाली विशेषताएं हैं। (आप बाद में #undef मैक्रो, BOOST_PP_REPEAT मंगलाचरण बुला कार्यों उत्पन्न करने के लिए के बाद कर सकते हैं)

+0

मैं पहले पैराग्राफ को ऊपर उठाऊंगा, लेकिन पूरा जवाब नहीं। –

+0

धन्यवाद, यह बहुत जानकारीपूर्ण है। मैंने बूस्ट का उपयोग नहीं किया है, यह देखना दिलचस्प है कि वहां क्या है। – cadabra

+0

क्यों बाध्य नहीं हो सकता है? –

1

यहाँ एक काम समाधान है:

#define FORMAT(items)             \ 
    ((std::ostringstream&)(std::ostringstream() << std::dec << items)).str() 

मैं काफी पहले तर्क के व्यवहार समझ में नहीं आता।

+0

std :: dec धारा 10 या दशमलव के रूप में संख्याओं को प्रस्तुत करने के लिए स्ट्रीम को बताता है। –

+0

यह एक सदस्य स्वरूपण का उपयोग करता है, फिर एक संदर्भ वापस करने के लिए, जो एक अस्थायी के लिए बाध्य किया जा सकता है। std :: dec पास करने के लिए एक अच्छा पैरामीटर है जो धारा के आउटपुट पर दुष्प्रभाव नहीं रखेगा। – coppro

0

मैक्रो के बजाय फ़ंक्शन का उपयोग क्यों न करें?

+0

क्योंकि वह सम्मिलन ऑपरेटर सिंटैक्स –

+0

का उपयोग करना चाहता है, मुझे लगता है कि मैं मैन्युअल रूप से एन टेम्पलेट फ़ंक्शन लिख सकता हूं, प्रत्येक तर्क के लिए एक और फिर प्रारूप (ए, "बी", सी, डी) का उपयोग करें। या उन्हें उत्पन्न करने के लिए coppro के समाधान का उपयोग करें। लेकिन न तो सुंदर है। – cadabra

2

यहाँ Cadabra की कि ostream राज्य के साथ गड़बड़ नहीं है की तरह एक जवाब है:

#define FORMAT(items)  static_cast<std::ostringstream &>((std::ostringstream() << std::string() << items)).str() 

मेरा मानना ​​है कि coppro के जवाब के पहले पैराग्राफ का वर्णन करता है क्यों बातें इस तरह से व्यवहार करते हैं।

+0

जीसीसी 4.0.1 पर संकलित नहीं करता है। यहां एक ऐसा है जो राज्य के साथ गड़बड़ नहीं करता है, लेकिन एक स्ट्रिंग की प्रतिलिपि बनाता है। #define प्रारूप (आइटम) \ ((std :: ostringstream और) (std :: ostringstream() << 0 << आइटम))। Str()। Substr (1) – cadabra

+0

चालाक ट्वीक, Cadabra –

+0

मैं केवल करने की कोशिश की माइक्रोसॉफ्ट विजुअलस्टूडियो 2008 –

20

यहां मैं उपयोग करता हूं। यह सब एक हेडर फ़ाइल में एक साफ वर्ग परिभाषा में फिट बैठता है।

अद्यतन: कोड में प्रमुख सुधार litb पर धन्यवाद।

// makestring.h: 

class MakeString 
{ 
    public: 
     std::stringstream stream; 
     operator std::string() const { return stream.str(); } 

     template<class T> 
     MakeString& operator<<(T const& VAR) { stream << VAR; return *this; } 
};

यह इस प्रकार से प्रयोग किया जाता है:

string myString = MakeString() << a << "b" << c << d;
+0

आपका दूसरा उदाहरण आपको एक अस्थायी स्ट्रिंग में पॉइंटर देने जा रहा है जिसे नष्ट कर दिया गया है - इसके साथ शुभकामनाएँ। –

+0

मैंने उस भाग को आजमाया, और यह वैसे भी संकलित नहीं करता है। मैंने इसे अपने जवाब से हटा दिया है! इनपुट के लिए धन्यवाद। –

+0

मैक्रो के लिए कोई आवश्यकता नहीं: टेम्पलेट स्ट्रिंगमेकर और ऑपरेटर << (टी कॉन्स और VAR) {स्ट्रीम << VAR; वापसी * ​​यह; } अपनी कक्षा परिभाषा के अंदर और "रेफरी" –

1

जब मैंने श्री का समाधान लिया (जिसे "पसंदीदा" चिह्नित किया गया, जिसे एक खूबसूरती से समझाया गया, और जी ++ के लिए पूरी तरह से काम कर रहा है), मैं एमएसवीसी ++ के साथ समस्याओं में भाग गया: इस मैक्रो के साथ निर्मित सभी तार खाली हो गए।

घंटे (और मेरे सिर को खरोंच करने और "reloaded" प्रश्न पूछने के बाद) बाद में, मुझे पता चला कि तलाश() कॉल अपराधी थी। मुझे यकीन है कि क्या MSVC++ उस के साथ अलग ढंग से करता नहीं हूँ, लेकिन भी MSVC++ के लिए Cadabra के

ostringstream() << std::dec 

काम करता है के साथ

ostringstream().seekp(0, ios_base::cur) 

की जगह।

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