dig*_*evo -5 c++ io error-handling fmt c++23
我想知道标准委员会是否已经修复了臭名昭著的Hello, world! 漏洞。我主要谈论新<print>库(尚未在任何编译器中使用)。
{fmt}库(它启发了标准库)尚未修复此问题。显然,它在输出时不会抛出任何异常/dev/full(从 v9.1.0 开始)。因此,使用 CI/O 函数(例如std::fflush错误处理)仍然是一件事。
下面的程序注意到错误并返回失败代码(因此没有错误):
#include <exception>
#include <cstdio>
#include <cstdlib>
#include <fmt/core.h>
int main()
{
fmt::println( stdout, "Hello, world!" );
if ( std::fflush( stdout ) != 0 || std::ferror( stdout ) != 0 ) [[unlikely]]
{
return EXIT_FAILURE;
}
}
Run Code Online (Sandbox Code Playgroud)
但这在 C++23 中可能吗?
#include <print>
#include <exception>
#include <cstdio>
#include <cstdlib>
int main()
{
try
{
std::println( stdout, "Hello, world!" );
}
catch ( const std::exception& ex )
{
return EXIT_FAILURE;
}
}
Run Code Online (Sandbox Code Playgroud)
对于那些不知道“Hello World”错误的人,下面的程序(Rust 中)会出现恐慌并输出一条有用的错误消息:
#include <exception>
#include <cstdio>
#include <cstdlib>
#include <fmt/core.h>
int main()
{
fmt::println( stdout, "Hello, world!" );
if ( std::fflush( stdout ) != 0 || std::ferror( stdout ) != 0 ) [[unlikely]]
{
return EXIT_FAILURE;
}
}
Run Code Online (Sandbox Code Playgroud)
#include <print>
#include <exception>
#include <cstdio>
#include <cstdlib>
int main()
{
try
{
std::println( stdout, "Hello, world!" );
}
catch ( const std::exception& ex )
{
return EXIT_FAILURE;
}
}
Run Code Online (Sandbox Code Playgroud)
相反,C++ 标准iostreams以及其他一些语言(C、Ruby、Java、Node.js、Haskell 等)默认情况下不会报告任何失败,即使在程序关闭文件流时也是如此。另一方面,其他一些(Python3、Bash、Rust、C# 等)确实报告了错误。
该函数的文档表明,如果写入流失败(以及其他失败的其他异常),std::println它将抛出。std::system_error当然,std::println成功写入流,失败通常发生在稍后将流实际写入文件系统时。
在 C++ 环境中,如果您需要保证数据确实到达磁盘,您有时需要使用类似的方法std::flush来检查是否没有发生错误。您可以争论这是否方便,但这遵循这样的逻辑:如果您不需要该功能,则不应该有任何开销。这是一个功能,而不是一个错误。
如果您需要这种保证,请编写一个小包装器,该包装器使用RAII技术在出现错误时抛出异常。这是关于析构函数中的释放与提交语义以及何时引入析构函数是一个好主意的很好的讨论。
#include <iostream>
struct SafeFile {
SafeFile(const std::string& filename)
: fp_(fopen(filename.c_str(), "w"))
, nuncaught_(std::uncaught_exceptions()) {
if (fp_ == nullptr)
throw std::runtime_error("Failed to open file");
}
~SafeFile() noexcept(false) {
fflush(fp_);
if (ferror(fp_) and nuncaught_ == std::uncaught_exceptions()) {
fclose(fp_);
throw std::runtime_error("Failed to flush data");
}
fclose(fp_);
}
auto operator*() {
return fp_;
}
FILE *fp_{nullptr};
int nuncaught_{};
};
int main()
{
try {
SafeFile fp("/dev/urandom");
fprintf(*fp, "Hello, world!");
}
catch ( const std::exception& ex )
{
std::cout << "Caught the exception" << std::endl;
return EXIT_FAILURE;
}
}
Run Code Online (Sandbox Code Playgroud)
Caught the exception
Run Code Online (Sandbox Code Playgroud)