DevMaster.net Forums
[[ Home | Forums | 3D Engines Database | Wiki | Articles/Tutorials | Game Dev Jobs | IRC Chat Network | Contact Us ]]

Go Back   DevMaster.net Forums > Site Discussions > Code & Snapshot Discussion
User Name
Password
Register FAQ Members List Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Search this Thread Display Modes
Old 09-13-2006, 08:00 AM   #1
dave_
Senior Member
 
Join Date: Sep 2005
Location: Brighton, UK
Posts: 584
Default

When debugging a program I generally use a combination of breakpoints and logging. For convenience I use std::cout. It is an instance of std::ostream that redirects its input to the stdio console. std::ostream just provides operations to perform output operations on stream buffer.

Most of the time using this stream is sufficient, but sometimes, for example when working on a windows application, you don't have access to the console. Rather than change the code that relies on std::cout it is best to redirect its output. Luckily ostream has been designed to have its stream buffer changed. A simple approach is to redirect the output to a file. For example:

Code:
// print to the console std::cout << "out to console" << std::endl; // open a file std::ofstream file("redirect.txt"); // replace the buffer in cout, remember the old one. std::streambuf *old_buffer = std::cout.rdbuf(file.rdbuf()); // log to cout which now redirects to the file std::cout << "to file" << std::endl; // and restore the old buffer std::cout.rdbuf(old_buffer); file.close();

Sometimes redirecting to a file is inconvient. To write to something else we need to create a new stream buffer. As I don't want to create to many dependencies and I'd like the streambuf to be a bit easier to use I've created a simple wrapper (its inline for brevity).

Code:
#include <streambuf> class BufferedStringBuf : public std::streambuf { public: BufferedStringBuf(int bufferSize) { if (bufferSize) { char *ptr = new char[bufferSize]; setp(ptr, ptr + bufferSize); } else setp(0, 0); } virtual ~BufferedStringBuf() { sync(); delete[] pbase(); } virtual void writeString(const std::string &str) = 0; private: int overflow(int c) { sync(); if (c != EOF) { if (pbase() == epptr()) { std::string temp; temp += char(c); writeString(temp); } else sputc(c); } return 0; } int sync() { if (pbase() != pptr()) { int len = int(pptr() - pbase()); std::string temp(pbase(), len); writeString(temp); setp(pbase(), epptr()); } return 0; } };

This class creates buffer for storing streamed output. The class is abstract; virtual void writeString(const std::string &str) = 0; must be overriden. writeString should implement your output operation, this could write the text to an overlay on screen, perhaps some kind of console or whatever you want.

At the end of a line, or when the ostream receives a flush sync is called. This causes the current buffer to be passed as a string to writeString().

When this buffer is filled, the overridden function overflow is called. This causes the stream to flush and attempts to store the character that has overflowed.

The buffer is optional, when the bufferSize is zero it constantly overflows. This is a less than optimal way of operating as it has to output each character individually, but it doesn’t take any extra memory.

This wrapper has been written with simplicity in mind, it might not be the most robust, but it only depends on the STL and its pretty short.

So enough of the details, lets make it do stuff! Here are some examples:

Code:
#include "BufferedStringBuf.h" #include <windows.h> const int LineSize = 256; class DebugBuf : public BufferedStringBuf { public: DebugBuf() : BufferedStringBuf(LineSize) {} virtual void writeString(const std::string &str) { OutputDebugString(str.c_str()); } };


The above class implements a log to visual studio's Output/Debug window.
To use it, take a similar approach to redirecting to a file.

Code:
std::cout << "out to console" << std::endl; // replace the buffer DebugBuf debug_buffer; std::streambuf *old_buffer = std::cout.rdbuf(&debug_buffer); std::cout << "to visual studio debug output window" << std::endl; // restore the old buffer std::cout.rdbuf(old_buffer);

Here is another example buffer:

Code:
class MessageBoxBuf : public BufferedStringBuf { public: MessageBoxBuf() : BufferedStringBuf(LineSize) {} virtual void writeString(const std::string &str) { if ( str.size() > 1 ) // message box doesnt care about single characters MessageBox(NULL, str.c_str(), "Error", MB_OK|MB_ICONERROR); } };

This one displays a message box when flushed.
Its probably not such a good one if you've got lots of text, but it just shows what can be done.

Finally, here is a useful buffer for chaining these buffers together.

Code:
class DupBuf : public BufferedStringBuf { public: DupBuf(std::ostream *stream1, std::ostream *stream2) : BufferedStringBuf(BufferSize), buffer1(stream1->rdbuf()), buffer2(stream2->rdbuf()) { } virtual void writeString(const std::string &str) { const char *ptr = str.c_str(); std::streamsize size = std::streamsize(str.size()); buffer1->sputn(ptr, size); buffer2->sputn(ptr, size); buffer1->pubsync(); buffer2->pubsync(); } private: std::streambuf *buffer1; std::streambuf *buffer2; };

This stores characters till the buffer is filled or flushed then it puts the characters in the other buffers and forces them to sync.
Used like this:

Code:
DebugBuf debug_window_buf; std::ostream debug_stream(&debug_window_buf); DupBuf dup_buf(&std::cout, &debug_stream); old_buff = std::cout.rdbuf(&dup_buf); std::cout << "Print to debug output and stdio" << std::endl;

It will print to both the debug window and to std::cout. Again this is written for simplicity not performance.

Feel free to pick this code apart. I welcome constructive criticism.

Cheers

Dave
dave_ is offline   Reply With Quote
Old 09-13-2006, 12:06 PM   #2
.oisyn
DevMaster Staff
 
.oisyn's Avatar
 
Join Date: Sep 2005
Location: The Netherlands
Posts: 1,442
Default Re: Ostream is easy

You can also do this in applications that don't have a console:
Code:
AllocConsole(); freopen("CONOUT$", "w", stdout); std::cout << "this string outputs to the newly created console window";
Of course, the downside is that it doesn't output to the outputwindow of VC++
___________________________________________
C++ addict
-
Currently working on: the 3D engine for Tomb Raider: Underworld and Deus Ex 3.
.oisyn is offline   Reply With Quote
Old 09-13-2006, 04:49 PM   #3
cypher543
Member
 
Join Date: Mar 2006
Location: Missouri, USA
Posts: 74
Default Re: Ostream is easy

This is cool. Very useful for a Quake-like console. I think I'll be using this in my next game project.
cypher543 is offline   Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Forum Jump


All times are GMT -7. The time now is 12:39 AM.


Powered by vBulletin
Copyright ©2000 - 2009, Jelsoft Enterprises Ltd.