Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy Parameter Evaluation

Tags:

c++

c++11

I'm adding a bit amount of tracing and debugging code into a class that I'm refactoring.

I have a Trace object which has some filtering properties and methods bool CanTrace(Level, , TracePropertyList = no_additional_properties) and bool Trace(Level, string, TracePropertyList = no_additional_properties).

There are already many places in the code where this trace object is used, and the string argument to the Trace method is usually some expression that I would like to avoid evaluating if I'm not going to end up outputting tracing info.

Repeating the chunk of code

if(trace.CanTrace(LEVEL_INFO, some_props))
  trace.Trace(LEVEL_INFO, consume_time().to_str(), some_props);

is ugly, and I'd like something shorter.

I was thinking about the macros

#define TRACE_WITH_PROPS(LEVEL,STRING,PROPS) //...

and

#define TRACE(LEVEL,STRING) //...

Is there a better way to do this? Possibly with templates or C++11? I don't like hiding things from the compiler with defines, and I'm doing my best to remove some macros elsewhere in this codebase.

like image 284
Flame Avatar asked Jul 15 '11 18:07

Flame


1 Answers

In C++11, you can use closure. You'd have something like:

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

In C++03, you can use the boost.lambda hack for similar effect.

However, having a macro wrapping the if(trace.CanTrace(...)) trace.Trace(...) is still slightly more efficient, because it doesn't even initialize the object with all the references the closure will need if tracing is not on. I suggest combining the macro with the stream interface, so you'd have

#define TRACE(level, some_props) \
    if(!trace.CanTrace(level, some_props)) 0; \
        else trace.Trace(level, some_props)

and call it like

TRACE(LEVEL_INFO, some_props) << "x is " << ExpensiveWayToGetX(y, z);

or define operator() instead of operator<< and for printf-style formatting:

TRACE(LEVEL_INFO, some_props)("x is %i", ExpensiveWayToGetX(y, z));

The if not enabled, than 0, else actually trace is there so that it does not eat the else if you ever write:

if(IsSomethingWrong())
    TRACE(LEVEL_WARNING, some_props) << WhatIsWrong() << " is wrong";
else
    DoSomething();

(without else in the macro, the else after it would go to the if inside the macro, but with that else, it will parse correctly)

like image 183
Jan Hudec Avatar answered Sep 20 '22 21:09

Jan Hudec