我有一个由多个函数组成的日志宏,它看起来像这样:
#define MY_LOG(s) { myMutex.lock(); std::cout << __PRETTY_FUNCTION__ << " (" << __LINE << ")" << s << std::endl; myMutex.unlock(); }
我希望能够调用宏,使其看起来类似于调用普通函数。我想让它以分号结束
在大多数情况下确实有效。下面是一个例子:
if (1 == 1)
MY_LOG("ok " << 1);
很好。没有问题。但在这种情况下不起作用:
if (1 == 1)
MY_LOG("1 == 1");
else
c++;
我得到一个错误,有一个else没有前面的if。有可能解决这个问题吗?
之所以有效,是因为在大多数情况下,一个额外的;
将被编译器忽略。你写的是{ ... };
。
对于if/else
则不是这样。考虑以下代码:
if (cond1)
if (cond2) something();
else {
...
}
如果额外的;
不能关闭内部的if
,那么您必须添加else { }
。C/c++的设计者使用;
作为它的简化版本。
现在要修复您的宏,您必须以需要;
但没有其他效果的东西结束。这个问题的解决方案是为什么在宏中使用明显没有意义的do-while和if-else语句?
#define mymacro(parameter) do{/**/} while(0)
但是随着std::source_location的引入,整个宏在现代(从c++20开始)c++中已经过时了。让它成为一个实际的函数:
void log(const std::string_view message,
const std::source_location location =
std::source_location::current())
{
std::lock_guard<std::mutex> lock(myMutex);
std::cout << "file: "
<< location.file_name() << "("
<< location.line() << ":"
<< location.column() << ") `"
<< location.function_name() << "`: "
<< message << 'n';
}
通常的解决方案是这样使用do { ... } while(0)
:
#define MY_LOG(s) do {
std::lock_guard<std::mutex> lock(myMutex);
std::clog << __PRETTY_FUNCTION__ << " (" << __LINE__ << ")" << s << 'n';
} while(false)
注意std::lock_guard
而不是手动锁定。
但是使用可变宏、可变函数模板和c++ 20std::source_location
,你可以做出更通用的东西:
#include <iostream>
#include <mutex>
#include <sstream>
#include <source_location>
template<class... Args>
void my_log(const std::source_location loc, Args&&... args)
{
static std::mutex myMutex;
std::ostringstream os; // used to build the logging message
os << loc.function_name() << " (" << loc.line() << "):" << std::boolalpha;
// fold expression:
((os << ' ' << args), ...); // add all arguments to os
std::lock_guard<std::mutex> lock(myMutex); // use a lock_guard
std::clog << os.str(); // and stream
}
// this just forwards the source location and arguments to my_log:
#define MY_LOG(...) my_log(std::source_location::current(), __VA_ARGS__)
int main() {
MY_LOG("A", 1, 2, 'B');
}
演示