Override C ++ Streams

In a C ++ program, we have 3 threads: stdin , stdout and stderr . Can I override them in a console application and use them in a form using application?

For example, if in some base class I have cout<< "..." , can I "redirect" to something visual (for example, in Windows Form)?

+7
source share
3 answers

What I would recommend doing is a class that wraps around iostream as follows:

 #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; } }; 

Then, when you want to change where the data goes, you simply change the behavior of the class. This is how you use the class:

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

[edit] As a suggested potato, I will add an example with something other than cout:

 #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; 

As for why you should try this instead of inheriting from something like a streaming stream, mainly because you can easily change where the Logger outputs will be dynamically. For example, you can have three different output streams and use a static member variable to exchange between them at runtime:

 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"; 

This can be easily expanded to create a powerful logging method. You can add filestreams, use std :: cerr etc.

+9
source

Here is the code I use to redirect std::cout to a GUI on Windows:

 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(); } }; 

Then inside main() :

 capturebuf altout; std::cout.set_rdbuf(&altout); 

Of course, you need to process the WM_APP message in your window procedure and pull the lines from the SList. But this handles the cout redirect part.

As jweyrich correctly points out, you need to change streambuf* back before altout goes out of scope. This code will do this:

 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); } }; 

And inside main :

 capturebuf altout; scoped_cout_streambuf_association redirect(altout); 
+2
source

The usual way to put the iostream interface in something else is to use the stringstream / istringstream / ostringstream in <sstream> .

The best way is to change the use of cin , cout , etc. for parameters like istream & and ostream & . If this is really not possible, you can modify cin.rdbuf() to indicate the stream you want to change, but it is extremely hacky and will not work with multithreading.

Copy the text from the form into istringstream , and then pass it to existing code as istream & . When you're done, read the results from stringstream , passed as ostream & , which replaced cout and copied it to the form.

Do not declare arguments like istringstream & or ostringstream & . Use base classes.

If you really want, you can subclass std::basic_stringbuf< char > and override the virtual sync and underflow to bind streams directly to text fields. But this is pretty advanced and probably not worth it.

+1
source

All Articles