Override c++ streams

What I would recommend doing is having a class which wraps around an iostream like this :

#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, whenever you want to change where the data goes, you just change the class behavior. Here is how you use the class:

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

[edit] As potato swatter suggested, I'll 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 string stream, mainly because you can easily change where the Logger outputs to dynamically. For instance, you could have three different output streams, and use a static member variable to swap between 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 easily be expanded to create a powerful way of dealing with logging. You could add filestreams, use std::cerr, etc.


Here's 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 then need to handle the WM_APP message in your window procedure and pull the strings off the SList. But this handles the cout redirection part.

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

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

Tags:

C++

Overriding