gpt4 book ai didi

C++ 异常处理和错误报告习惯用法

转载 作者:IT老高 更新时间:2023-10-28 22:28:33 24 4
gpt4 key购买 nike

在 C++ 中,RAII 通常被认为是处理异常的一种优越方法:如果抛出异常,则解除堆栈,调用所有析构函数并清理资源。

但是,这会导致错误报告出现问题。假设一个非常通用的函数失败,堆栈被展开到顶层,我在日志中看到的只是:

Couldn't read from socket: connection reset by peer.

...或任何同样通用的消息。这并没有说明引发异常的上下文。特别是如果我正在运行类似事件队列处理循环的东西。

当然,我可以用 try/catch block 包装对套接字读取的每个调用,捕获异常,用更详细的上下文信息构造一个新的异常并重新抛出它,但这违背了拥有 RAII 的目的,并且是缓慢但肯定会比处理返回错误代码更糟糕。

在标准 C++ 中详细报告错误的更好方法是什么?我也愿意接受涉及 Boost 的建议。

最佳答案

正如 James McNellis 在这里所建议的,有一个非常巧妙的技巧,涉及到一个保护对象和 std::uncaught_exception 工具。

想法是这样写代码:

void function(int a, int b)
{
STACK_TRACE("function") << "a: " << a << ", b: " << b;

// do anything

}

并且仅在实际抛出异常的情况下记录消息。

类很简单:

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__)

可以使用 __PRETTY_FUNC__ 或等效项来避免命名函数,但我在实践中发现它对我自己的口味来说过于困惑/冗长。

请注意,如果您希望它一直存在到作用域的末尾,则需要一个命名对象,这就是这里的目的。我们可以想出一些棘手的方法来生成唯一标识符,但我从来不需要它,即使在函数内保护更窄的范围时,名称隐藏规则也对我们有利。

如果您将它与 ExceptionManager (引发的异常自行注册的地方)结合使用,那么您可以获得对最新异常的引用,并且在进行日志记录时,您可以决定在其中设置堆栈异常本身。这样它就会被 what 打印出来,如果异常被丢弃则被忽略。

这是一个品味问题。

请注意,在存在 ExceptionManager 的情况下,您必须注意并非所有异常都可以用它检索 --> 只有您自己制作的异常。因此,您仍然需要采取措施防止 std::out_of_range 和第 3 方异常。

关于C++ 异常处理和错误报告习惯用法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3783370/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com