Valhalla Legends Archive

Programming => General Programming => C/C++ Programming => Topic started by: Eibro on March 07, 2004, 12:20 PM

Title: Treating debug output as ostream object
Post by: Eibro on March 07, 2004, 12:20 PM
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)
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;
Then declare a basic_debugostream object, and start printing to it.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;
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.
Title: Re:Treating debug output as ostream object
Post by: Kp on March 07, 2004, 12:46 PM
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.
Title: Re:Treating debug output as ostream object
Post by: Eibro on March 07, 2004, 01:35 PM
Quote from: Kp on March 07, 2004, 12:46 PM
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.
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.
Title: Re:Treating debug output as ostream object
Post by: Skywing on March 07, 2004, 02:26 PM
Quote from: Eibro on March 07, 2004, 01:35 PM
Quote from: Kp on March 07, 2004, 12:46 PM
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.
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.
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.
Title: Re:Treating debug output as ostream object
Post by: Eibro on March 07, 2004, 03:37 PM
QuoteI 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.
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:
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;
Title: Re:Treating debug output as ostream object
Post by: Define on March 30, 2004, 11:29 AM
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
Title: Re:Treating debug output as ostream object
Post by: K on March 30, 2004, 01:17 PM
Quote from: Define aka MosDef on March 30, 2004, 11:29 AM
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

If your compiler can't compile completely ISO compliant code, you need to get a new compiler.
Title: Re:Treating debug output as ostream object
Post by: Kp on March 30, 2004, 02:20 PM
Quote from: Define aka MosDef on March 30, 2004, 11:29 AMRemember 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.

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.]
Title: Re:Treating debug output as ostream object
Post by: Define on March 30, 2004, 07:07 PM
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.
Title: Re:Treating debug output as ostream object
Post by: Zakath on March 30, 2004, 07:18 PM
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.
Title: Re:Treating debug output as ostream object
Post by: Maddox on March 30, 2004, 07:38 PM
Nifty. (http://www.coruscantcity.net/board/graemlins/biggthumpup.gif)
Title: Re:Treating debug output as ostream object
Post by: Define on March 30, 2004, 08:45 PM
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.
Title: Re:Treating debug output as ostream object
Post by: Adron on March 31, 2004, 03:39 AM
Quote from: Define aka MosDef on March 30, 2004, 08:45 PM
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.

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?
Title: Re:Treating debug output as ostream object
Post by: Define on March 31, 2004, 10:17 AM
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.
Title: Re:Treating debug output as ostream object
Post by: Define on March 31, 2004, 10:24 AM
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*
Title: Re:Treating debug output as ostream object
Post by: Adron on March 31, 2004, 11:15 AM
Quote from: Define aka MosDef on March 31, 2004, 10:17 AM
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.


Elaborate on those please? Like what exactly is the syntax difference for new?