【发布时间】:2012-02-08 07:48:17
【问题描述】:
我有一个使用 C++ 库的 Qt/C++ 应用程序。
这个库有一个日志机制,可以将字符串消息写入标准错误。
现在,我希望能够将这些消息重定向到我的 Qt 工具中的面板。 我想避免修改该库,因为许多其他客户都采用了该库。 知道如何在运行时获取这些消息吗?
如果有可能更改它,将这些消息传送到应用程序可能是一个好的做法?
【问题讨论】:
我有一个使用 C++ 库的 Qt/C++ 应用程序。
这个库有一个日志机制,可以将字符串消息写入标准错误。
现在,我希望能够将这些消息重定向到我的 Qt 工具中的面板。 我想避免修改该库,因为许多其他客户都采用了该库。 知道如何在运行时获取这些消息吗?
如果有可能更改它,将这些消息传送到应用程序可能是一个好的做法?
【问题讨论】:
这是非常糟糕的库设计。不过……
它如何写入标准错误。如果输出到std::cerr,
那么你可以改变std::cerr使用的streambuf,比如:
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.
别忘了恢复原来的streambuf;离开std::cerr
指向已被破坏的filebuf 不是一个好主意。
如果他们使用FILE*,C 中有一个freopen 函数(并且通过
包含在 C++ 中),您可以使用。
如果他们使用系统级输出(write 在 Unix 下,WriteFile
在 Windows 下),那么您将不得不使用一些系统级代码
改变输出。 (open 在新文件上,close 在 fd 上
STDERR_FILENO, 和dup2 设置STDERR_FILENO 使用新的
在 Unix 下打开的文件。我不确定这是否可能
Windows——可能是 ReOpenFile 或一些组合
CloseHandle 后跟CreateFile。)
编辑:
我刚刚注意到您实际上想要输出到 Qt 窗口。这
意味着您可能需要一个字符串,而不是一个文件。如果
库正在使用std::cerr,您可以使用std::stringbuf,而不是
一个std::filebuf;实际上,您可能想要创建自己的流缓冲区,
接听sync 的电话(通常会在每次
<<std::cerr)。如果库使用其他技术之一,
我唯一能想到的就是定期读取文件,看看
如果有任何添加。 (我会在 Unix 中使用 read(),ReadFile()
在 Windows 中,为了确保能够区分
读取零字节,因为自上次以来没有写入任何内容
读取和错误条件。 FILE* 和 iostream 函数处理
读取零字节作为文件结尾,不会进一步读取。)
【讨论】:
streambuf。 (std::cerr 已初始化,因此每个 << 都会导致在 streambuf 上调用 sync。)
写入标准错误实际上是一个系统调用:
write(2, "blahblah ...");
您可以将文件描述符编号 2 重定向到任何东西(文件、管道、套接字):
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);
在您的情况下,您将需要一个管道。
close(2);
int pipefd[2];
pipe2(pipefd);
dup2(pipefd[1], 2);
close(pipefd[1]);
那么,所有写入stderr的东西都可以通过读取pipe[0]得到:
read(pipe[0], buffer, ...);
【讨论】:
read 必须在不同线程(或不同进程)的循环中才能可靠。
如果他们使用对 std::cerr 的调用,您可以将其重定向到 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_;
};
您可以使用以下方法捕获输出:
std::ostringstream os;
cerr_redirector red(os);
std::cerr << "This is written to the stream" << std::endl;
std::cout 将不受影响:
std::cout << "This is written to stdout" << std::endl;
因此您可以测试您的捕获是否正常工作:
std::cout << "and now: " << os.str() << std::endl;
或者只是将os.str() 的内容添加到您的 Qt 窗口中。
【讨论】:
在这里我找到了我需要的完整实现......
感谢大家的帮助! :)
Will loading a DLL dynamically reconcile its stderr to a main application? If so, then how...?
【讨论】: