2011-08-24 20 views
7

एक सी ++ प्रोग्राम में, हमारे पास 3 स्ट्रीम हैं: stdin, stdout, और stderr। क्या मैं इन्हें कंसोल एप्लिकेशन में ओवरराइड कर सकता हूं और उन अनुप्रयोगों में उपयोग कर सकता हूं जो फॉर्म का उपयोग करते हैं?सी ++ धाराओं को ओवरराइड करें

उदाहरण के लिए, यदि कुछ बेस क्लास में, मेरे पास cout<< "..." है, तो क्या मैं कुछ दृश्य (जैसे विंडोज फॉर्म) पर "रीडायरेक्ट" कर सकता हूं?

+2

सी ++ "रूपों" नहीं है। –

+3

ठीक है, मेरा मतलब कुछ दृश्य है, कंसोल – Bakudan

+0

नोट नहीं, जो आमतौर पर स्ट्रीमिंग व्यवहार को ओवरराइड करना std :: streambuf इंटरफेस को कार्यान्वित करके किया जाता है। यह अपेक्षाकृत जटिल कार्य है, इसलिए आपको शायद प्रदान किए गए अन्य उत्तरों का उपयोग करना चाहिए। – Basilevs

उत्तर

9

क्या मैं कर रहा सिफारिश करेंगे एक वर्ग है जो इस तरह एक iostream इर्द-गिर्द घूमती चल रहा है: तो फिर

#include <iostream> 
#define LOG Log() 

class Log 
{ 
    public: 
     Log(){} 
     ~Log() 
     { 
     // Add an newline. 
     std::cout << std::endl; 
     } 


     template<typename T> 
     Log &operator << (const T &t) 
     { 
     std::cout << t; 
     return * this; 
     } 
}; 

, जब भी आप जहां डाटा चला जाता है बदलना चाहते हैं, तो आप सिर्फ वर्ग व्यवहार को बदलने। का तरीका यहां बताया वर्ग का उपयोग करें:

LOG << "Use this like an iostream."; 

[संपादित करें] आलू swatter के रूप में सुझाव दिया है, मैं अदालत के अलावा कुछ के साथ एक उदाहरण जोड़ देंगे:

#include <sstream> 
#define LOG Log() 

// An example with a string stream. 
class Log 
{ 
    private: 
     static std::stringstream buf; 
    public: 
     Log(){} 
     ~Log() 
     { 
     // Add an newline. 
     buf << std::endl; 
     } 


     template<typename T> 
     Log &operator << (const T &t) 
     { 
     buf << t; 
     return * this; 
     } 
}; 

// Define the static member, somewhere in an implementation file. 
std::stringstream Log::buf; 

कारण है कि आप की कोशिश करनी चाहिए का सवाल है यह एक स्ट्रिंग स्ट्रीम की तरह कुछ से विरासत के बजाय, मुख्य रूप से क्योंकि आप आसानी से बदल सकते हैं जहां लॉगर गतिशील रूप से आउटपुट करता है। उदाहरण के लिए, आप तीन अलग-अलग उत्पादन स्ट्रीम नहीं है, और रनटाइम पर के बीच स्वैप करने के लिए एक स्थिर सदस्य चर इस्तेमाल कर सकते हैं:

class Log 
{ 
    private: 
     static int outputIndex = 0; 
     // Add a few static streams in here. 
     static std::stringstream bufOne; 
     static std::stringstream bufTwo; 
     static std::stringstream bufThree; 
    public: 
     // Constructor/ destructor goes here. 

     template<typename T> 
     Log &operator << (const T &t) 
     { 
     // Switch between different outputs. 
     switch (outputIndex) 
     { 
      case 1: 
       bufOne << t; 
       break; 
      case 2: 
       bufTwo << t; 
      case 3: 
       bufThree << t; 
      default: 
       std::cout << t; 
       break; 
     } 
     return * this; 
     } 

     static void setOutputIndex(int _outputIndex) 
     { 
      outputIndex = _outputIndex; 
     } 
}; 

// In use 
LOG << "Print to stream 1"; 
Log::setOutputIndex(2); 
LOG << "Print to stream 2"; 
Log::setOutputIndex(3); 
LOG << "Print to stream 3"; 
Log::setOutputIndex(0); 
LOG << "Print to cout"; 

यह आसानी से प्रवेश से निपटने का एक शक्तिशाली तरीका बनाने के लिए विस्तारित किया जा सकता। आप filestreams जोड़ सकता है, std :: cerr, आदि

+1

मैं सहमत हूं (सिद्धांत रूप में)। –

+1

क्या सिर्फ एक नया ओस्ट्रीम व्युत्पन्न वर्ग बनाना और इसका उपयोग करना आसान नहीं होगा? –

+0

यह मूल रूप से खरोंच से शुरू हो रहा है। क्या होगा, उदाहरण के लिए, मौजूदा कोड स्वरूप मैनिपुलेटर्स या 'एंडल' का उपयोग करता है? साथ ही, यह उदाहरण यह बताता नहीं है कि 'cout' के अलावा कुछ के साथ इंटरफ़ेस कैसे करें। – Potatoswatter

1

हमेशा की तरह उपयोग करने के कुछ और पर एक iostream इंटरफ़ेस डाल करने के लिए <sstream> में stringstream/istringstream/ostringstream कक्षाओं का प्रयोग है।

सबसे अच्छा तरीका प्रकार istream & और ostream & के मापदंडों को cin, cout, आदि के उपयोग को बदलने के लिए है। यदि यह वास्तव में असंभव है, तो आप जिस स्ट्रीम को संशोधित करना चाहते हैं उस पर इंगित करने के लिए आप cin.rdbuf() को संशोधित कर सकते हैं, लेकिन यह बेहद हैकिश है और मल्टीथ्रेडिंग के साथ काम नहीं करेगा।

फॉर्म से टेक्स्ट को istringstream में कॉपी करें, फिर इसे मौजूदा कोड पर istream & के रूप में पास करें। जब आप पूरा कर लें, stringstream से ostream & के रूप में पारित परिणामों को पढ़ें जो cout को प्रतिस्थापित करें और इसे फ़ॉर्म में कॉपी करें।

istringstream & या के तर्कों की घोषणा न करें। बेस कक्षाओं का प्रयोग करें।

आप वास्तव में चाहते हैं, तो आप std::basic_stringbuf<char> उपवर्ग तथा पाठ खानों के लिए सीधे धाराओं टाई sync और underflow आभासी कार्यों रद्द कर सकते थे। लेकिन यह काफी उन्नत है और संभवतः इसके लायक नहीं है।

struct STDOUT_BLOCK : SLIST_ENTRY 
{ 
    char sz[]; 
}; 

class capturebuf : public std::stringbuf 
{ 
protected: 
    virtual int sync() 
    { 
     if (g_threadUI && g_hwndProgressDialog) { 
      // ensure NUL termination 
      overflow(0); 
      // allocate space 
      STDOUT_BLOCK* pBlock = (STDOUT_BLOCK*)_aligned_malloc(sizeof *pBlock + pptr() - pbase(), MEMORY_ALLOCATION_ALIGNMENT); 
      // copy buffer into string 
      strcpy(pBlock->sz, pbase()); 
      // clear buffer 
      str(std::string()); 
      // queue string 
      ::InterlockedPushEntrySList(g_slistStdout, pBlock); 
      // kick to log window 
      ::PostMessageA(g_hwndProgressDialog, WM_APP, 0, 0); 
     } 
     return __super::sync(); 
    } 
}; 

फिर अंदर main():

+1

आप 'cin.rdbuf()' को संशोधित नहीं करते हैं, आप इसे कॉल करते हैं। और यह "हैकिश" नहीं है, यह बिल्कुल सही है कि मानक लाइब्रेरी iostreams का उपयोग करने के लिए डिज़ाइन किया गया है। –

+0

@ बेन: मैं एक स्थानीय चर पर इंगित करने के लिए वैश्विक चर 'cin' के अंतर्निहित बफर सूचक को संशोधित करने का जिक्र कर रहा हूं, ताकि एक बुलाया गया कार्य वैश्विक के माध्यम से स्थानीय तक पहुंच सके। वह बहुत हैकिश है। – Potatoswatter

+0

फिर, मानक iostreams को उनके 'स्ट्रीमबफ' ऑब्जेक्ट्स को प्रतिस्थापित करने के लिए डिज़ाइन किया गया है, और वे सार्वजनिक एपीआई को इतना साफ करने के लिए प्रदान करते हैं। इस तरह मानक पुस्तकालय iostreams में polymorphism लागू किया जाता है। –

2

यहाँ कोड मैं विंडोज पर एक जीयूआई को std::cout रीडायरेक्ट करने के लिए उपयोग करते हैं

capturebuf altout; 
std::cout.set_rdbuf(&altout); 
बेशक

हैं तो आप अपनी विंडो में WM_APP संदेश को संभालने की ज़रूरत प्रक्रिया और स्लाइस्ट से तार खींचें। लेकिन यह cout पुनर्निर्देशन भाग को संभालता है।

जैसा कि jweyrich सही ढंग से नोट करता है, आपको को altout दायरे से बाहर जाने से पहले बदलने की आवश्यकता है। इस कोड को ऐसा करेंगे:

struct scoped_cout_streambuf_association 
{ 
    std::streambuf* orig; 
    scoped_cout_streambuf_association(std::streambuf& buf) 
     : orig(std::cout.rdbuf()) 
    { 
     std::cout.rdbuf(&buf); 
    } 

    ~scoped_cout_streambuf_association() 
    { 
     std::cout.rdbuf(orig); 
    } 
}; 

और main अंदर:

capturebuf altout; 
scoped_cout_streambuf_association redirect(altout); 
+0

बाहर निकलने से पहले मूल rdbuf को पुनर्स्थापित करने के लिए मत भूलना, अन्यथा यह दुर्घटनाग्रस्त हो सकता है। – jweyrich

+0

कोई कार्य 'set_rdbuf' नहीं है, केवल 'ios :: rdbuf' के दो अधिभार हैं। – Potatoswatter

+0

@ पोटाटोस्वाटर: दृश्य सी ++ में है, और यह कोड विंडोज-विशिष्ट है, हालांकि सामान्य विचार काफी पोर्टेबल है, विवरण नहीं हैं। –

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