我正在使用一个日志记录模块,可以在运行时启用/禁用报告。电话通常是这样的:
WARN(
"Danger Will Robinson! There are "
+ boost::lexical_cast<string>(minutes)
+ " minutes of oxygen left!"
);
我正在使用 WARN 的内联函数,但我很好奇幕后进行了多少优化——对整个程序中的参数进行评估将是昂贵的。
WARN
函数是这样的:
bool WARNINGS_ENABLED = false;
inline void WARN(const string &message) {
if (!WARNINGS_ENABLED) {
return;
}
// ...
}
考虑到构造字符串参数没有副作用,编译器会优化它吗?是否需要一定程度的优化(
-Ox
中的g++
对于某些x
)?
如果您需要能够在运行时有选择地启用和禁用警告,编译器将不能能够优化调用。
您需要的是将 function 重命名为
WARN2
并添加一个宏,例如:
#define WARN(s) do {if (WARNINGS_ENABLED) WARN2(s);} while (false)
这将阻止在运行时对 s 求值,除非启用了警告。
do-while 是一个技巧,允许它在代码中的任何地方使用(裸语句、带花括号的 if 块内的语句、不带花括号的 if 块内的语句、带花括号和不带花括号的 while 语句等)。
您可以使用 -S 选项检查 GCC/G++ 的功能。这将在实际组装之前输出代码 - 请参阅gcc(1)。
在这种情况下,GCC 和 G++ 的行为或多或少是相同的。 所以我先将代码翻译成C来进行一些进一步的测试:
char WARNINGS_ENABLED = 0;
inline void WARN(const char* message) {
if (!WARNINGS_ENABLED) {
return;
}
puts(message);
}
int main() {
WARN("foo");
return 0;
}
运行 gcc -O3 -S file.c 并查看输出文件 'file.s'
你会看到 GCC 没有删除任何东西!
这不是您所要求的,但为了让编译器有机会优化该代码,您必须使 WARNINGS_ENABLED constant。另一种方法是使其成为“静态”并且不更改该文件中的值。 但是:使其成为静态会产生符号无法导出的副作用。
static const char WARNINGS_ENABLED = 0;
inline void WARN(const char* message) {
if (!WARNINGS_ENABLED) {
return;
}
puts(message);
}
int main() {
WARN("foo");
return 0;
}
GCC 然后完全清理代码。
我不是 boost 专家,但我猜测有一种方法可以构造一个 lambda,只有在 WARNINGS_ENABLED 为 true 时才会对其进行求值以生成字符串。 类似...
inline void warnFunc(some_boost_lambda &message_generator) {
if (WARNINGS_ENABLED) {
cerr << message_generator() << endl;
}
}
#define WARN(msg) warnFunc(...insert boost magic here to turn msg into a lambda...)
void inline void LogWarning(const string &message)
{
//Warning
}
#ifdef WARNINGS_ENABLED
#define WARN(a) LogWarning(a)
#else
#define WARN(a)
#endif
这就是 ASSERT() 宏的工作原理。 WARN 中括号内的所有代码甚至都没有通过预处理器到达编译器。这意味着您可以做其他事情,例如
#ifdef WARNINGS_ENABLED
// Extra setup for warning
#endif
//....
WARN(uses setup variables)
它会以两种方式编译。
为了让优化器认识到括号中没有副作用,您可以在其中放置一些非常复杂的语句(即高级字符串操作),这些语句很难以任何方式证明。