c++ log functions using template SFINAE for conditional compile

前提是你 提交于 2021-02-19 08:34:44

问题


I am evaluating if it is possible to leverage C++11 features to replace logging Macros without any run-time additional cost.

I come out with this demo:

enum class LogLevel {
    Fatal = 0,
    DFatal = 1,
    Error = 2,
    Normal = 3,
    Verbose = 4,
    Debug = 5
};

constexpr LogLevel log_compiled = LogLevel::Normal;
LogLevel log_runtime = LogLevel::Error;

#ifdef NDEBUG
constexpr LogLevel log_fatal = LogLevel::Fatal;
#else
constexpr LogLevel log_fatal = LogLevel::DFatal;
#endif


template <LogLevel L, typename std::enable_if<(L <= log_fatal)>::type* = nullptr>
void Log(std::string message) {

    std::cout << "Fatal level: " << (int) L << " Message: " << message << std::endl;
    exit(0);
}

template <LogLevel L, typename std::enable_if<(L>log_fatal && L <= log_compiled)>::type* = nullptr>
void Log(std::string message) {

    if (L <= log_runtime) {
        std::cout << "Level: " << (int) L << " Message: " << message << std::endl;
    }

}

template <LogLevel L, typename std::enable_if<(L > log_compiled)>::type* = nullptr>
void Log(std::string message) {
}

int main(int argc, char *argv[]) {

    //not compiled
    Log<LogLevel::Verbose>("Something to much usual");

    //compiled, not printed
    Log<LogLevel::Normal>("Something usual");

    //compiled, printed
    Log<LogLevel::Error>("No disk space");

    //compiled, printed, terminate in Debug mode
    Log<LogLevel::DFatal>("Unexpected contition, recoverable");

    //compiled, printed, terminate always
    Log<LogLevel::Fatal>("Unexpected contition, unrecoverable");

    return 0;
}

This way I handle the compile time exclusion, the runtime log level and the fatal conditions in a very consistent way.

It would probably be adapted for streams with the << operator.

My questions:

//not compiled
Log<LogLevel::Verbose>("Something to much usual");

Will this actually result in a NOOP by most compilers? Will the string exist in the code?

Is this approach a bad idea?


回答1:


As written, the compiler cannot optimize away

Log<LogLevel::Verbose>("Something to much usual");

because it constructs and then destructs a std::string, which may have side effects (e.g., allocating and then freeing memory using possibly-replaced ::operator new and ::operator delete).

If you write your Log templates to take a const char * instead, however, then the call can be fully optimized out. Given

template <LogLevel L, typename std::enable_if<(L > log_compiled)>::type* = nullptr>
void Log(const char * ) {
}

int main() {
    Log<LogLevel::Verbose>("Something to much usual");
    return 0;
}

g++ 4.9 at -O2 compiles it to simply

xorl    %eax, %eax
ret



回答2:


Actually, all of those Log<> functions will be included in the executable code, except when the compiler can't find the appropriate template function overload. In that case you'll get a compile error. The only case when a template function is not included is when it is not used anywhere. So, non of your functions calls resolve in NOOP.



来源:https://stackoverflow.com/questions/25352964/c-log-functions-using-template-sfinae-for-conditional-compile

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!