3

I'm writing a class to display a custom console from inside the application. I'm also using glog to log messages to a file and at the same time to stderr. How can I have my console class listen to stderr?

I thought of making a custom fstream and the doing something like:

CustomStream cs;
auto original_buf = std::cerr.rdbuf(cs.rdbuf());

and having calls to the stream operator << sent to the console class.

Or subclassing directly from std::filebuf and calling:

CustomFilebuf fb;
auto original_buf = std::cerr.rdbuf(&fb);

Is this the right way to do it? I searched for some sample code but couldn't find very much.

Edit1: I tried using stream tee, but glog logs with stderr and not std::cerr, so I wasn't able to grab any data.

キキジキ
  • 1,443
  • 1
  • 25
  • 44
  • What do you mean with *listen* to stderr? And what is glog? – Olaf Dietsche Feb 23 '13 at 22:43
  • I want to capture the stderr output and send it to my console class so I can render it inside the application. I said listen because I don't want to poll repeatedly for new data, but just send a notification when some new message gets flushed. – キキジキ Feb 23 '13 at 22:48
  • 2
    Then you don't need your own stream. A streambuf, which diverts the contents of stderr should be sufficient. See [Tee streams](http://wordaligned.org/articles/cpp-streambufs#toctee-streams) for an example. – Olaf Dietsche Feb 23 '13 at 22:56
  • Olaf, thank you very much. I'm looking into it now and looks like exactly what I needed. – キキジキ Feb 23 '13 at 23:10

1 Answers1

1

I am uncertain if this is relevant to your question, but...

ISO C99 says in 7.19.5.3, paragraph 6:

When a file is opened with update mode ('+' as the second or third character in the above list of mode argument values), both input and output may be performed on the associated stream. However, output shall not be directly followed by input without an intervening call to the fflush function [...], and input shall not be directly followed by output without an intervening call to a file positioning function, unless the input operation encounters end-of-file.

Also some say reading from stderr is "undefined behavior"...

Though you can read from stderr, as long as you flush before reading:

fwrite("x", 1, 1, stderr);
fflush(stderr);
fgetc(stderr);

Also take a look at How do I read stdout/stderr output of a child process correctly?


For anyone who would like to redirect stdout to a console Window in a Win32 app there is AllocConsole.

I even created a simple (trivial) function to redirect stdout to the console Window:

#include <fstream>
#include <io.h>
#include <fcntl.h>

#define cMaxConsoleLines 500

void ReadyConsole() {

    short int hConHandle;
    long lStdHandle;

    CONSOLE_SCREEN_BUFFER_INFO coninfo;

    FILE *fp;

    // Allocate a console for the program
    AllocConsole();

    // set the screen buffer to be big enough to let us scroll text
    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);

    coninfo.dwSize.Y = cMaxConsoleLines; // The max number of lines for the console!

    SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);


    // Redirect STDOUT to the console
    lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

    fp = _fdopen(hConHandle, "w");  // Writing to the console

    *stdout = *fp;

    setvbuf(stdout, NULL, _IONBF, 0);
    // -------------------------------


    // Redirect STDIN to the console
    lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

    fp = _fdopen(hConHandle, "r");  // Reading from the console

    *stdin = *fp;

    setvbuf(stdin, NULL, _IONBF, 0);
    // ------------------------------


    // Redirect STDERR to the console
    lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
    hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);

    fp = _fdopen(hConHandle, "w");  // STDERR writing to the console!

    *stderr = *fp;

    setvbuf(stderr, NULL, _IONBF, 0);
    // ------------------------------


    // Point the console to STDIO
    std::ios::sync_with_stdio();

}

If you would like the console to be for Debug only, make sure to include <crtdbg.h>, which defines whether the app is in debug mode (for VC++), then, for example you could add:

#ifdef _DEBUG
// The file with the ReadyConsole function
#include <DebugStuff.h>
#endif

and to use it

#ifdef _DEBUG
ReadyConsole(); // Ready the console for debugging
#endif

#ifdef _DEBUG
fprintf(stdout, "Position, Line 1, DEBUG-INFO-HERE");
cout << "COUT is working!"; // NOTE, for cout, you will need <iostream>
#endif

Here is an additional little function (it logs the message to both stderr and stderr.log file)

void StdErr(char* Error) {

    fprintf(stderr, Error);
    FILE* FP = fopen("stderr.log", "a");
    fputs(Error, FP);
    fclose(FP);

}
Community
  • 1
  • 1
Mitch
  • 44
  • 6
  • I'm already using it, but now I was trying switch my console class. The standard console outputs stderr and stdout of his own, what I'm asking is how to implement the same but for my class. – キキジキ Feb 24 '13 at 01:08
  • Oh, Very well. I presume that you solved this problem, with the link suggested by Olaf then. – Mitch Feb 24 '13 at 01:37
  • Though I am not sure what you are wanting exactly (my fault probably)... My example does capture stderr as the output occurs and logs it into a console (stdin style), as well as cout, and stdout... If my example is unhelpful, please explain in just a little more details (maybe pictures too), what the issue is (I apologize). – Mitch Feb 24 '13 at 04:49
  • Wait -- Are you trying to send the stderr to both the console (built into the app) and a log file? – Mitch Feb 24 '13 at 04:57
  • when I log something on glog, it appends it to a log file and sends it also to stderr via fprintf, fwrite and fflush. What I want to do is instance my console class and have it print what the logger sends to stderr. I found this blog where explains how to achieve something similar, but that code makes use of libraries not availables on windows, and I hoped the solution would be simpler... http://zpasternack.blogspot.jp/2011/07/adventures-in-redirection-part-deux.html – キキジキ Feb 24 '13 at 05:46
  • +1, That is a really good question... I wish that I could answer it, but honestly, If I am understanding you correctly, I am uncertain how feasible it is... The only suggestion I have, is to use freopen, and a timer to read the data once every few seconds, and refresh... – Mitch Feb 24 '13 at 06:11
  • Yes I think you're right. Also, editing glog to accept an additional stream shouldn't be so difficult. Anyway, I'm curious about how the standard console does it. – キキジキ Feb 24 '13 at 23:41
  • I am curious about how the standard console does it too... I will be using some advanced debugging equipment later to find out! – Mitch Feb 25 '13 at 00:04