This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

| Debug Stream Submitted by

When I saw John Raptis' "C++ Console" posting, I decided to post this code that I've had kicking around. It subclasses std::ostream into 'dbgstream', implementing iostream style output to the windows OutputDebugString API. Included is a sample main() showing usage, but usage is pretty simple: just '#include' the header file and declare a dbgstream object. You can then use it just like any other iostream. This code can be used as a jumping off point for all kinds of std::ostream redirection -- changing it to append output to a log window would be trivial. I also have a version of this code which works with UNIX syslog, anyone interested in that code should contact me. The code compiles with VC++5, using the included STL. It should compile with STLport as well (the UNIX version of the code does ;-).

Currently browsing [dbgstream.zip] (2,412 bytes) - [dbgstream.h] - (3,390 bytes)

    /*
    ** File: dbgstream.h -- header file for debugger/console stream
    */
    #ifndef _H_DBGSTREAM_
    #define _H_DBGSTREAM_

    #include <ostream>
    #include <string>

    /// The following text and the ideas from it are from Dietmar Kuehl.
    // <!! Copyright (C) 1997 Dietmar Kuehl >
    // <!! Universitaet Konstanz, Germany, Lehrstuhl fuer praktische >
    // <!! Informatik I >
    // I'm not sure if the assertion is correct with "modern" Std C++ libraries,
    // but It's a simple thing to keep in the code ;-)

    // -----------------------------------------------------------------------------
    // To get the order of construction and destruction straight I use a
    // technique presented to me by Ron Klatchko (ron@crl.com): It would
    // be cleaner to construct the 'streambuf' before the 'ostream is
    // constructed (and destruction correspondingly in the reverse order)
    // but bases are constructed prior to members. Thus, Ron came up with
    // the idea to use a private base class, becoming the left most base
    // class, containing the corresponding member: This yields the desired
    // order of construction. I called this technique "Ron's Member".

    /// This class is a utility class to avoid problems with stupid compilers.
    /// see header file for details.
    template <class T>
    struct PBase // Ron's member
    {
    	T sbuf;
    	PBase() { }
    };

    // This class implements a stream buffer for outputting to the win32
    // debugging console.

    /*!
        The dbgbuf class is used by the dbgstream class to manage output
        to OutpuDebugString() and to a 'tee' stream.  The 'tee' stream can
    	be any std::ostream object.  It's important to note that a pointer
    	to the tee ostream is kept inside this object, so it should not
    	be deleted until the dbgbuf object is no longer in use. (the dbgbuf
        object does not try to copy the ostream object).

        Messages are sent to the debugger and the tee stream when a newline
    	character is seen.  Any leftover characters in the buffer will be output
        when the object is destroyed.

        This class is not normally used by itself, it is usually attached to
        a dbgstream object.
    */
    class dbgbuf : public std::streambuf {
    private:
    	std::string msg;	///< buffer for current log message
    	std::ostream *tee;	///< if != 0, send output here, also.

        /// internal function used to direct output to syslog and (optionally)
        /// to the tee stream.
        void flushMsg();

    public:
        /// constructor.
        dbgbuf() : tee(0) { };

        /// Destructor.  No magic here.
        ~dbgbuf();

        /// Set a stream to be used as an additional output.  Messages are
        /// directed to this stream as well as to syslog().
    	std::ostream *setTee(std::ostream *_tee);

    protected:

        /// streambuf implementation.
        virtual int overflow(int ch);

    private:
        dbgbuf(dbgbuf const &);                // disallow copy construction
        void operator= (dbgbuf const &);          // disallow copy assignment
    };

    /// dbgstream objects provide ostream functionality for dbgbuf
    /// objects.
    class dbgstream :
    	private PBase<dbgbuf>,
    	public std::basic_ostream<char,std::char_traits<char> >
    {
    public:
        /// Constructor.
        explicit dbgstream() :
    		std::basic_ostream<char,std::char_traits<char> > (&sbuf)
    		{ }

        /// set tee object.  Reflects to underlying stream buffer object.
        std::ostream *setTee(std::ostream *_tee) {
    		return sbuf.setTee(_tee);
        }
    };

    extern dbgstream dbg;

    #endif /* _H_DBGSTREAM_ */

Currently browsing [dbgstream.zip] (2,412 bytes) - [dbgstream.cpp] - (1,183 bytes)

    /*
    ** file: dbgstream.cpp -- debugging stream
    */
    #include "dbgstream.h"

    #define WIN32_LEAN_AND_MEAN
    #include <windows.h>
    #include <fstream>

    #ifdef DBG_TIMESTAMP
    extern "C" {
    #include <time.h>
    };
    #endif /* DBG_TIMESTAMP */

    using namespace std;

    dbgbuf::~dbgbuf()
    {
        flushMsg();
    }

    void dbgbuf::flushMsg()
    {
    	if (msg.length() > 0) {
    #if DBG_TIMESTAMP
    		char tbuf[64];
    		time_t t = time(0);
    		struct tm tm;
    		tm = *localtime(&t);
    		strftime(tbuf,sizeof(tbuf),"%b %d %R:%S",&tm);

    		OutputDebugStringA(tbuf);
    		OutputDebugStringA(":");
    		if (tee) {
    			(*tee) << tbuf << ": ";
    		}

    #endif /* DBG_TIMESTAMP */
    		OutputDebugStringA(msg.c_str());
    		OutputDebugStringA("\n");
    		if (tee) {
    			(*tee) << msg << endl << flush;
    		}

    		msg.erase();	// erase message buffer
    	}
    }

    std::ostream *dbgbuf::setTee(std::ostream *_tee)
    {
        std::ostream *otee = tee;
        tee = _tee; return otee;
    }

    int dbgbuf::overflow(int c)
    {
        if (c == '\n') {
    		flushMsg();
        } else {
    		msg += c;
        }
        return c == -1 ? -1 : ' ';
    }

    #ifdef TEST_MAIN
    INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
    {
    	dbgstream dbg;

    	dbg << "Hello, World." << endl;

    	return 0;
    }
    #endif /* TEST_MAIN */

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.