C++-Streams überschreiben

C++-Streams überschreiben

Ich würde empfehlen, eine Klasse zu haben, die einen Iostream wie folgt umschließt:

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

Wann immer Sie dann ändern möchten, wohin die Daten gehen, ändern Sie einfach das Klassenverhalten. So verwenden Sie die Klasse:

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

[Bearbeiten]Wie von Kartoffelklatsche vorgeschlagen, füge ich ein Beispiel mit etwas anderem als cout hinzu:

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

Warum sollten Sie dies versuchen, anstatt von etwas wie einem String-Stream zu erben, hauptsächlich weil Sie leicht ändern können, wo der Logger dynamisch ausgibt. Beispielsweise könnten Sie drei verschiedene Ausgabeströme haben und eine statische Mitgliedsvariable verwenden, um zur Laufzeit zwischen ihnen zu wechseln:

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

Dies kann leicht erweitert werden, um eine leistungsstarke Methode zum Umgang mit der Protokollierung zu schaffen. Sie könnten Dateiströme hinzufügen, std::cerr verwenden usw.


Hier ist der Code, den ich verwende, um std::cout umzuleiten zu einer GUI unter 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();
    }
};

Dann innerhalb von main() :

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

Natürlich müssen Sie dann mit WM_APP umgehen Nachricht in Ihrer Fensterprozedur und ziehen Sie die Zeichenfolgen aus der SList. Aber das behandelt die cout Weiterleitungsteil.

Wie jweyrich richtig anmerkt, müssen Sie den streambuf* ändern zurück vor altout geht aus dem Rahmen. Dieser Code wird dies tun:

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

Und in main :

capturebuf altout;
scoped_cout_streambuf_association redirect(altout);