5
votes

After searching quite a bit in the Internet for a solution I decided to ask here if my solution is fine.

I'm trying to write a simple and modular C logging library intended to be simple to disable and specially helping PhD students and researchers to debug an algorithm reducing as much as possibile the impact of the logging system.

My problem is that I want make possible for the user of the library to disable the logging system at compile time producing an executable in which the cost of the logger is 0.

The C code would look like this:

...
logger_t * logger;

result = logger_init(logger);
if(result == -1) {...}
...

this will simply initialize the logger. Looking for an example code I have checked the assert.h header but that soulution results in a list of warnings in my case. In fact, if logger_init() is substituted by 0 using the macro this will result in the variable logger never used.

For this reason I've decided to use this approach:

int logger_init(logger_t *logger);

#ifndef NLOG /* NLOG not defined -> enable logging */
int logger_init(logger_t *logger) {
...
}
#else  /* NLOG defined --> the logging system must be disabled */
#define logger_init(logger)     (int)((logger = NULL) == NULL)
#endif /* NLOG */

this does not result in warnings and I also avoid the overhead of calling the function. In fact my first try was to do like this:

int logger_init(logger_t *logger) {
  #ifndef NLOG /* NLOG not defined -> enable logging */
  ...
  #endif
  return 0;
}

keep calling the function even if I do not need it.

Do you think my solution could be considered a good solution? Is there a better solution?

Thanks a lot, guys! Cheers, Armando

2
Empty functions do the job, compiler inlines them, then they get removedpaulm

2 Answers

10
votes

The standard idiom for that, at least in the 90s, was:

#ifndef NLOG
void logger_init(logger_t *logger);
void logger_log(logger_t *logger, ...);
#else
#define logger_init (void)sizeof
#define logger_log  (void)sizeof
#endif

Remember that sizeof operands are not evaluated, although they are syntax checked. This trick works also with variadic functions, because the sizeof operator will see an expresion with several comma operators:

logger_log(log, 1, 2, 3);

Converts to:

(void)sizeof(log, 1, 2, 3);

Those commas are not separating parameters (sizeof is not a function but an operator), but they are comma operators.

Note that I changed the return value from int to void. No real need for that, but the sizeof return would be mostly meaningless.

0
votes

Can't your disabled version simply be a constant:

#ifndef NLOG /* NLOG not defined -> enable logging */
int logger_init(logger_t *logger) {
...
}
#else  /* NLOG defined --> the logging system must be disabled */
#define logger_init(logger)     0
#endif /* NLOG */

This way, you just have (after pre-compilation): result = 0; which shouldn't produce any warnings.