How to read from a library standard error?

断了今生、忘了曾经 提交于 2019-12-11 02:09:44

问题


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?


回答1:


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.)




回答2:


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, ...);



回答3:


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.




回答4:


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...?



来源:https://stackoverflow.com/questions/9189461/how-to-read-from-a-library-standard-error

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!