2009-11-19 14 views
18

मैं दो (या अधिक) धाराओं को एक में लिखना चाहता हूं। मेरा लक्ष्य यह है कि किसी भी आउटपुट को cout, cerr पर निर्देशित किया गया है, और clog को भी मूल स्ट्रीम के साथ फ़ाइल में आउटपुट किया जा सकता है। (। जब चीजें कंसोल के लिए लॉग इन कर रहे हैं, उदाहरण के लिए बंद करने के बाद, मैं अभी भी वापस जाकर उत्पादन देखने में सक्षम हो करना चाहते हैं।)मैं आउटपुट स्ट्रीम कैसे बना सकता हूं, इसलिए आउटपुट एक साथ कई स्थानों पर जाता है?

मैं इस तरह कुछ करने का सोच रहा था:

class stream_compose : public streambuf, private boost::noncopyable 
{ 
public: 
    // take two streams, save them in stream_holder, 
    // this set their buffers to `this`. 
    stream_compose; 

    // implement the streambuf interface, routing to both 
    // ... 

private: 
    // saves the streambuf of an ios class, 
    // upon destruction restores it, provides 
    // accessor to saved stream 
    class stream_holder; 

    stream_holder mStreamA; 
    stream_holder mStreamB; 
}; 

जो सीधे-आगे पर्याप्त लगता है।

// anything that goes to cout goes to both cout and the file 
stream_compose coutToFile(std::cout, theFile); 
// and so on 

मैं भी boost::iostreams को देखा है, लेकिन कुछ भी संबंधित नहीं देखा था: तो मुख्य में कॉल कुछ जैसा होगा।

क्या इसे पूरा करने के लिए कोई और बेहतर/सरल तरीका है?

+0

मुझे यहां जवाब पसंद है: http://stackoverflow.com/a/13978705/2662901 – feetwet

उत्तर

11

आप Boost.IOStreams में कुछ भी नहीं मिला है। क्या आपने tee_device पर विचार किया था?

12

यदि आपके पास यह पूरी तरह से stdlib के भीतर करना है तो आपके पास सही डिजाइन — है।

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

वैकल्पिक रूप से, और आप केवल, stdout & stderr (जो आम है) को संभालने के लिए मानक यूनिक्स tee कार्यक्रम (या अपने मंच पर समकक्ष) के माध्यम से अपने कार्यक्रम चलाने चाहते हैं, या तो जब कार्यक्रम लागू यह अपने आप को करने से, या फोर्किंग द्वारा कार्यक्रम के भीतर, धाराओं को उपयुक्त के रूप में स्थापित करना, आदि

संपादित करें: आपने मुझे सोचा, और मुझे यह पता होना चाहिए कि यह अधिकार कैसे प्राप्त करें। यहां मेरा firstapproximation है।

#ifndef INCLUDE_GUARD_A629F54A136C49C9938CB33EF8EDE676 
#define INCLUDE_GUARD_A629F54A136C49C9938CB33EF8EDE676 

#include <cassert> 
#include <cstring> 
#include <streambuf> 
#include <map> 
#include <vector> 

template<class CharT, class Traits=std::char_traits<CharT> > 
struct basic_streamtee : std::basic_streambuf<CharT, Traits> { 
    typedef std::basic_ios<CharT, Traits> Stream; 
    typedef std::basic_streambuf<CharT, Traits> StreamBuf; 

    typedef typename StreamBuf::char_type char_type; 
    typedef typename StreamBuf::traits_type traits_type; 
    typedef typename StreamBuf::int_type int_type; 
    typedef typename StreamBuf::pos_type pos_type; 
    typedef typename StreamBuf::off_type off_type; 

    basic_streamtee() : _key_buf(0) {} 
    basic_streamtee(Stream& a, Stream& b) : _key_buf(0) { 
     this->pubimbue(a.rdbuf()->getloc()); 
     _set_key_buf(a.rdbuf()); 
     insert(a); 
     insert(b); 
    } 
    ~basic_streamtee() { 
     sync(); 
     for (typename std::map<Stream*, StreamBuf*>::iterator i = _bufs.begin(); 
      i != _bufs.end(); 
      ++i) 
     { 
      StreamBuf* old = i->first->rdbuf(i->second); 
      if (old != this) { 
       old->pubsync(); 
      } 
     } 
    } 

    // add this functionality? 
    // streambufs would be unconnected with a stream 
    // easy to do by changing _bufs to a multimap 
    // and using null pointers for the keys 
    //void insert(StreamBuf* buf); 
    //void remove(StreamBuf* buf); 

    void insert(Stream& s) { 
     sync(); 
     if (!_bufs.count(&s)) { 
      if (!_key_buf) { 
       _set_key_buf(s.rdbuf()); 
      } 
      _bufs[&s] = s.rdbuf(this); 
     } 
    } 
    void remove(Stream& s) { 
     sync(); 
     typename std::map<Stream*, StreamBuf*>::iterator i = _bufs.find(&s); 
     if (i != _bufs.end()) { 
      StreamBuf* old = i->second; 
      i->first->rdbuf(i->second); 
      _bufs.erase(i); 

      if (old == _key_buf) { 
       _set_key_buf(_bufs.empty() ? 0 : _bufs.begin()->second); 
      } 
     } 
    } 

private: 
    basic_streamtee(basic_streamtee const&); // not defined 
    basic_streamtee& operator=(basic_streamtee const&); // not defined 

    StreamBuf* _key_buf; 
    std::map<Stream*, StreamBuf*> _bufs; 

    void _set_key_buf(StreamBuf* p) { 
     //NOTE: does not sync, requires synced already 
     _key_buf = p; 
     _update_put_area(); 
    } 
    void _update_put_area() { 
     //NOTE: does not sync, requires synced already 
     if (!_key_buf) { 
      this->setp(0, 0); 
     } 
     else { 
      this->setp((_key_buf->*&basic_streamtee::pbase)(), 
         (_key_buf->*&basic_streamtee::epptr)()); 
     } 
    } 


#define FOREACH_BUF(var) \ 
for (typename std::map<Stream*, StreamBuf*>::iterator var = _bufs.begin(); \ 
var != _bufs.end(); ++var) 


    // 27.5.2.4.1 Locales 
    virtual void imbue(std::locale const& loc) { 
     FOREACH_BUF(iter) { 
      iter->second->pubimbue(loc); 
     } 
    } 


    // 27.5.2.4.2 Buffer management and positioning 
    //virtual StreamBuf* setbuf(char_type* s, std::streamsize n); // not required 
    //virtual pos_type seekoff(off_type off, std::ios_base::seekdir way, 
    //       std::ios_base::openmode which); // not required 
    //virtual pos_type seekpos(pos_type sp, std::ios_base::openmode which); // not required 
    virtual int sync() { 
     if (!_key_buf) { 
      return -1; 
     } 
     char_type* data = this->pbase(); 
     std::streamsize n = this->pptr() - data; 
     (_key_buf->*&basic_streamtee::pbump)(n); 
     FOREACH_BUF(iter) { 
      StreamBuf* buf = iter->second; 
      if (buf != _key_buf) { 
       buf->sputn(data, n); //BUG: ignores put errors 
       buf->pubsync(); //BUG: ignroes errors 
      } 
     } 
     _key_buf->pubsync(); //BUG: ignores errors 
     _update_put_area(); 
     return 0; 
    } 


    // 27.5.2.4.3 Get area 
    // ignore input completely, teeing doesn't make sense 
    //virtual std::streamsize showmanyc(); 
    //virtual std::streamsize xsgetn(char_type* s, std::streamsize n); 
    //virtual int_type underflow(); 
    //virtual int_type uflow(); 


    // 27.5.2.4.4 Putback 
    // ignore input completely, teeing doesn't make sense 
    //virtual int_type pbackfail(int_type c); 


    // 27.5.2.4.5 Put area 
    virtual std::streamsize xsputn(char_type const* s, std::streamsize n) { 
     assert(n >= 0); 
     if (!_key_buf) { 
      return 0; 
     } 

     // available room in put area? delay sync if so 
     if (this->epptr() - this->pptr() < n) { 
      sync(); 
     } 
     // enough room now? 
     if (this->epptr() - this->pptr() >= n) { 
      std::memcpy(this->pptr(), s, n); 
      this->pbump(n); 
     } 
     else { 
      FOREACH_BUF(iter) { 
       iter->second->sputn(s, n); 
       //BUG: ignores put errors 
      } 
      _update_put_area(); 
     } 
     return n; 
    } 
    virtual int_type overflow(int_type c) { 
     bool const c_is_eof = traits_type::eq_int_type(c, traits_type::eof()); 
     int_type const success = c_is_eof ? traits_type::not_eof(c) : c; 
     sync(); 
     if (!c_is_eof) { 
      char_type cc = traits_type::to_char_type(c); 
      xsputn(&cc, 1); 
      //BUG: ignores put errors 
     } 
     return success; 
    } 

#undef FOREACH_BUF 
}; 

typedef basic_streamtee<char> streamtee; 
typedef basic_streamtee<wchar_t> wstreamtee; 

#endif 

अब, यह परीक्षण पूरा से दूर है (जब यह टूट जाता है, तो आप दोनों टुकड़े रखने के लिए मिलता है।), लेकिन यह काम करने के लिए लगता है:

#include "streamtee.hpp" 

#include <cassert> 
#include <iostream> 
#include <sstream> 

int main() { 
    using namespace std; 
    { 
     ostringstream a, b; 
     streamtee tee(a, b); 
     a << 42; 
     assert(a.str() == "42"); 
     assert(b.str() == "42"); 
    } 
    { 
     ostringstream a, b; 
     streamtee tee(cout, a); 
     tee.insert(b); 
     a << 42 << '\n'; 
     assert(a.str() == "42\n"); 
     assert(b.str() == "42\n"); 
    } 
    return 0; 
} 

यह एक साथ रखें एक फ़ाइल के साथ:

#include "streamtee.hpp" 

#include <iostream> 
#include <fstream> 

struct FileTee { 
    FileTee(std::ostream& stream, char const* filename) 
    : file(filename), buf(file, stream) 
    {} 

    std::ofstream file; 
    streamtee buf; 
}; 

int main() { 
    using namespace std; 

    FileTee out(cout, "stdout.txt"); 
    FileTee err(clog, "stderr.txt"); 
    streambuf* old_cerr = cerr.rdbuf(&err.buf); 

    cout << "stdout\n"; 
    clog << "stderr\n"; 

    cerr.rdbuf(old_cerr); 
    // watch exception safety 

    return 0; 
} 
+1

इसलिए मुझे मेरा कार्यान्वयन भी मिला, हमारा कोड काफी करीब था, हालांकि मैंने अपनी दो कक्षाओं में विभाजित किया (एक टी के स्ट्रीमबफ, अन्य tee'd धाराओं, जो पूर्व टीई का इस्तेमाल किया)। हालांकि, एरिक ने इंगित किया है कि बढ़ावा पहले से ही लागू करता है, इसलिए यदि मैं अपना काम कर सकता हूं तो मैं कैसे उम्मीद कर रहा हूं, मैं उसका जवाब स्वीकार करूंगा। लेकिन आपको पता होना चाहिए कि यह पोस्ट बहुत उपयोगी था, मैं इससे बहुत कुछ सीखता हूं। :) – GManNickG

+0

अच्छा समाधान! टूटा हुआ लिंक: https://bitbucket.org/kniht/scraps/src/01ecc1346bc5/cpp/kniht/streamtee.hpp –

11

मैं एक कस्टम स्ट्रीम बफर लिखूंगा जो आपके सभी लिंक किए गए धाराओं के बफर को केवल आगे भेज देगा।

#include <iostream> 
#include <fstream> 
#include <vector> 
#include <algorithm> 
#include <functional> 

class ComposeStream: public std::ostream 
{ 
    struct ComposeBuffer: public std::streambuf 
    { 
     void addBuffer(std::streambuf* buf) 
     { 
      bufs.push_back(buf); 
     } 
     virtual int overflow(int c) 
     { 
      std::for_each(bufs.begin(),bufs.end(),std::bind2nd(std::mem_fun(&std::streambuf::sputc),c)); 
      return c; 
     } 

     private: 
      std::vector<std::streambuf*> bufs; 

    }; 
    ComposeBuffer myBuffer; 
    public: 
     ComposeStream() 
      :std::ostream(NULL) 
     { 
      std::ostream::rdbuf(&myBuffer); 
     } 
     void linkStream(std::ostream& out) 
     { 
      out.flush(); 
      myBuffer.addBuffer(out.rdbuf()); 
     } 
}; 
int main() 
{ 
    ComposeStream out; 
    out.linkStream(std::cout); 
    out << "To std::cout\n"; 

    out.linkStream(std::clog); 
    out << "To: std::cout and std::clog\n"; 

    std::ofstream file("Plop"); 
    out.linkStream(file); 
    out << "To all three locations\n"; 
} 
+0

आपके रास्ते में कुछ त्रुटियां हैं। यह वास्तव में मेरे जैसा था, वास्तव में।समस्या यह है कि मुझे तीन बनाने की आवश्यकता होगी, क्योंकि मेरे पास 'cout' और' cerr' एक दूसरे से जुड़ा नहीं हो सकता है। (वे डिफ़ॉल्ट रूप से उसी स्थान पर आउटपुट करते हैं) – GManNickG

+0

मेरे लिए संकलित और चलाता है। यदि क्लोग और कोउट एक ही स्थान पर जा रहे हैं तो कंपोज़स्ट्रीम की समस्या नहीं है। –

+0

अच्छा और सरल। हालांकि, यह पहले से ही cout/clog/etc का उपयोग कर कोड की अनुमति नहीं देगा। रचनाकृत धारा में अपना आउटपुट प्राप्त करने के लिए। (यह निश्चित रूप से एक लाभ हो सकता है, लेकिन यह नहीं है कि जिस तरह से मैंने इसे पढ़ा था।) –

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