We have inherited a very convolved project (500kloc) with a lot of preprocessor conditional logic, most of which is no longer relevant, and I want to clean it up.
Can I use the preprocessor¹ to expand only some of the conditional logic, and leave all other preprocessor macros, defines, and includes alone in the output?
¹ Here, by "the preprocessor", I really mean "any tool", either a standard C preprocessor, something I can install, or even a hacked-together Perl or Python script.
For instance, suppose we have this set of code:
#include <foo>
#define baz
#define bar(a) do{(a)+1} \
while(0)
#ifdef X
#if Y > 20
#if Z > 5
so_far_so_good = true;
#endif
#ifdef baz
something();
#endif
#else
otherthing();
#endif
#else
#if Z > 10
wow().this.is.bad;
#endif
#endif
The tool I want (and might need to write if it doesn't exist) would be a version of CPP that accepts not only the list of definitions for a particular invocation, but also a list of defines to respect during expansion. Any preprocessor symbol not in the second list is completely left alone. An example is in order:
cpptreadlight -DY=22 --only=Y
would produce:
#include <foo>
#define baz
#define bar(a) do{(a)+1} \
while(0)
#ifdef X
#if Z > 5
so_far_so_good = true;
#endif
#ifdef baz
something();
#endif
#else
#if Z > 10
wow().this.is.bad;
#endif
#endif
and:
cpptreadlight -DY=22 -DZ=8 -DX --only=Y,baz,Z
would give me:
#include <foo>
#define bar(a) do{(a)+1} \
while(0)
#ifdef X
so_far_so_good = true;
something();
#else
#endif
Notice that even though X
is defined, it was left behind, because it didn't appear in the --only
list. Also note that baz
was in the --only
list, and so was expanded once it was defined in the source.
I tried a hack-solution: escaping uninteresting stuff using a pipeline like the following (my own gsub tool is used, but it does what you might expect):
function escape_tr() {
gsub "#(define|include)" '@@@\1' < $1 |
(echo '#include "simple.h"' && gsub '\\$' "%%%") |
cpp -C -P -DY=301 -DZ > $1.new
}
Now I can filter out a lot of stuff, and then put the things I want the preprocessor to expand in simple.h
. Comments are left alone, and #line
directives are left out.
This almost does the trick, in that includes are not pulled in, #define
blocks are not defined, and so not expanded into the body. But of course it doesn't let me specify the set of conditional logic that I'd like to keep in the output. That's bad. Some of it is important to keep conditional.
#if
nests, and the #else
and #endif
tokens don't lexically match, putting the problem beyond a regex pipeline. I need a full-blown parser, something practically identical to cpp itself, but with finer grained control of what is expanded.
Hence, before digging into a preprocessor to implement this, I thought I'd ask if anyone has solved this problem before. I can't be the only one to have inherited a preprocessor spaghetti nest full of dead branches.
There is a tool called "unifdef" that will do what you want.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With