Redirecting cerr and clog to OutputDebugString()

std::cout, std::cerr and std::clog are streams just like any other. And just like any other, they work in conjuction with a stringbuf object.

The Win32 OutputDebugString function allows you to send a message to your debugger. In Visual C++ 2010, it is displayed in the “Output” window.

The code below will modify cerr and clog to redirect the data to the debugger.

#include <ostream>
#include <Windows.h>

/// \brief This class is a derivate of basic_stringbuf which will output all the written data using the OutputDebugString function
template<typename TChar, typename TTraits = std::char_traits<TChar>>
class OutputDebugStringBuf : public std::basic_stringbuf<TChar,TTraits> {
public:
	explicit OutputDebugStringBuf() : _buffer(256) {
		setg(nullptr, nullptr, nullptr);
		setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());
	}

	~OutputDebugStringBuf() {
	}

	static_assert(std::is_same<TChar,char>::value || std::is_same<TChar,wchar_t>::value, "OutputDebugStringBuf only supports char and wchar_t types");

	int sync() try {
		MessageOutputer<TChar,TTraits>()(pbase(), pptr());
		setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());
		return 0;
	} catch(...) {
		return -1;
	}

	int_type overflow(int_type c = TTraits::eof()) {
		auto syncRet = sync();
		if (c != TTraits::eof()) {
			_buffer[0] = c;
			setp(_buffer.data(), _buffer.data() + 1, _buffer.data() + _buffer.size());
		}
		return syncRet == -1 ? TTraits::eof() : 0;
	}


private:
	std::vector<TChar>		_buffer;

	template<typename TChar, typename TTraits>
	struct MessageOutputer;

	template<>
	struct MessageOutputer<char,std::char_traits<char>> {
		template<typename TIterator>
		void operator()(TIterator begin, TIterator end) const {
			std::string s(begin, end);
			OutputDebugStringA(s.c_str());
		}
	};

	template<>
	struct MessageOutputer<wchar_t,std::char_traits<wchar_t>> {
		template<typename TIterator>
		void operator()(TIterator begin, TIterator end) const {
			std::wstring s(begin, end);
			OutputDebugStringW(s.c_str());
		}
	};

};

Usage :

int main() {
	#ifndef NDEBUG
		#ifdef _WIN32
			static Utilities::OutputDebugStringBuf<char> charDebugOutput;
			std::cerr.rdbuf(&charDebugOutput);
			std::clog.rdbuf(&charDebugOutput);
		
			static Utilities::OutputDebugStringBuf<wchar_t> wcharDebugOutput;
			std::wcerr.rdbuf(&wcharDebugOutput);
			std::wclog.rdbuf(&wcharDebugOutput);
		#endif
	#endif

	...

	std::cerr << "Error: something bad happened" << std::endl;		// will be displayed in the debugger

	...
}

54 thoughts on “Redirecting cerr and clog to OutputDebugString()

  1. Thanks for the code!

    I needed to do some things to get it to compile cleanly:
    between lines 1 & 2: added “#include ”
    beginning of line 8: removed “explicit”
    (explicit is only for constructors with 1 argument)
    beginning of line 17: added “protected”
    beginning of lines 18 & 26: added “virtual”
    beginning of line 27: replaced “auto” with “int”
    (auto is default storage type)
    line 29: added “(TChar)” between “=” and “c”
    (eliminate possible truncation warning)

    With those changes, it compiles with no warnings or errors at warning level W4.

    Nice work!

  2. Indeed, nice work!

    The code uses the latest greatest in C++0x and STL.

    I had to make small changes to make it compile under Visual Studio 2008.

    Code is below.

    -88-

    #include
    #include
    #include
    #include
    #include

    #define nullptr NULL

    /// \brief This class is a derivate of basic_stringbuf which will output all the written data using the OutputDebugString function
    template<typename TChar, typename TTraits = std::char_traits>
    class OutputDebugStringBuf : public std::basic_stringbuf {
    public:
    OutputDebugStringBuf() : _buffer(1024) {
    setg(nullptr, nullptr, nullptr);

    //Visual Studio 2010 has data() member in vector class
    // setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());

    // Visual Studio 2008 code
    setp(&_buffer[0], &_buffer[0], &_buffer[0] + _buffer.size());
    }

    ~OutputDebugStringBuf() {
    }

    //static_assert(std::is_same::value || std::is_same::value, “OutputDebugStringBuf only supports char and wchar_t types”);

    int sync()
    {
    try {
    MessageOutputer()(pbase(), pptr());
    //Visual Studio 2010 has data() member in vector class
    // setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());

    // Visual Studio 2008 code
    setp(&_buffer[0], &_buffer[0], &_buffer[0] + _buffer.size());
    return 0;
    } catch(…) {
    return -1;
    }
    }

    std::char_traits::int_type overflow(std::char_traits::int_type c = TTraits::eof()) {
    int syncRet = sync();
    if (c != TTraits::eof()) {
    _buffer[0] = c;
    // Visual Studio 2010 code
    // setp(_buffer.data(), _buffer.data() + 1, _buffer.data() + _buffer.size());

    // Visual Studio 2008 code
    setp(&_buffer[0], &_buffer[0] + 1, &_buffer[0] + _buffer.size());
    }
    return syncRet == -1 ? TTraits::eof() : 0;
    }

    private:
    std::vector _buffer;

    template
    struct MessageOutputer;

    template
    struct MessageOutputer<char,std::char_traits> {
    template
    void operator()(TIterator begin, TIterator end) const {
    std::string s(begin, end);
    OutputDebugStringA(s.c_str());
    }
    };

    template
    struct MessageOutputer<wchar_t,std::char_traits> {
    template
    void operator()(TIterator begin, TIterator end) const {
    std::wstring s(begin, end);
    OutputDebugStringW(s.c_str());
    }
    };
    };

  3. One more try with proper quotes…

    #include <string>
    #include <sstream>
    #include <iostream>
    #include <ostream>
    #include <vector>

    #define nullptr NULL

    /// \brief This class is a derivate of basic_stringbuf which will output all the written data using the OutputDebugString function
    template<typename TChar, typename TTraits = std::char_traits<TChar>>
    class OutputDebugStringBuf : public std::basic_stringbuf<TChar,TTraits> {
    public:
    OutputDebugStringBuf() : _buffer(1024) {
    setg(nullptr, nullptr, nullptr);

    //Visual Studio 2010 has data() member in vector class
    // setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());

    // Visual Studio 2008 code
    setp(&_buffer[0], &_buffer[0], &_buffer[0] + _buffer.size());
    }

    ~OutputDebugStringBuf() {
    }

    //static_assert(std::is_same<TChar,char>::value || std::is_same<TChar,wchar_t>::value, “OutputDebugStringBuf only supports char and wchar_t types”);

    int sync()
    {
    try {
    MessageOutputer<TChar,TTraits>()(pbase(), pptr());
    //Visual Studio 2010 has data() member in vector class
    // setp(_buffer.data(), _buffer.data(), _buffer.data() + _buffer.size());

    // Visual Studio 2008 code
    setp(&_buffer[0], &_buffer[0], &_buffer[0] + _buffer.size());
    return 0;
    } catch(…) {
    return -1;
    }
    }

    std::char_traits<char>::int_type overflow(std::char_traits<char>::int_type c = TTraits::eof()) {
    int syncRet = sync();
    if (c != TTraits::eof()) {
    _buffer[0] = c;
    // Visual Studio 2010 code
    // setp(_buffer.data(), _buffer.data() + 1, _buffer.data() + _buffer.size());

    // Visual Studio 2008 code
    setp(&_buffer[0], &_buffer[0] + 1, &_buffer[0] + _buffer.size());
    }
    return syncRet == -1 ? TTraits::eof() : 0;
    }

    private:
    std::vector<TChar> _buffer;

    template<typename TChar, typename TTraits>
    struct MessageOutputer;

    template<>
    struct MessageOutputer<char,std::char_traits<char>> {
    template<typename TIterator>
    void operator()(TIterator begin, TIterator end) const {
    std::string s(begin, end);
    OutputDebugStringA(s.c_str());
    }
    };

    template<>
    struct MessageOutputer<wchar_t,std::char_traits<wchar_t>> {
    template<typename TIterator>
    void operator()(TIterator begin, TIterator end) const {
    std::wstring s(begin, end);
    OutputDebugStringW(s.c_str());
    }
    };
    };

  4. I have a question.

    In VS2012 I got a warning message of

    1>c:\users\kims11\codes\projects\opengl\sandbox\my_opengl_tests\test05\windows_error_output.h(94): warning : exception specification for virtual function “OutputDebugStringBuf::~OutputDebugStringBuf” is incompatible with that of overridden function “std::basic_streambuf::~basic_streambuf [with _Elem=TChar, _Traits=TTraits]”

    What does this mean? Is it possible to correct this?

    BTW, this is a beautiful piece of codes.
    Thanks a lot.

  5. Pingback: Louis
  6. Pingback: stuart
  7. Pingback: Herman
  8. Pingback: Darrell
  9. Pingback: Henry
  10. Pingback: Keith
  11. Pingback: arturo
  12. Pingback: jon
  13. Pingback: Charles
  14. Pingback: ted
  15. Pingback: Jackie
  16. Pingback: Milton
  17. Pingback: gene
  18. Pingback: Arturo
  19. Pingback: Edgar
  20. Pingback: Mario
  21. Pingback: troy
  22. Pingback: Roy
  23. Pingback: Mitchell
  24. Pingback: shawn
  25. Pingback: Brent
  26. Pingback: arthur
  27. Pingback: steve
  28. Pingback: Andy
  29. Pingback: Jackie
  30. Pingback: Harvey
  31. Pingback: Francisco
  32. Pingback: oscar
  33. Pingback: trevor
  34. Pingback: Neil
  35. Pingback: Gary
  36. Pingback: Nathan
  37. Pingback: Morris
  38. Pingback: tracy
  39. Pingback: Henry
  40. Pingback: Cecil
  41. Pingback: russell
  42. Pingback: ben
  43. Pingback: Troy
  44. Pingback: Kent
  45. Pingback: Randall
  46. Pingback: arturo
  47. Pingback: rex
  48. Pingback: brent
  49. Pingback: Ernest
  50. Pingback: Danny

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>