Valhalla Legends Forums Archive | C/C++ Programming | Treating debug output as ostream object

AuthorMessageTime
Eibro
I've always wanted to be able to do this, and after looking around at the internals of ostream, I got it. You can print to the debug console using regular ostream techniques (eg. cout)
[code]template < typename T, class CharTraits = std::char_traits< T > >
class basic_debugstreambuf : private std::basic_streambuf< T, CharTraits >
{
public:
   typedef CharTraits::off_type off_type;
   typedef CharTraits::char_type char_type;
   typedef CharTraits::int_type int_type;
   typedef std::vector< T > BufferType;
   
public:
   basic_debugstreambuf() {
      m_outputBuffer.reserve( 32 );
   }

protected:
   virtual int_type overflow( int_type c = CharTraits::eof() )
   {   
      if ( c == CharTraits::eof() )
         return CharTraits::not_eof( c );

      m_outputBuffer.push_back( c );
      if ( c == TEXT( '\n' ) )
      {
         // FIXME
         m_outputBuffer.push_back( TEXT( '\0' ) );
         OutputDebugString( reinterpret_cast< const T* >( &m_outputBuffer[0] ) );
         m_outputBuffer.clear();
         m_outputBuffer.reserve( 32 );
      }
      return c;
   }
   
protected:
   BufferType m_outputBuffer;
};

template < typename T, class CharTraits = std::char_traits< T > >
class basic_debugostream : public std::basic_ostream< T, CharTraits >
{
public:
   typedef basic_debugstreambuf< T, CharTraits > DebugStreamBuf;
   typedef std::basic_streambuf< T, CharTraits > StreamBuf;
   typedef std::basic_ostream< T, CharTraits > OStream;

public:
   basic_debugostream() : OStream( ( StreamBuf* )&m_streamBuffer )
   { clear(); }

protected:
   DebugStreamBuf m_streamBuffer;
};

typedef basic_debugostream< char > debugostream;
typedef basic_debugostream< wchar_t > wdebugostream;
typedef basic_debugostream< TCHAR > tdebugostream;
[/code]Then declare a basic_debugostream object, and start printing to it.[code]tdebugostream dout;
// ...

dout << "Hello world, i'm printing to the debug console!\n";
dout << "Just overload operator << for std::ostream and I will support you!\n";
dout.fill( TEXT('0') );
dout.width( 6 );
dout << "55 + 100 = " << 55 + 100 << std::endl;
[/code]The only downside right now is that nothing gets printed until a \n gets fed into the stream. I'm currently looking for a better way to do this. I hope someone finds this useful, or at least entertaining.
March 7, 2004, 6:20 PM
Kp
From experience, OutputDebugString does not have an implied newline if you don't include one in the message you feed it. So, if you wanted to do so, you could add a bool buffer_output which, when set to false, causes it to invoke OutputDebugString after every invocation of operator<<. Or you could add a new flush method to cause it to call ODS when flush is called.
March 7, 2004, 6:46 PM
Eibro
[quote author=Kp link=board=30;threadid=5649;start=0#msg48104 date=1078685216]
From experience, OutputDebugString does not have an implied newline if you don't include one in the message you feed it. So, if you wanted to do so, you could add a bool buffer_output which, when set to false, causes it to invoke OutputDebugString after every invocation of operator<<. Or you could add a new flush method to cause it to call ODS when flush is called.
[/quote]Yeah, OutputDebugString doesn't have an implied newline. I don't believe basic_streambuf::overflow is called with each insertion, though. (eg. operator <<) Really, what I should be doing is feeding each individual character into OutputDebugString when overflow is called, but that's rather inefficient. I suppose I could buffer x amount of characters, then feed them in and have the user call flush() to print immediatly.
March 7, 2004, 7:35 PM
Skywing
[quote author=Eibro link=board=30;threadid=5649;start=0#msg48123 date=1078688138]
[quote author=Kp link=board=30;threadid=5649;start=0#msg48104 date=1078685216]
From experience, OutputDebugString does not have an implied newline if you don't include one in the message you feed it. So, if you wanted to do so, you could add a bool buffer_output which, when set to false, causes it to invoke OutputDebugString after every invocation of operator<<. Or you could add a new flush method to cause it to call ODS when flush is called.
[/quote]Yeah, OutputDebugString doesn't have an implied newline. I don't believe basic_streambuf::overflow is called with each insertion, though. (eg. operator <<) Really, what I should be doing is feeding each individual character into OutputDebugString when overflow is called, but that's rather inefficient. I suppose I could buffer x amount of characters, then feed them in and have the user call flush() to print immediatly.
[/quote]
I would recommend avoiding that. A single call to OutputDebugString is atomic, but if you do it character by character you risk mixing your output with somebody else's (think kd or DbgView). Not to mention that since an exception is internally raised each time you call OutputDebugString, that will probabaly be a serious performance hit.
March 7, 2004, 8:26 PM
Eibro
[quote]I would recommend avoiding that. A single call to OutputDebugString is atomic, but if you do it character by character you risk mixing your output with somebody else's (think kd or DbgView). Not to mention that since an exception is internally raised each time you call OutputDebugString, that will probabaly be a serious performance hit.[/quote] Hmm I did not know that, good point. So really, the best way I can think to do this is call OutputDebugString() when a newline is found, or when the user manually calls std::basic_ostream::flush(). I found that flush() isn't declared as virtual, but it internally calls virtual std::basic_streambuf::sync(), (which is virtual) so overloading that method works nicely.

With changes:
[code]template < typename T, class CharTraits = std::char_traits< T > >
class basic_debugstreambuf : private std::basic_streambuf< T, CharTraits >
{
public:
   typedef CharTraits::off_type off_type;
   typedef CharTraits::char_type char_type;
   typedef CharTraits::int_type int_type;
   typedef std::vector< T > BufferType;
   
public:
   basic_debugstreambuf() {
      _Init( NULL, NULL, NULL, &pBegin, &pCurrent, &pLength );
      m_outputBuffer.reserve( 32 );
   }

protected:
   virtual int_type overflow( int_type c = CharTraits::eof() )
   {   
      if ( c == CharTraits::eof() )
         return CharTraits::not_eof( c );

      m_outputBuffer.push_back( c );
      if ( c == TEXT( '\n' ) )
         sync();

      return c;
   }

   int sync()
   {
      m_outputBuffer.push_back( TEXT( '\0' ) );
      OutputDebugString( reinterpret_cast< const T* >( &m_outputBuffer[0] ) );
      m_outputBuffer.clear();
      m_outputBuffer.reserve( 32 );
      return 0;
   }
   
protected:
   // put begin, put current and put length
   char_type* pBegin;
   * pCurrent;
   int_type pLength;
   BufferType m_outputBuffer;
};

template < typename T, class CharTraits = std::char_traits< T > >
class basic_debugostream : public std::basic_ostream< T, CharTraits >
{
public:
   typedef basic_debugstreambuf< T, CharTraits > DebugStreamBuf;
   typedef std::basic_streambuf< T, CharTraits > StreamBuf;
   typedef std::basic_ostream< T, CharTraits > OStream;

public:
   basic_debugostream() : OStream( ( StreamBuf* )&m_streamBuffer )
   { clear(); }

protected:
   DebugStreamBuf m_streamBuffer;
};

typedef basic_debugostream< char > debugostream;
typedef basic_debugostream< wchar_t > wdebugostream;
typedef basic_debugostream< TCHAR > tdebugostream;[/code]
March 7, 2004, 9:37 PM
Define
Remember that each compiler is like a different dialect for a language. State what compiler you use so that others whom do not know this don't waste there time trying to figure out why they get errors.

;D
March 30, 2004, 5:29 PM
K
[quote author=Define aka MosDef link=board=30;threadid=5649;start=0#msg52656 date=1080667787]
Remember that each compiler is like a different dialect for a language. State what compiler you use so that others whom do not know this don't waste there time trying to figure out why they get errors.

;D
[/quote]

If your compiler can't compile completely ISO compliant code, you need to get a new compiler.
March 30, 2004, 7:17 PM
Kp
[quote author=Define aka MosDef link=board=30;threadid=5649;start=0#msg52656 date=1080667787]Remember that each compiler is like a different dialect for a language. State what compiler you use so that others whom do not know this don't waste there time trying to figure out why they get errors.[/quote]

If you're going to complain that freely supplied code doesn't work in your compiler, you should indicate precisely what version and what compiler you are using. I haven't checked the code for ISO compliance, but if K says it is, I'll believe it. Barring really nasty compiler specific hacks, most things that work in any given modern compiler will work in others (or can be made to work very easily -- for instance, supplying dummies for gcc builtins which have no VC equivalent).

Also, what changes did you need to make to get this to compile? I'm assuming you did fix it instead of just complain at us...

[Kp edit: fixed minor spelling typo.]
March 30, 2004, 8:20 PM
Define
Complaine? Lol. I was meanly saying it to inform others whom do not know this about it. I was not complaining as you say. Lol.
March 31, 2004, 1:07 AM
Zakath
If you supply code that is ISO compliant, there's no reason to say anything about it. A disclaimer about compilers is only necessary when the code only works in a specific environment.
March 31, 2004, 1:18 AM
Maddox
Nifty. [img]http://www.coruscantcity.net/board/graemlins/biggthumpup.gif[/img]
March 31, 2004, 1:38 AM
Define
I am not stating I have a problemn compiling the project he stated. I for one don't have any use for it. I was meanly stating that to help others whom do not know that.
March 31, 2004, 2:45 AM
Adron
[quote author=Define aka MosDef link=board=30;threadid=5649;start=0#msg52785 date=1080701120]
I am not stating I have a problemn compiling the project he stated. I for one don't have any use for it. I was meanly stating that to help others whom do not know that.
[/quote]

Yes, you're mean. Stop being so mean and start being more constructive. Complain at the compiler not being listed when there's a problem with it not being listed.

Unless you have proof that there is some windows compiler that is frequently used today and which doesn't support the code, why would anyone be helped by listing what compiler was used?
March 31, 2004, 9:39 AM
Define
Well, for example I will use the GNU and AT&T:

*Each comes with its own set of header files to be included. In both
versions, if it can't find a C++ header file, it will fall back on the
corresponding C header file (which may not work as expected).
* The syntax of the "new" statement differs slightly between the two versions.

Two small, but imporant differences.
March 31, 2004, 4:17 PM
Define
I was not complaining about anything. If I wanted to complaine to someone about the code or the compiler, the complaint would not be here. Thank you. *MosDef*
March 31, 2004, 4:24 PM
Adron
[quote author=Define aka MosDef link=board=30;threadid=5649;start=0#msg52837 date=1080749820]
Well, for example I will use the GNU and AT&T:

*Each comes with its own set of header files to be included. In both
versions, if it can't find a C++ header file, it will fall back on the
corresponding C header file (which may not work as expected).
* The syntax of the "new" statement differs slightly between the two versions.

Two small, but imporant differences.

[/quote]

Elaborate on those please? Like what exactly is the syntax difference for new?
March 31, 2004, 5:15 PM

Search