简体   繁体   中英

How can I prevent a logging string being compiled based on a compile-time verbosity level?

If in a 'logger.h' file I have this:

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;

I include this header in a bunch of.cpp files. Then in one of them I do:

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

Because the inline Logger<4> loggerObj is instructed to be defined only once and we include the header in multiple.cpp files, we don't know where it has been defined. We only know that the compiler defines it once in 'some'.cpp. Therefore when we call func() in some.cpp the 'loggerObj' identifier is probably akin to something like 'extern Logger<4> loggerObj;'. If this is the case, am I right in saying that the compiler doesn't have its full definition at that point? Also that would mean that the everything in the func() function will be compiled, and then only the linker can do the if constexpr statement against the object that's probably defined in another translation unit.

I'm not sure if this is a link-time optimization, but what's in the LOG() macro invocation should be easily optimized out by the linker, right? In other words the code generated shouldn't contain std::string() + std::string() etc.?

Unreal Engine has logging system similar to this but I think it may work completely at the macro level, so in macro invocation UELog(category, verbosity, msg) I think the entire line is just elided by the preprocessor instead of copying something like if constexpr.

Is there a way I can do this? I can't find the source code for this part of the logging system.

Your solution is almost correct. It doesn't compile because compileTimeVerbosity is not constexpr, but if you make it constexpr replacing

static inline int compileTimeVerbosity = CompileTimeVerbosity;

with

static constexpr int compileTimeVerbosity = CompileTimeVerbosity;

then it does what you want: https://godbolt.org/z/azecWMY86 .

As a side note, never make a macro that expands into if (...) { } . Loot at this example:

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

You'd expect else branch refers to the outer if , but in reality if inside the macro hijacks it.

The way to fix this is to always wrap macro code in do {... } while (false) , eg:

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

This loop executes exactly once and it doesn't have nasty gotchas.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM