问题
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