5
votes

In our application, we created our own logging system. There are several different log types, debug, error, warning, communication, performance and ..., in this logging system. There are lots of #ifdef and #endif to disable a specific log type. These #ifdef and #endif make the code hard to read.

We are thinking remove these #ifdef and #endif and have a check before the message write to the file. That means there are lots of "useless" calls to the logging system. These calls will not result in any writing activity.

Is there a better way to turn on/off a log type, without these #ifdef and #endif AND these "useless" calls?

2
Can you give an example of what you mean? Do you just mean 1 ifdef per logging type, or something else? Maybe some code showing the ifdefs would help...rabensky
Can't you contain all the ifdefs in one place with something like #ifdef ENABLE_WARN \n #define WARN(x) printf(x) \n #else \n #define WARN(x) \n #endif?Oliver Charlesworth
@cluracan Not 100% sure, but my guess is that the OP is trying to avoid having a ton of #ifdef DEBUG ... #endif everywhereDennis Meng
You could make the optimizer omit those useless calls using a const global log level and separate functions for each type (i.e. if(log_lvl < 3) ect)BlackBear
@DennisMeng You mean he's putting the ifdefs inside the code?! In that case just do an ifdef once in a header file that defines a "log_error/warning/communication" macros that either do nothing or log depending on the loglevel?rabensky

2 Answers

7
votes

What about the following:

// comment out if not needed
#define ENABLE_LOG

#ifdef ENABLE_LOG
#  define LOG(x) x
#else
#  define LOG(x) (void) 0 
#endif

Later you could just call:

LOG(mylogger.call());

Updated the #else part as Dietrich Epp suggested.

7
votes

In addition to the #define solution which works very well, I'd like to present an alternative using templates

template<bool B>
void log(std::string message){}

template<>
void log<true>(std::string message){log_internal(message);}

#define DEBUG true
#define COMMUNICATION false

...

log<DEBUG>("this message will be logged");
log<COMMUNICATION>("this message won't");

The #define solution really is great for most cases, but there are some reasons to use this solution:

  • You may need scoping - i.e. not having your log devices dirty the global namespace. This solution can be put in a namespace where the #define one can't.

  • You may need stricter control on what can and can't be done. The LOG(x) define has the problem that anything can be put in the x - and if the logging is turned off you won't see a problem.

To clarivy - your code might compile and work with

LOG(std::cout << "Here!" << endl);

because this specific log is turned off. But one day 2 years from now someone will turn on the logging and get endl undefined errors all over the place. Or worse - he may find that turning on logging suddenly requires linking to a library long gone (because that logging specifically called a function defined in this library), or uses a function that has long-since changed interface (or even was removed completely! True story :( )

Edit

I was asked to add this to the answer:

It may seem like there's a function call overhead in cases where you don't log (the empty function). This isn't the case as the compiler optimizes it out. If you want to be sure of this - add an inline directive to the functions.

Also, you might want to change it from an std::string to a const char * just to make sure no string constructor is called - but that too should be optimized out automatically by the compiler.

Anyway, like I said this isn't intrinsically better than the #define solution. I actually still use the #define in my projects - but there are some specific instances where this template solution is preferable.