使用std::source_location报告错误的最佳实践
Best practice for error reporting with std::source_location
在C++20中引入的新标准库功能中,有一个std::source_location
类,它使我们能够方便地捕获和处理以前只能通过实现定义的宏(如__FILE__
或__LINE__
(访问的信息。我的理解是,它的预期用途是用于日志记录和错误报告功能。例如,声明为的函数
void log(const char* message, std::source_location src_loc = std::source_location::current())
可以记录包括源文件名和来自其调用位置的行的消息。然而,经过一点实验,我很想更广泛地使用source_location
,我想知道这是否是一个好的实践。
为了举个例子,让我们假设我正在开发一个应用程序,该应用程序可能会遇到一个特定的错误,而该错误没有合理的恢复方法。唯一要做的就是关闭,但我想确保记录一些关于导致错误的情况的有用信息。我将介绍一些非常简化的代码片段,在这个问题中,我只想专注于报告错误的位置,所以我将跳过记录其他信息或关闭程序的详细信息。
假设错误总是发生在函数foo
中。一种简单的报告方式是:
int foo(double x)
{
// ...
if (error_occurred)
{
log(error_message);
// shutdown
}
// ...
}
然而,这将报告来自log
的调用站点的错误,该调用站点始终与foo
的实现完全相同。这不是很有用,尤其是如果foo
是一个从代码中的不同位置多次调用的低级别函数。向foo
报告导致错误的调用位置会更有帮助。我想比较一下解决这个问题的两种方法。
1.抛出异常
假设我们有类似fun_1
、fun_2
、…的函数,。。。其呼叫CCD_ 12。我们可以将foo
修改为
int foo(double x)
{
// ...
if (error_occurred)
{
throw std::runtime_error{error_message};
}
// ...
}
然后在CCD_ 14内部:
try
{
n = foo(x);
}
catch (const std::runtime_error& error)
{
log(error.what());
// shutdown
}
这样,CCD_ 15将向CCD_ 16报告与呼叫相关联的捕获块的位置。
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
的信息。这是个好主意吗?使用CCD_ 34作为属于"0"的函数中的参数是否合理;生产代码";而不仅仅是专用的错误处理功能?这会导致我忽略的任何问题吗?
考虑选项3
int foo(double x)
{
// ...
if (error1_occurred)
{
log(error_message, std::source_location::current());
// shutdown
} else if (error2_occured) {
log(error_message, std::source_location::current());
}
// ...
}
当有多个错误时,你想知道它是从哪里来的。您想知道错误发生的位置的source_location,而不是调用堆栈中的某个位置。这不是噪音,但它为日志输出添加了有价值的信息。
另一方面,如果你确实想在某个地方收集信息,可以考虑选项4:
int foo(double x)
{
// ...
if (error_occurred)
{
throw my_error{error_message,std::source_location::current()};
}
// ...
}
这不是很有用,尤其是如果foo是一个低级别函数,它在代码中的不同位置被多次调用
这与旧的__FILE__
和__LINE__
宏没有什么不同。它们显示预处理器替换它们的文件/行。std::source_location
的工作原理类似。
话虽如此,但由您决定要在日志中包含哪些信息。没有一个一刀切的解决方案。
- 警告处理为错误这里有什么问题
- "error: no matching function for call to"构造函数错误
- 在C#中处理C++指针而不使用unsafe的最佳方法
- boost::进程间消息队列引发错误
- C++,OpenCV,尝试显示图像时"OpenCV(4.3.0) Error: Assertion failed (size.width>0 && size.height>0)"此错误
- 有关插入适配器的错误。[错误]请求从 'back_insert_iterator<vector<>>' 类型转换为非标量类型
- QT在错误的班级中寻找空位
- vector.resize()中的分配错误
- 代码在main()中运行,但在函数中出现错误
- 释放错误后堆使用
- (C++)分析树以计算返回错误值的简单算术表达式
- 使用std::source_location报告错误的最佳实践
- 在C++中样板"冷/never_inline"错误处理技术的最佳方法是什么?
- 抽象包装带有异常的 C 错误处理的最佳方法
- 以错误的方式调用子类中的函数的最佳方法是什么
- 从计数错误的文件中读取数据。读取数据的最佳做法是什么?
- 从C++函数返回有意义的错误-最佳实践
- 在C++文件IO期间捕获所有错误(或ios对象中的任何错误)的最佳实践
- c++:读取文件时错误处理(无异常)的最佳实践是什么?
- 从分段错误中恢复的最佳实践