• Welcome to Valhalla Legends Archive.
 

Treating debug output as ostream object

Started by Eibro, March 07, 2004, 12:20 PM

Previous topic - Next topic

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)
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.
Eibro of Yeti Lovers.

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.
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

Eibro

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.
Eibro of Yeti Lovers.

Skywing

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.

Eibro

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;
Eibro of Yeti Lovers.

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

K

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.

Kp

#7
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.]
[19:20:23] (BotNet) <[vL]Kp> Any idiot can make a bot with CSB, and many do!

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.

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.
Quote from: iago on February 02, 2005, 03:07 PM
Yes, you can't have everybody...contributing to the main source repository.  That would be stupid and create chaos.

Opensource projects...would be dumb.

Maddox

asdf.

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.

Adron

#12
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?

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.

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*