Reputation: 4031
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
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
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.
Upvotes: 1
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
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