Stefano
Stefano

Reputation: 4031

How to read from a library standard error?

I have a Qt/C++ acpplication which is using a C++ library.

This library has a log mechanism that writes string messages to standard error.

Now, I would like to be able to redirect those messages toward a panel in my Qt tool. I would like to avoid modifying the library because is adopted by many other clients. Any idea how to get at runtime these messages?

Having instead the possibility of changing it what could be a good practise for carrying those messages up to the application?

Upvotes: 2

Views: 1898

Answers (4)

Stefano
Stefano

Reputation: 4031

Here I found a complete implemenation of what i needed...

Thanks everybody for the help! :)

Will loading a DLL dynamically reconcile its stderr to a main application? If so, then how...?

Upvotes: 0

johnsyweb
johnsyweb

Reputation: 141810

If they're using calls to std::cerr, you can redirect this to a std::ostringstream.

#include <iostream>
#include <sstream>

class cerr_redirector
{
public:
    cerr_redirector(std::ostream& os)
        :backup_(std::cerr.rdbuf())
         ,sbuf_(os.rdbuf())
    {
        std::cerr.rdbuf(sbuf_);
    }

    ~cerr_redirector()
    {
        std::cerr.rdbuf(backup_);
    }

private:
    cerr_redirector();
    cerr_redirector(const cerr_redirector& copy);
    cerr_redirector& operator =(const cerr_redirector& assign);

    std::streambuf* backup_;
    std::streambuf* sbuf_;
};

You can capture the output using:

std::ostringstream os;
cerr_redirector red(os);
std::cerr << "This is written to the stream" << std::endl;

std::cout will be unaffected:

std::cout << "This is written to stdout" << std::endl;

So you can then test your capture is working:

std::cout << "and now: " << os.str() << std::endl;

Or just add the contents of os.str() to your Qt Window.

Demonstration at ideone.

Upvotes: 1

James Kanze
James Kanze

Reputation: 153929

That's very poor library design. However...

How does it write to standard error. If it is outputing to std::cerr, then you can change the streambuf used by std::cerr, something like:

std::filebuf logStream;
if ( ~logStream.open( "logfile.txt" ) )
    //  Error handling...
std::streambuf* originalCErrStream = std::cerr.rdbuf();
std::cerr.rdbuf( &logStream );
//  Processing here, with calls to library
std::cerr.rdbuf( originalCErrStream );  //  Using RAII would be better.

Just don't forget to restore the original streambuf; leaving std::cerr pointing to a filebuf which has been destructed is not a good idea.

If they're using FILE*, there's an freopen function in C (and by inclusion in C++) that you can use.

If they're using system level output (write under Unix, WriteFile under Windows), then you're going to have to use some system level code to change the output. (open on the new file, close on fd STDERR_FILENO, and dup2 to set STDERR_FILENO to use the newly opened file under Unix. I'm not sure it's possible under Windows—maybe something with ReOpenFile or some combination of CloseHandle followed by CreateFile.)

EDIT:

I just noticed that you actually want to output to a Qt window. This means that you probably need a string, rather than a file. If the library is using std::cerr, you can use a std::stringbuf, instead of a std::filebuf; you may, in fact, want to create your own streambuf, to pick up calls to sync (which will normally be called after each << on std::cerr). If the library uses one of the other techniques, the only thing I can think of is to periodically read the file, to see if anything has been added. (I would use read() in Unix, ReadFile() in Windows for this, in order to be sure of being able to distinguish a read of zero bytes, due to nothing having been written since the last read, and an error condition. FILE* and iostream functions treat a read of zero bytes as end of file, and will not read further.)

Upvotes: 4

Zang MingJie
Zang MingJie

Reputation: 5275

write to stderr is actually a syscall:

write(2, "blahblah ...");

you can redirect file descriptor number 2 to anything (file, pipe, socket):

close(2);                        // close old stderr
int redirect_target = open(...); // open a file where you want to redirect to
                                 // or use pipe, socket whatever you like
dup2(redirect_target, 2);        // copy the redirect_target fd to fd number 2
close(redirect_target);

in your situation, you will need a pipe.

close(2);
int pipefd[2];
pipe2(pipefd);
dup2(pipefd[1], 2);
close(pipefd[1]);

then, everything write to stderr can be obtained by reading pipe[0]:

read(pipe[0], buffer, ...);

Upvotes: 2

Related Questions