2012-03-07 13 views
12

पर इंडेंट जोड़ने के लिए कैसे करें हमारे प्रोजेक्ट में हम डेटा के एक आसान पठनीय प्रारूप को मुद्रित करने के लिए हमारे ऑब्जेक्ट मॉडल में सी ++ स्ट्रीम ऑपरेटर (< <) का उपयोग करते हैं।धारा ऑपरेटर

std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) { 
    oStream << "[SomeMember1: " << iOwnClass._ownMember1 << "]\n"; 
    oStream << "[SomeMember2: " << iOwnClass._ownMember2 << "]\n"; 
} 

लॉगिंग में इस में परिणामी:: एक सरल उदाहरण यह है

[SomeMember1: foo] 
[SomeMember2: bar] 

क्या अब हम चाहते हैं कि ऑपरेटर का परिणाम इंडेंट करने के लिए सक्षम होने के लिए है। कुछ कॉलिंग क्लास इस तरह के परिणाम नहीं चाहते हैं, लेकिन प्रत्येक पंक्ति से पहले 2 रिक्त स्थान इंडेंट जोड़ना चाहते हैं। हम इंडेंट को निर्दिष्ट करने वाले हमारे वर्ग में एक सदस्य जोड़ सकते हैं, लेकिन यह एक सुरुचिपूर्ण समाधान प्रतीत नहीं होता है।

बेशक यह एक बहुत बड़ा मुद्दा नहीं है, लेकिन अगर यह काम करेगा तो हमारा लॉगिंग इतना अच्छा होगा।

धन्यवाद

उत्तर

21
आप यकीन है कि यह हमेशा जोड़ने के लिए/घटाना वांछित मांगपत्र (IMHO यह एक जोड़तोड़ का उपयोग कर अधिक से अधिक सुंदर है) एक गार्ड का उपयोग करके ऐसा कर सकता है

सरल समाधान ostream और वास्तविक streambuf जो छानने streambuf पर्ची के लिए है। की तरह कुछ:

IndentingOStreambuf indent(std::cout); 
// Indented output... 

indent क्षेत्र से बाहर हो जाता है, सब कुछ सामान्य करने के लिए देता है:

class IndentingOStreambuf : public std::streambuf 
{ 
    std::streambuf*  myDest; 
    bool    myIsAtStartOfLine; 
    std::string   myIndent; 
    std::ostream*  myOwner; 
protected: 
    virtual int   overflow(int ch) 
    { 
     if (myIsAtStartOfLine && ch != '\n') { 
      myDest->sputn(myIndent.data(), myIndent.size()); 
     } 
     myIsAtStartOfLine = ch == '\n'; 
     return myDest->sputc(ch); 
    } 
public: 
    explicit   IndentingOStreambuf( 
          std::streambuf* dest, int indent = 4) 
     : myDest(dest) 
     , myIsAtStartOfLine(true) 
     , myIndent(indent, ' ') 
     , myOwner(NULL) 
    { 
    } 
    explicit   IndentingOStreambuf(
          std::ostream& dest, int indent = 4) 
     : myDest(dest.rdbuf()) 
     , myIsAtStartOfLine(true) 
     , myIndent(indent, ' ') 
     , myOwner(&dest) 
    { 
     myOwner->rdbuf(this); 
    } 
    virtual    ~IndentingOStreambuf() 
    { 
     if (myOwner != NULL) { 
      myOwner->rdbuf(myDest); 
     } 
    } 
}; 

सम्मिलित करने के लिए, बस streambuf का एक उदाहरण बना सकते हैं।

(प्रवेश के लिए, मैं एक है कि थोड़ा अधिक जटिल है: LoggingOStreambuf__FILE__ और __LINE__ तर्कों के रूप लेता है, myIndent सेट इन तर्कों के साथ एक स्वरूपित स्ट्रिंग के अलावा एक समय टिकट के लिए, एक खरोज करने के लिए इसे रीसेट करता है स्ट्रिंग प्रत्येक उत्पादन के बाद, एक std::ostringstream में उत्पादन के सभी एकत्र करता है, और नाशक में यह outputs atomically myDest करने के लिए।)

+0

पूरी तरह से काम किया! हालांकि, मैंने कुछ बदलाव किए हैं, जैसे वृद्धि बढ़ाना और घटाना इंडेंट विधि। मेरे लॉग ठीक दिखते हैं कि मैं उन्हें अब कैसे चाहता हूं। धन्यवाद। –

+0

@ जेम्स: क्या आपके पास अधिक जटिल कोड भी उपलब्ध होगा? – Cookie

1

यह करने के लिए ऐसा नहीं है अच्छा तरीका एक वैश्विक चर है, जो बताता है खरोज जोड़ना है। कुछ ऐसा:

std::string OwnClassIndentation; 
std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) { 
    oStream << "[SomeMember1:" << OwnClassIndentation << iOwnClass._ownMember1 << "]\n"; 
    oStream << "[SomeMember2:" << OwnClassIndentation << iOwnClass._ownMember2 << "]\n"; 
} 

और फिर इसे उचित के रूप में सेट करें।

+2

आप केवल खरोज चाहते हैं जब एक विशेष प्रकार का है, जिसका 'ऑपरेटर <<' आप को परिभाषित outputting , तो आप 'std :: ios_base :: xalloc' और' std :: ios_base :: iword' t का उपयोग कर सकते हैं ओ प्रति स्ट्रीम इंडेंटेशन बनाए रखें। यदि आप 'dest << "कुछ स्ट्रिंग को आउटपुट करते समय भी इंडेंटेशन चाहते हैं," हालांकि, आपको फ़िल्टरिंग स्ट्रीमबफ का उपयोग करने की आवश्यकता होगी। –

+0

@ जेम्ससांज: अच्छा बिंदु - यह मेरे समाधान पर लागू होता है - इसके बारे में नहीं सोचा था। –

+0

यह निश्चित रूप से काम करेगा, लेकिन मुझे वास्तव में वैश्विक चर पसंद नहीं है, खासकर जब उन्हें केवल मेरे लॉग प्रारूपित करने की आवश्यकता होती है :) –

0

आप अपनी खुद की धारा वर्ग एक खरोज चर है कि बनाने के लिए और उस वर्ग के लिए endl ओवरराइड, खरोज डालने कर सकते हैं।

+2

जो इंडेंट नहीं करेगा पहला आउटपुट (हो सकता है या कोई सुविधा न हो), अंतिम पंक्ति के बाद रिक्त स्थान डालेगा (अपरिभाषित व्यवहार अगर फाइल टेक्स्ट मोड में खुलती है), और जब 'आउटपुट' आउटपुट होता है तो इंडेंट नहीं होगा। और निश्चित रूप से, आपको सभी '<<' ऑपरेटरों को भी परिभाषित करना होगा। यह 'स्ट्रीमबफ' के लिए एक नौकरी है, न कि 'ओस्ट्रीम' कक्षा। –

+0

मैंने इसके बारे में भी सोचा, लेकिन ओस्ट्रीम मुझे लिखना है, मेरे हाथों में नहीं है, इसलिए मैं अपना 'प्रकार बदल नहीं सकता। मुझे अपने लॉगिंग lib से ओस्ट्रीम का उपयोग करना होगा। स्ट्रीमबफ काम किया। –

+0

@W। गोमन: आप ओस्ट्रीम से प्राप्त कर सकते हैं, इसमें वर्चुअल विधियां हैं। – Dani

1

यह एक कस्टम धारा-जोड़तोड़ कि धारा के आंतरिक एक्स्टेंसिबल सरणी के एक शब्द में वांछित खरोज स्तरीय संग्रहीत करता है का उपयोग किया जा सकता है। आप फ़ंक्शन ios_base::xalloc फ़ंक्शन का उपयोग करके ऐसे शब्द का अनुरोध कर सकते हैं। यह फ़ंक्शन आपको आपके शब्द का सूचकांक देगा। आप ios_base::iword का उपयोग कर इसे एक्सेस कर सकते हैं। एक तरह से लागू करने के लिए है कि इस होगा:

struct indent { 
    indent(int level) : level(level) {} 
private: 
    friend std::ostream& operator<<(std::ostream& stream, const indent& val); 

    int level; 
}; 

std::ostream& operator<<(std::ostream& stream, const indent& val) { 
    for(int i = 0; i < val.level; i++) { 
     stream << " "; 
    } 
    return stream; 
} 

std::ostream& operator<<(std::ostream & oStream, const OwnClass& iOwnClass) { 
    oStream << indent(oStream.iword(index)) << "[SomeMember1: " << 
       iOwnClass._ownMember1 << "]\n"; 
    oStream << indent(oStream.iword(index)) << "[SomeMember2: " << 
       iOwnClass._ownMember2 << "]\n"; 
} 

आप जहां index स्टोर करने के लिए यह पता लगाने की होगा। यह प्रभावी रूप से आप स्ट्रीम में कस्टम राज्य जोड़ने (ध्यान दें कि यह धागा सुरक्षित बाहर के बॉक्स नहीं होगा) करने के लिए अनुमति देता है। हर समारोह है कि खरोज चाहता धारा करने का अनुरोध किया खरोज जोड़ने के लिए, और इसे फिर से घटाना जब यह किया जाता है चाहिए।

class indent_guard { 
public: 
    indent_guard(int level, std::ostream& stream, int index) 
    : level(level), 
     stream(stream), 
     index(index) 
    { 
     stream.iword(index) += level; 
    } 

    ~indent_guard() { 
     stream.iword(index) -= level; 
    } 

private: 
    int level; 
    std::ostream& stream; 
    int index; 
}; 

आप इस तरह इसका इस्तेमाल कर सकते:

void some_func() { 
    indent_guard(2, std::cout, index); 

    // all output inside this function will be indented by 2 spaces 

    some_func(); // recursive call - output will be indented by 4 spaces 

    // here it will be 2 spaces again 
} 
+0

कम से कम आप 'iostream' :-) जानते हैं। यह स्पष्ट रूप से कस्टम वर्गों के लिए किसी भी विशेष मैनिपुलेटर्स को संभालने का तरीका है। इससे पहले मदद नहीं होती है कि पहला आउटपुट आपके द्वारा परिभाषित प्रकार नहीं है; ऐसे मामलों में, आपको 'स्ट्रीमबफ' स्तर पर आउटपुट को अवरुद्ध करने की आवश्यकता है। –

+0

यह मेरी समस्या को पूरी तरह हल नहीं करता है, लेकिन निश्चित रूप से "जानना अच्छा" है। धन्यवाद। –

+0

आपका 'some_func() 'एक स्टैक ओवरफ़्लो का कारण बन रहा है। – uckelman