为什么使用 freopen() 和 stderr 重定向 assert() 的输出可以工作,而在正常重定向 std::cerr 的缓冲区时却不能?

Why does redirecting the output of assert() work when done using freopen() and stderr but not when redirecting the buffer of std::cerr normally?

提问人:JensB 提问时间:9/23/2023 更新时间:9/23/2023 访问量:64

问:

我想重定向断言失败时创建的输出,以便将其写入日志文件而不是输出窗口。我假设用于输出,但是当重定向到我的日志文件时,仍然输出到输出窗口而不是我的文件:assert()assert()std::cerrstd::cerrassert()

std::ofstream ofs("log-file.txt");
std::cerr.rdbuf(ofs.rdbuf());

assert(false); // Still outputs "Assertion failed: false" to the output window and not to the log file

我发现了一个有类似问题的 SO 问题,它的答案建议改用,如下所示:freopen

freopen("log-file.txt", "w", stderr);

这给了我一个错误说'freopen': This function or variable may be unsafe. Consider using freopen_s instead.

freopen_s似乎采取不同的论点,我无法让它工作。freopen

建议使用另一个 SO 答案,但只需添加到 Visual Studio 项目属性中的命令行即可。这确实删除了错误,现在正确地输出到我的日志文件中。freopen_CRT_SECURE_NO_WARNINGSassert()

但这一切似乎不必要地复杂。感觉应该有一种平稳的方法来实现我想做的事情,而不会抑制 3 级编译器警告。为什么我不能简单地简单地重定向用于输出的任何内容的缓冲区?究竟用什么来输出,如果不是 ,或者?(我试过重定向所有三个)assert()assert()std::coutstd::cerrstd::clog

C++ cout 断言 freopen

评论

2赞 Sam Varshavchik 9/23/2023
由于是一个 C 库函数,因此使用 C++ 类作为其输出将是一个非常巧妙的技巧。assert()
0赞 Ted Lyngmo 9/23/2023
“这一切似乎都不必要地复杂” - 确实如此,因为他们注定要禁用 MSVC 中的某些标准功能,因此有必要定义是否要在 MSVC 中使用这些功能。否则,它非常简单。_CRT_SECURE_NO_WARNINGS
0赞 Retired Ninja 9/23/2023
并不是说我认为Microsoft试图强迫你使用的其他“安全”功能与正确使用它们所取代的功能相比非常有用,但它并不难使用。文档页面上甚至还有一个示例。learn.microsoft.com/en-us/cpp/c-runtime-library/reference/......freopen_s
0赞 Eugene 9/23/2023
在 Visual Studio 中,重定向仅适用于控制台应用程序。请考虑创建自己的类似断言的宏。stderr
0赞 JensB 9/26/2023
@TedLyngmo 用于执行此操作是否常见?重定向输出不应该是C++中非常普遍的事情吗?我的意思是,大多数现实生活中的代码不是使用某种外部日志记录吗,当断言失败时,您会希望更新吗?当然,程序在将原因输出到错误位置时静默退出从来都不是理想的行为吗?freopenassert

答:

0赞 Ahmed AEK 9/23/2023 #1

您的进程有一个名为 的操作系统缓冲区,这是打印到的地方,因为它是一个 C 函数,另一方面是一个 C++ 对象,它将推送到 std::cerr 的文本打印到缓冲区。stderrassertstd::cerrstderr

修改对象将更改将打印到的缓冲区,但它不会修改直接打印的底层操作系统缓冲区,以便修改您需要特定于操作系统的功能,例如 or(其实现由操作系统定义,因为它们正在修改操作系统缓冲区)std::cerrstd:cerrstderrassertfreopendup2