Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partially processing a file with the preprocessor [duplicate]

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.

like image 578
clord Avatar asked Nov 10 '10 18:11

clord


1 Answers

There is a tool called "unifdef" that will do what you want.

like image 122
Paul Tomblin Avatar answered Oct 05 '22 23:10

Paul Tomblin