RAII 获取在销毁过程中捕获的错误的方法
RAII way to get errors that are caught during destruction
在 Wikipedia 上文件 I/O 的 RAII 典型示例中,关闭文件时发生的任何错误都被吞没:
#include <iostream>
#include <string>
#include <fstream>
#include <stdexcept>
void write_to_file (const std::string & message) {
// try to open file
std::ofstream file("example.txt");
if (!file.is_open())
throw std::runtime_error("unable to open file");
// write message to file
file << message << std::endl;
// file will be closed when leaving scope (regardless of exception)
}
似乎无法判断file
自动关闭时是否出错;显然,当 file
在范围内时,只能调用 file.rdstate()
。
我可以手动调用 file.close()
然后检查错误,但我必须在范围内 return 的每个地方都这样做,这违背了 RAII 的目的。
有人评论说析构函数中只会发生文件系统损坏等不可恢复的错误,但我不相信这是真的,因为据我所知,析构函数会在关闭文件之前刷新文件,并且在刷新时可能会发生可恢复的错误。
那么有没有一种通用的 RAII 方法来获取销毁过程中发生的错误?我读到从析构函数中抛出异常是危险的,所以这听起来不像是正确的方法。
我能想到的最简单的方法是注册一个回调函数,如果在销毁过程中出现任何错误,析构函数将调用该回调函数。令人惊讶的是,似乎没有 ios_base::register_callback
支持的事件。这似乎是一个重大疏忽,除非我误解了什么。
但也许回调是在现代 class 设计中销毁期间获得错误通知的最常见方式?
我假设在析构函数中调用任意函数也是危险的,但也许将调用包装在 try/catch
块中是完全安全的。
您可能会部分处理析构函数失败的情况:
class Foo {
public:
Foo() : count(std::uncaught_exceptions()) {}
~Foo() noexcept(false)
{
if (std::uncaught_exceptions() != count) {
// ~Foo() called during stack unwinding
// Cannot throw exception safely.
} else {
// ~Foo() called normally
// Can throw exception
}
}
private:
int count;
};
如果您有特定的代码来处理展开时文件关闭的任何错误,您可以添加另一个抽象级别...
class MyFileHandler {
std::ofstream& f_;
public:
MyFileHandler(std::ofstream& f) : f_(f) {}
~MyFileHandler() {
f_.close();
// handle errors as required...
}
// copy and assignments elided for brevity, but should well be deleted
};
void write_to_file (const std::string & message) {
// try to open file
std::ofstream file("example.txt");
MyFileHandler fileCloser(file);
if (!file.is_open())
throw std::runtime_error("unable to open file");
// write message to file
file << message << std::endl;
// file will be closed when leaving scope (regardless of exception)
}
根据您的用例,您可以将 std::ofstream
嵌入 class。
在 Wikipedia 上文件 I/O 的 RAII 典型示例中,关闭文件时发生的任何错误都被吞没:
#include <iostream>
#include <string>
#include <fstream>
#include <stdexcept>
void write_to_file (const std::string & message) {
// try to open file
std::ofstream file("example.txt");
if (!file.is_open())
throw std::runtime_error("unable to open file");
// write message to file
file << message << std::endl;
// file will be closed when leaving scope (regardless of exception)
}
似乎无法判断file
自动关闭时是否出错;显然,当 file
在范围内时,只能调用 file.rdstate()
。
我可以手动调用 file.close()
然后检查错误,但我必须在范围内 return 的每个地方都这样做,这违背了 RAII 的目的。
有人评论说析构函数中只会发生文件系统损坏等不可恢复的错误,但我不相信这是真的,因为据我所知,析构函数会在关闭文件之前刷新文件,并且在刷新时可能会发生可恢复的错误。
那么有没有一种通用的 RAII 方法来获取销毁过程中发生的错误?我读到从析构函数中抛出异常是危险的,所以这听起来不像是正确的方法。
我能想到的最简单的方法是注册一个回调函数,如果在销毁过程中出现任何错误,析构函数将调用该回调函数。令人惊讶的是,似乎没有 ios_base::register_callback
支持的事件。这似乎是一个重大疏忽,除非我误解了什么。
但也许回调是在现代 class 设计中销毁期间获得错误通知的最常见方式?
我假设在析构函数中调用任意函数也是危险的,但也许将调用包装在 try/catch
块中是完全安全的。
您可能会部分处理析构函数失败的情况:
class Foo {
public:
Foo() : count(std::uncaught_exceptions()) {}
~Foo() noexcept(false)
{
if (std::uncaught_exceptions() != count) {
// ~Foo() called during stack unwinding
// Cannot throw exception safely.
} else {
// ~Foo() called normally
// Can throw exception
}
}
private:
int count;
};
如果您有特定的代码来处理展开时文件关闭的任何错误,您可以添加另一个抽象级别...
class MyFileHandler {
std::ofstream& f_;
public:
MyFileHandler(std::ofstream& f) : f_(f) {}
~MyFileHandler() {
f_.close();
// handle errors as required...
}
// copy and assignments elided for brevity, but should well be deleted
};
void write_to_file (const std::string & message) {
// try to open file
std::ofstream file("example.txt");
MyFileHandler fileCloser(file);
if (!file.is_open())
throw std::runtime_error("unable to open file");
// write message to file
file << message << std::endl;
// file will be closed when leaving scope (regardless of exception)
}
根据您的用例,您可以将 std::ofstream
嵌入 class。