views:

708

answers:

5

Original Question

What I'd like is not a standard C pre-processor, but a variation on it which would accept from somewhere - probably the command line via -DNAME1 and -UNAME2 options - a specification of which macros are defined, and would then eliminate dead code.

It may be easier to understand what I'm after with some examples:

#ifdef NAME1
#define ALBUQUERQUE "ambidextrous"
#else
#define PHANTASMAGORIA "ghostly"
#endif

If the command were run with '-DNAME1', the output would be:

#define ALBUQUERQUE "ambidextrous"

If the command were run with '-UNAME1', the output would be:

#define PHANTASMAGORIA "ghostly"

If the command were run with neither option, the output would be the same as the input.

This is a simple case - I'd be hoping that the code could handle more complex cases too.

To illustrate with a real-world but still simple example:

#ifdef USE_VOID
#ifdef PLATFORM1
#define VOID void
#else
#undef VOID
typedef void    VOID;
#endif /* PLATFORM1 */
typedef void *  VOIDPTR;
#else
typedef mint     VOID;
typedef char *  VOIDPTR;
#endif /* USE_VOID */

I'd like to run the command with -DUSE_VOID -UPLATFORM1 and get the output:

#undef VOID
typedef void    VOID;
typedef void *  VOIDPTR;

Another example:

#ifndef DOUBLEPAD
#if (defined NT) || (defined OLDUNIX)
#define DOUBLEPAD 8
#else
#define DOUBLEPAD 0
#endif /* NT */
#endif /* !DOUBLEPAD */

Ideally, I'd like to run with -UOLDUNIX and get the output:

#ifndef DOUBLEPAD
#if (defined NT)
#define DOUBLEPAD 8
#else
#define DOUBLEPAD 0
#endif /* NT */
#endif /* !DOUBLEPAD */

This may be pushing my luck!

Motivation: large, ancient code base with lots of conditional code. Many of the conditions no longer apply - the OLDUNIX platform, for example, is no longer made and no longer supported, so there is no need to have references to it in the code. Other conditions are always true. For example, features are added with conditional compilation so that a single version of the code can be used for both older versions of the software where the feature is not available and newer versions where it is available (more or less). Eventually, the old versions without the feature are no longer supported - everything uses the feature - so the condition on whether the feature is present or not should be removed, and the 'when feature is absent' code should be removed too. I'd like to have a tool to do the job automatically because it will be faster and more reliable than doing it manually (which is rather critical when the code base includes 21,500 source files).

(A really clever version of the tool might read #include'd files to determine whether the control macros - those specified by -D or -U on the command line - are defined in those files. I'm not sure whether that's truly helpful except as a backup diagnostic. Whatever else it does, though, the pseudo-pre-processor must not expand macros or include files verbatim. The output must be source similar to, but usually simpler than, the input code.)

Status Report (one year later)

After a year of use, I am very happy with 'sunifdef' recommended by the selected answer. It hasn't made a mistake yet, and I don't expect it to. The only quibble I have with it is stylistic. Given an input such as:

#if (defined(A) && defined(B)) || defined(C) || (defined(D) && defined(E))

and run with '-UC' (C is never defined), the output is:

#if defined(A) && defined(B) || defined(D) && defined(E)

This is technically correct because '&&' binds tighter than '||', but it is an open invitation to confusion. I would much prefer it to include parentheses around the sets of '&&' conditions, as in the original:

#if (defined(A) && defined(B)) || (defined(D) && defined(E))

However, given the obscurity of some of the code I have to work with, for that to be the biggest nit-pick is a strong compliment; it is valuable tool to me.


The New Kid on the Block

Having checked the URL for inclusion in the information above, I see that (as predicted) there is an new program called Coan that is the successor to 'sunifdef'. It is available on SourceForge and has been since January 2010. I'll be checking it out...further reports later this year, or maybe next year, or sometime, or never.

A: 

Is the answer to http://stackoverflow.com/questions/461093/viewing-compiler-expanded-code-c viable for you?

ChrisW
Thanks for the suggestion - no, it is not what I need. I know how to look at what the compiler sees (I spent a few hours the other day looking at 37,000 lines of expanded output to work out why something was going wrong). I want the #include and other lines to remain, and macros unexpanded.
Jonathan Leffler
+6  A: 

I know absolutely nothing about C, but it sounds like you are looking for something like unifdef. Note that it hasn't been updated since 2000, but there is a successor called "Son of unifdef" (sunifdef).

Jörg W Mittag
Downloaded both. Had some hiccups porting sunifdef to Solaris 10; I've relayed that information back to the developer. Looking promising now I've worked around the issues. Thanks.
Jonathan Leffler
sunifdef is working quite nicely - and revealing hitherto unrealized depths of depravity in the code base. Oh well; it will end up cleaner. Thanks for the tip-off, Jörg.
Jonathan Leffler
You're welcome. I'm a sucker for clean, beautiful, expressive code – I'm glad I could help you make the world a better place :-)
Jörg W Mittag
+2  A: 

Around 2004 I wrote a tool that did exactly what you are looking for. I never got around to distributing the tool, but the code can be found here:

http://casey.dnsalias.org/exifdef-0.2.zip (that's a dsl link)

It's about 1.7k lines and implements enough of the C grammar to parse preprocessor statements, comments, and strings using bison and flex.

johnny
+2  A: 

I used unifdef years ago for just the sort of problem you describe, and it worked fine. Even if it hasn't been updated since 2000, the syntax of preprocessor ifdefs hasn't changed materially since then, so I expect it will still do what you want. I suppose there might be some compile problems, although the packages appear recent.

I've never used sunifdef, so I can't comment on it directly.

Dale Hagglund
+1  A: 

If you need something similar to a preprocessor, the flexible solution is Wave (from boost). It's a library designed to build C-preprocessor-like tools (including such things as C++03 and C++0x preprocessors). As it's a library, you can hook into its input and output code.

MSalters
Thank you. That looks interesting, but requires packaging to do the job whereas sunifdef is already packaged and is doing the job quite nicely. I've added the URL - thanks for the info.
Jonathan Leffler