воскресенье, 11 декабря 2011 г.

Simple logger/debug messages with levels in C (2)

Another version of this task:
#ifndef _DBG_H
#define _DBG_H
#include <stdio.h>
#define PRN(...) printf(__VA_ARGS__)
#define ALIAS_OF_LEVEL(L) (1<<(L))

#ifndef LEVEL
#define LEVEL 0
#endif

#define PRN_L(L, ...) if ((LEVEL) & (L)) PRN(__VA_ARGS__)

#endif

And usage:

#include "dbg.h"

// define levels names (as aliases) and it's number
#define HARDWARE_LEVEL ALIAS_OF_LEVEL(1)
#define WRAPPER_LEVEL ALIAS_OF_LEVEL(2)
#define APP_LEVEL ALIAS_OF_LEVEL(3)

#define LEVEL (HARDWARE_LEVEL|APP_LEVEL)

void main() {
        PRN_L(HARDWARE_LEVEL, "There %d\n", 1);
        PRN_L(WRAPPER_LEVEL, "There %d\n", 2);
        PRN_L(APP_LEVEL, "There %d\n", 3);
}

The difference between first version is that now dbg.h does not limit our levels number. So programmer creates needed levels himself: in the example he created HARDWARE_LEVEL, WRAPPER_LEVEL and APP_LEVEL.

And like in the first version there is not any extra-code generated if PRN_L() is called for disabled level. Look at .s file, generated by gcc for this example:

 .file "dbg.c"
 .def ___main; .scl 2; .type 32; .endef
 .section .rdata,"dr"
LC0:
 .ascii "There %d\12\0"
 .text
.globl _main
 .def _main; .scl 2; .type 32; .endef
_main:
 pushl %ebp
 movl %esp, %ebp
 andl $-16, %esp
 subl $16, %esp
 call ___main
 movl $1, 4(%esp)
 movl $LC0, (%esp)
 call _printf
 movl $3, 4(%esp)
 movl $LC0, (%esp)
 call _printf
 leave
 ret
 .def _printf; .scl 2; .type 32; .endef
And we're seeing only 2 _printf calls - for HARDWARE_LEVEL and APP_LEVEL!

пятница, 9 декабря 2011 г.

Simple logger/debug messages with levels in C

This snippet shows way to turning-on/-off different levels of log/debug messaging in C program:

For example, lets create log messages support with 3 levels and possibility to select what levels are enabled:
#define PRN(...) printf(__VA_ARGS__)
#define L1 1
#define L2 2
#define L3 4

#define PRN_L(L, ...) PRN_##L(__VA_ARGS__)

#if (LEVEL) & (L3)
#  define PRN_L3(...) PRN(__VA_ARGS__)
#else
#  define PRN_L3(...)
#endif

#if (LEVEL) & (L2)
#  define PRN_L2(...) PRN(__VA_ARGS__)
#else
#  define PRN_L2(...)
#endif

#if (LEVEL) & (L1)
#  define PRN_L1(...) PRN(__VA_ARGS__)
#else
#  define PRN_L1(...)
#endif
And somewhere in our program use it:
// enable only messages of levels 1 and 3 (equals to 1|4==5)
#define LEVEL (L1|L3)
#include "logger.h"

void main() {
    // use general macro or PRN_L1(), PRN_L2(), PRN_L3()
    PRN_L(L1, "There %d\n", 1);
    PRN_L(L2, "There %d\n", 2); // no effect!
    PRN_L(L3, "There %d\n", 3);
}
What else? With LEVEL definition you can control enabled levels: 0 - disable all levels or use OR-ed combination of L1..3