懒惰参数评估

Fla*_*ame 12 c++ c++11

我正在将一些跟踪和调试代码添加到我正在重构的类中.

我有一个Trace具有一定的过滤属性和方法的对象bool CanTrace(Level, , TracePropertyList = no_additional_properties)bool Trace(Level, string, TracePropertyList = no_additional_properties).

代码中已经有很多地方使用了这个跟踪对象,并且该Trace方法的字符串参数通常是一些表达式,如果我不打算最终输出跟踪信息,我想避免评估.

重复一大堆代码

if(trace.CanTrace(LEVEL_INFO, some_props))
  trace.Trace(LEVEL_INFO, consume_time().to_str(), some_props);
Run Code Online (Sandbox Code Playgroud)

是丑陋的,我想要更短的东西.

我在考虑宏

#define TRACE_WITH_PROPS(LEVEL,STRING,PROPS) //...
Run Code Online (Sandbox Code Playgroud)

#define TRACE(LEVEL,STRING) //...
Run Code Online (Sandbox Code Playgroud)

有一个更好的方法吗?可能使用模板或C++ 11?我不喜欢用编译器隐藏编译器中的东西,我正在尽力去除这个代码库中的其他地方的一些宏.

Jan*_*dec 6

在C++ 11中,您可以使用闭包.你有类似的东西:

trace.Trace(LEVEL_INFO, [&](){ return format("x is %i") % ExpensiveWayToGetX(y, z); }, some_props);
Run Code Online (Sandbox Code Playgroud)

在C++ 03中,您可以使用boost.lambda hack来获得类似的效果.

但是,有一个宏包装if(trace.CanTrace(...)) trace.Trace(...)仍然稍微更高效,因为它甚至没有初始化对象与闭包将需要跟踪时所需的所有引用.我建议将宏与流接口结合起来,这样你就可以了

#define TRACE(level, some_props) \
    if(!trace.CanTrace(level, some_props)) 0; \
        else trace.Trace(level, some_props)
Run Code Online (Sandbox Code Playgroud)

并称之为

TRACE(LEVEL_INFO, some_props) << "x is " << ExpensiveWayToGetX(y, z);
Run Code Online (Sandbox Code Playgroud)

或定义operator()而不是operator<<和为printf样式格式:

TRACE(LEVEL_INFO, some_props)("x is %i", ExpensiveWayToGetX(y, z));
Run Code Online (Sandbox Code Playgroud)

if如果没有启用,则为0,否则实际上是跟踪,所以如果你写过它就不会吃掉其他的:

if(IsSomethingWrong())
    TRACE(LEVEL_WARNING, some_props) << WhatIsWrong() << " is wrong";
else
    DoSomething();
Run Code Online (Sandbox Code Playgroud)

(在宏中没有其他内容,之后的else将转到宏内部的if,但是除此之外,它将正确解析)


Bja*_*une 0

为了避免字符串处理,lambda 是一个很好的解决方案,如 Johannes Schaub 的回答所示。如果您的编译器尚不支持 lambda,那么您可以在没有 C++0x 功能且没有宏的情况下这样做:

doTrace(LEVEL_INFO, some_props) << "Value of i is " << i;
Run Code Online (Sandbox Code Playgroud)

doTrace 将返回一个带有虚拟运算符 << 的临时对象。如果不执行跟踪,则返回一个其operator<< 不执行任何操作的对象。否则返回一个对象,其运算符 << 执行您想要的操作。临时对象的析构函数可以发出信号,表明正在输出的字符串已完成。现在析构函数不应该抛出异常,因此如果跟踪事件的最终处理可以抛出异常,那么您可能需要包含operator<<的事件结束重载

即使没有进行跟踪,此解决方案也会导致多次虚拟函数调用,因此它的效率低于“if(tracing) ...”。这应该只在性能关键循环中真正重要,您可能无论如何都想避免进行跟踪。在任何情况下,在这些情况下或者当执行跟踪的逻辑比一系列 << 的序列更复杂时,您都可以恢复使用 if 检查跟踪。