如何防止基于编译时详细级别编译日志记录字符串



如果在"logger.h"文件中,我有这个:

template <int CompileTimeVerbosity>
struct Logger
{
static inline int compileTimeVerbosity = CompileTimeVerbosity;
void log(const std::string& msg) { }
};
#define LOG(logger, msgVerbosity, msg) 
if constexpr (msgVerbosity >= logger.compileTimeVerbosity) 
{ 
logger.log(msg); 
} 
inline Logger<4> loggerObj;

我将这个标头包含在一堆.cpp文件中。然后我做了其中一个:

void func()
{
LOG(loggerObj, 2, std::string("Sorry, you called a ") + std::to_string(int()) + " on a " + std::to_string(int()))
}

因为内联Logger<4>loggerObj被指示只定义一次,并且我们在多个.cpp文件中包含了标头,我们不知道它是在哪里定义的。我们只知道编译器在"some".cpp中定义了一次。因此,当我们在some.cpp中调用func((时,"loggerObj"标识符可能类似于"extern Logger<4>loggerObj;'。如果是这样的话,我说编译器在这一点上没有完整的定义是对的吗?这也意味着func((函数中的所有内容都将被编译,然后只有链接器才能对可能在另一个转换单元中定义的对象执行if constexpr语句。

我不确定这是否是链接时间优化,但LOG((宏调用中的内容应该很容易被链接器优化出来,对吧?换句话说,生成的代码不应该包含std::string((+std::字符串((等。?

Unreal Engine有类似的日志记录系统,但我认为它可能完全在宏级别工作,所以在宏调用UELog(category,verbose,msg(中,我认为预处理器只是忽略了整行内容,而不是复制if constexpr之类的内容。

有什么办法我能做到这一点吗?我找不到日志系统这一部分的源代码。

您的解决方案几乎是正确的。它不编译,因为compileTimeVerbosity不是constexpr,但如果您将其设置为constexpr以替换

static inline int compileTimeVerbosity = CompileTimeVerbosity;

带有

static constexpr int compileTimeVerbosity = CompileTimeVerbosity;

那么它会做你想做的事:https://godbolt.org/z/azecWMY86.

顺便说一句,千万不要制作一个扩展为if (...) { }的宏。本例中的织机:

if (...)
LOG(...)
else
...

您可能期望else分支引用外部if,但实际上宏内部的if劫持了它

解决此问题的方法是始终将宏代码包装在do { ... } while (false)中,例如:

#define LOG(logger, msgVerbosity, msg) 
do { if constexpr (...) { ... } } while(false)

这个循环只执行一次,并且没有令人讨厌的gotchas。

最新更新