Ale*_*x B 15 c++ exception-handling
在C++中,RAII经常被提倡作为异常处理的优秀方法:如果抛出异常,则堆栈被展开,所有析构函数都被调用,资源被清理.
但是,这会出现错误报告问题.假设一个非常通用的函数失败,堆栈被解开到顶层,我在日志中看到的只有:
无法从套接字读取:由对等方重置连接.
......或任何同样通用的信息.这并没有说明抛出异常的上下文.特别是如果我正在运行类似事件队列处理循环的东西.
当然,我可以用try/catch块包装每次调用套接字读取,捕获异常,构造一个带有更详细上下文信息的新内容并重新抛出它,但是它违背了使用RAII的目的,并且缓慢但肯定地变得比处理返回错误代码更糟糕.
在标准C++中详细报告错误报告的更好方法是什么?我也对涉及Boost的建议持开放态度.
正如詹姆斯麦克纳利斯在这里所说的那样,有一个非常巧妙的技巧涉及一个防守对象和std::uncaught_exception设施.
想法是编写这样的代码:
void function(int a, int b)
{
STACK_TRACE("function") << "a: " << a << ", b: " << b;
// do anything
}
Run Code Online (Sandbox Code Playgroud)
并且仅在实际抛出异常的情况下记录消息.
这堂课非常简单:
class StackTrace: boost::noncopyable // doesn't make sense to copy it
{
public:
StackTrace(): mStream() {}
~StackTrace()
{
if (std::uncaught_exception())
{
std::cout << mStream.str() << '\n';
}
}
std::ostream& set(char const* function, char const* file, unsigned int line)
{
return mStream << file << "#" << line << " - " << function << " - ";
}
private:
std::ostringstream mStream;
};
#define STACK_TRACE(func) \
StackTrace ReallyUnwieldyName; \
ReallyUnwieldyName.set(func, __FILE__, __LINE__)
Run Code Online (Sandbox Code Playgroud)
人们可以使用__PRETTY_FUNC__或等同于避免命名该功能,但我在实践中发现它对我自己的品味来说太过错误/冗长.
请注意,如果您希望它存在到作用域的末尾,则需要一个命名对象,这就是此处的目的.我们可以想出生成唯一标识符的棘手方法,但我从来没有需要它,即使在保护函数中的较窄范围时,名称隐藏规则对我们有利.
如果将它与ExceptionManager(将抛出的异常自身注册的东西)结合起来,那么您可以获得对最新异常的引用,并且在记录的情况下,您可以决定在异常本身内设置堆栈.因此,what如果异常被丢弃,它将被打印并被忽略.
这是一个品味问题.
请注意,在ExceptionManager您面前,您必须意识到并非所有异常都可以通过它来检索 - >只有您自己制作的异常.因此,您仍需要一定程度的防范std::out_of_range和第三方例外.
我从来没有真正这样做过,但你可以推出你自己的“堆栈跟踪”:
struct ErrorMessage {
const char *s;
ErrorMessage(const char *s) : msg(s) {}
~ErrorMessage() { if (s) std::cout << s << "\n"; }
void done() { s = 0; }
};
void someOperation() {
ErrorMessage msg("Doing the first bit");
// do various stuff that could throw
msg = "Doing the second bit";
// do more stuff that could throw
msg.done();
}
Run Code Online (Sandbox Code Playgroud)
您可以有多个级别(尽管不一定在每个级别都使用它):
void handleFoo() {
ErrorMessage msg("Handling foo event");
someOperation();
msg.done();
}
Run Code Online (Sandbox Code Playgroud)
并添加更多构造函数和成员:
void handleBar(const BarEvent &b) {
ErrorMessage msg(std::stringstream("Handling bar event ") << b.id);
someOperation();
msg.done();
}
Run Code Online (Sandbox Code Playgroud)
并且您无需将消息写入std::cout. 它可能是某个日志记录对象,并且该对象可以将它们排队,并且实际上不会将它们输出到日志,除非捕获站点告诉它这样做。这样,如果您捕获到不需要记录的异常,则不会写入任何内容。
它并不漂亮,但它比 try/catch/throw 或检查返回值更漂亮。如果您忘记调用done成功(例如,如果您的函数有多个返回而您错过了一个),那么您至少会在日志中看到您的错误,这与资源泄漏不同。
[编辑:哦,使用合适的宏,您可以将__FILE__和存储__LINE__在 中ErrorMessage。]
| 归档时间: |
|
| 查看次数: |
4318 次 |
| 最近记录: |