【发布时间】:2020-11-02 09:22:10
【问题描述】:
在 C++20 中引入的新标准库工具中,有 std::source_location——一个允许我们方便地捕获和处理以前只能通过实现定义的宏(如 __FILE__ 或 __LINE__)访问的信息的类.我的理解是它的预期用途是记录和错误报告功能。例如,一个函数声明为
void log(const char* message, std::source_location src_loc = std::source_location::current())
可以记录一条消息,包括源文件名和来自其调用位置的行。然而,经过一些实验,我很想更广泛地使用source_location,我想知道这是否是一个好习惯。
举个例子,假设我正在开发一个应用程序,它可能会遇到一个特定的错误,它没有合理的恢复方式。唯一要做的就是关闭,但我想确保记录一些有关导致错误的情况的有用信息。我将介绍一些非常简化的sn-ps代码,在这个问题中我只想专注于报告错误的位置,因此我将跳过有关记录其他信息或关闭程序的详细信息。
假设错误总是发生在函数foo中。一种简单的报告方式是:
int foo(double x)
{
// ...
if (error_occurred)
{
log(error_message);
// shutdown
}
// ...
}
但是,这将报告来自log 调用站点的错误,这始终与foo 的实现完全相同。这不是很有用,特别是如果foo 是一个从代码中的不同位置多次调用的低级函数。将导致错误的调用位置报告给foo 会更有帮助。我想比较解决这个问题的两种方法。
1。抛出异常
假设我们有像fun_1、fun_2、...这样调用foo的函数。我们可以将foo修改为
int foo(double x)
{
// ...
if (error_occurred)
{
throw std::runtime_error{error_message};
}
// ...
}
然后在fun_1:
try
{
n = foo(x);
}
catch (const std::runtime_error& error)
{
log(error.what());
// shutdown
}
这样log 将报告与调用foo 相关联的catch 块的位置。
2。传播source_location
或者,我们可以修改foo的签名和正文:
int foo(double x, std::source_location src_loc = std::source_location::current())
{
// ...
if (error_occurred)
{
log(error_message, src_loc);
// shutdown
}
// ...
}
然后,在fun_1 中除了n = foo(x); 之外不做任何事情,我们得到与方法1 相同的报告。(甚至稍微好一点,因为它直接指向foo 的调用而不是catch 块。 )
这两种方法都不完全令人满意,因为它们都给代码添加了一些噪音,但我认为这是错误处理不可避免的,问题是如何最小化它。我看到以下问题。
- 抛出异常
-
foo的呼叫站点被严重污染。如果有很多函数,如fun_1和fun_2,可能每个函数都调用foo多次,并且我们想准确报告哪个调用导致错误,那么每个函数都必须用try/catch块装饰.
- 传播
source_location
-
foo的签名被污染了。 - 如果
foo和fun_1之间的调用堆栈较长,那么我们将不得不污染所有中间函数的签名和调用站点。
我相信方法 1 在 C++ 中是相当标准的做法,但我找不到任何关于按照我建议的方式使用 source_location 的信息。这会是个好主意吗?使用source_location 作为属于“生产代码”的函数中的参数而不是专用的错误处理函数是否合理?这会导致我忽略的任何问题吗?
【问题讨论】:
标签: c++ error-handling