Often when working in C++ I find myself in a situation where I want to be able to quickly drop some formatted output in a specific way. My usual standby is to use a lot of intermediate stringstream objects, like this:
void OutputMessage(const std::wstring& message);
int foo = 4;
std::wstring bar = "Stuff!";
// Do some work on foo and bar
msg << "State of foo is " << foo << " and bar is " << bar;
This allows any given kind of output function we want: a message box, output to a custom console in a game, logging to a file, sending over a network debug connection, and so on.
However, I find this unwieldy and annoying, especially when I have to dot my code with lots of inline stringstreams. Using this method is fine for quick and dirty debug logging, but it can quickly sap performance since it wastes a lot of time allocating memory.
To address this issue, I whipped up the following simple class:
std::wstring outstr = Stream.str();
OutputStream& operator << (const T& val)
Stream << val;
OutputStream& operator << (std::basic_ostream<wchar_t, std::char_traits<wchar_t> >&
(__cdecl *ptr)(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >&))
Examining the Solution in Detail
Let's walk through the code.
The first bit in the destructor ensures that if the object goes out of scope, any remaining output is dumped. The actual Flush() function is responsible for doing that. Included is a check to avoid dumping the output if there isn't anything to say. Flush() automatically clears out the buffer so that you can easily reuse the same object to do multiple output runs.
The next part is where the magic happens. The first operator << does the heavy lifting; any of your standard output usages will flow through here. Note that all this really does is defer the work of creating the buffer to the standard wostringstream class. This also means that any type which can be used with a standard output stream will work with OutputStream as well.
The second operator << is designed to allow you to use std::endl to flush the stream automatically. This means that piping endl into an OutputStream is effectively a call to Flush(), which in turn calls our custom OutputMessage() function.
Using the Code
Putting it into action is easy:
// As soon as stream goes out of scope, "Foo42baz" will be output
stream << "Foo" << 42 << "baz";
// Or we can do it the more traditional way:
stream2 << "Quux" << 343 << "Zimbabwe" << std::endl;
So now you can have all the advantages of easy stream formatting like std::cout would give you, except you can couple it easily to any kind of output function you want, or even multiple output types at the same time.
In this example, I've used a free function OutputMessage() which presumably actually displays or otherwise handles the messages from the buffer. However, you may find this too simple. A good extension would be to make a class with a pure virtual method OutputMessage(), and turn OutputStream into a template class that can be "attached" to different types of outputters. For example, you could implement a MessageBoxOutputter (which derives from the abstract base class) and then use OutputStream
Another thing to note is that this example does not support the full set of IO formatting functionality provided in the C++ standard library - just endl and other similar IO manipulators. Extending this should be easy: just pass the calls through to the Stream member variable and let it do all the work.
One final note: I have assumed here the use of MBCS/Unicode. If you are still living in the 1990s and refuse to use Unicode, just remove the "w" from wstring and wostringstream, and change wchar_t to char.