Sunday, June 19, 2011

Removing conditional macros when tracing C/C++ code with Vim

Working with source code buried in conditional macros(#if, #ifdef, #ifndef, ...) is sometimes really annoying, especially when we are trying to get the whole picture of what it does instead of every details in it.

Simply expanding the conditionals with gcc -E usually doesn't help in this situation. Expansion of macros as well as the header files even makes the picture muddier. The unifdef(1) utility come in handy for this.
$ man unifdef(1)

UNIFDEF(1) Programmer's Manual UNIFDEF(1) NAME unifdef, unifdefall — remove preprocessor conditionals from code SYNOPSIS unifdef [-bBcdeKknsStV] [-Ipath] [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [-o outfile] [infile] unifdefall [-Ipath] ... file
Apply it as vim filter command repeatedly and incrementally until you get your picture clear enough.
 vim /tmp/dummy.c

#if (LOG_LEVEL > 3) #define log printk #else #define log(...) #endif #ifdef ENABLE_FOO void foo_xxx() { ... } #endif #ifdef ENABLE_BAR void bar_yyy() { ... } #endif
:%! unifdef -k -UENABLE_FOO
 vim /tmp/dummy.c

#if (LOG_LEVEL > 3) #define log printk #else #define log(...) #endif #ifdef ENABLE_BAR void bar_yyy() { ... } #endif
:%! unifdef -k -DENABLE_BAR -DLOG_LEVEL=4
 vim /tmp/dummy.c

#define log printk void bar_yyy() { ... }

Note that:
By default, unifdef ignores #if and #elif lines with constant expressions; it can be told to process them by specifying the -k flag on the command line.