我有两个日志函数在进入和离开函数时显示,
void logEnter(const source_location location = source_location::current())
和void logExit(const source_location location = source_location::current())
,显示的信息是函数名称和行。
现在我有一个函数
Foo
,它有多个返回语句。我想在输入函数时调用 logEnter (使用默认参数)(简单),并在返回时调用 logExit (使用默认参数)。问题是,我必须为每个返回添加一个调用(如果我想在许多函数上实现这一点,我会很痛苦)。
我想知道是否可以想出一个单行解决方案,即类似于
lock_guard
,只需在函数 Foo 的开头定义一些对象即可实现此目的。对于一个简单的类,问题在于析构函数不能接受任何参数,并且它应该获取调用者源位置,而不是析构函数位置。
我不需要关心异常。澄清一下,函数 Foo 已经实现了,所以我编辑的代码越少越好。
示例:
void logEnter(const source_location location = source_location::current()){
cout << "Log, entering " << location.function_name() <<" at line "<< location.line() << endl;
}
void logExit(const source_location location = source_location::current()){
cout << "Log, exiting " << location.function_name() <<" at line "<< location.line() << endl;
}
// Working implementation
string parity(int x) {
logEnter();
if (x%2 == 0) {
logExit(); return "even";
}
logExit(); return "odd";
}
// Wish implementation
string parity(int x) {
logFunctionGuard();
if (x%2 == 0)
return "even";
return "odd";
}
调用 parity(3) 时,cout 终端应显示
Log, entering parity at line 10
Log, exiting parity at line 13
这可能有点被诅咒,但你可以做
#define return logExit(); return
自动将 logExit 添加到所有 return 语句中。如果您只想将其应用于 Foo,请在函数 Foo 的上方编写宏,在下方编写
#undef return
但请注意,使用关键字作为宏标识符可能会导致 UB,因为它是一个保留的宏名称,正如 Jarod42 指出的那样。
另一种选择是编写一个简单的类
class LogExit {
public:
LogExit(T t, std::source_location sourceLocation = std::source_location::current()){
cout << "Log, exiting " << location.function_name() <<" at line "<< location.line() << endl;
}
};
其中
T
是Foo的返回类型,它可以从T
隐式转换。然后将 Foo 的返回类型更改为 LogExit
瞧。这不适用于 void,并且需要您将返回值转换回 T
(如果需要)。
否则,有 boost stacktrace 那么您可以定义一个没有成员的类,并从其构造函数和析构函数中打印您感兴趣的堆栈跟踪部分。
我打算建议使用智能指针的删除器,例如这里:
#include <iostream>
#include <functional>
#include <memory>
#include <source_location>
using namespace std;
#define logFunctionGuard() \
logEnter(); \
const auto onExit = unique_ptr<char, function<void(char*)>>( \
(char*)1, \
[location = source_location::current()](char*) { \
logExit(location); \
} \
);
但不幸的是,这会为出口打印与入口相同的行。
更换
[location = source_location::current()](char*) {
由
[](char*, source_location location = source_location::current()) {
根本不提供正确的退出功能。
尽管如此,还是将其留在这里,并提供指向 Godbolt 的链接,作为其他人的灵感,因为评论太长了。