From: Dave Gillespie <daveg@thymus.synaptics.com>
Subject: Re: Bad things about ifdef's (was: Syntax of Comments)
Reply-To: Dave Gillespie <daveg@thymus.synaptics.com>
Organization: Compilers Central
Date: Fri, 25 Sep 1992 21:42:37 GMT
Keywords: Pascal, C, design, syntax

Stavros Macrakis writes:
> Why ifdef's are a bad idea (reason #132):
> How do you check your code for type-correctness and syntax-correctness in
> the presence of many #ifdef's?  Do you compile every possible combination
> at least once?

This problem is especially acute when doing language translation instead
of compilation.  (By translation I mean converting a program in one
language to a program in another, where the new program is expected to be
maintained in its new language.)

Just yesterday I got a message from a user of my Pascal to C translator,
p2c, who was trying to translate a program full of conditional compilation
directives.  P2c currently evaluates these at translation-time, so that a
program would have to be re-translated specially for each target platform,
configuration option, or whatever.  A program that once was concise and
easily maintainable has turned into an unmanageable crowd of
similar-but-different versions.

P2c parses whole Pascal procedures at a time, transforms the parse trees
in various ways to make them more "C-like" in nature, then writes the
trees out in C form.  This means everything the parser sees has to be
expressible in some reasonable kind of parse tree or symbol table.
Conditional compilation directives, because they work at the token level
rather than at the syntactic level, don't fit well into parse trees.

Here's one of my favorite examples, using C's notation for conditional
compilation:

    var
    #ifdef foo
      x : integer;
    #else
      x : real;
    #endif
    begin
      writeln(x)
    end.

One can imagine making a special conditional entry in the symbol table
that could express the idea that "x" is an integer under some conditions
but a real under others.  And one can imagine writing two different
declarations inside the analogous #ifdef based on this symbol table entry.
But note that the "writeln(x)" statement must also be conditionalized:

    #ifdef foo
    printf("%d\n", x);
    #else
    printf("%f\n", x);
    #endif

Now consider five different variables all declared this way, with the
statement "writeln(a,b,c,d,e)".  Following this algorithm the translator
would need to emit a 32-way decision tree!

Here's another evil example:

    #ifdef foo
    if x < 0 then
      writeln('(', -x, ')')
    else
    #endif
      writeln(x);

A translator that tried to record this in a parse tree would either have
to use such a general tree notation that transformations on the tree would
be impractical, or somehow factor the conditionals outward to reach whole
statements, e.g.,

    #ifdef foo
    if x < 0 then writeln( ... ) else writeln(x);
    #else
    writeln(x);
    #endif

Duplicating code is a bad idea for a translator, because it makes the
resulting program much harder to maintain---especially if the copies can
show up arbitrarily far apart.

Besides, it's not clear that any such technique would work for *all* the
crazy things that can be done with conditional compilation.  I'm just
thanking my lucky stars that Pascal compilers as a rule don't provide
macros!

Lest you think these are contrived examples, by the way, I'm sorry to say
they are both patterned after things I have seen, and even written, in
real code.

P2c does offer one partial way out: It is possible to make a special sort
of Pascal comment which the translator passes through directly to the C
code without /* */ delimiters, allowing arbitrary C code to be embedded by
the user.  So one can run one's Pascal code through a sed script that
changes "#ifdef foo" to "{EMBED #ifdef foo}" and so on; the result will be
#ifdef's in the "analogous" places in the C code.  P2c has a number of
heuristics for putting the comments back into the output in places that
closely match their locations in the input.  For the simple case of an
#ifdef surrounding a whole statement, declaration, or procedure, things
usually work fine.  Still, it's not a very satisfying solution.

On the other hand, p2c compiles on a wide variety of machines and produces
code that works on an even wider variety.  And I'll bet you can guess
how...  ;-)

(P2c 1.20 is available by anonymous FTP on csvax.cs.caltech.edu, if you're
curious.)

								-- Dave
-- 
Send compilers articles to compilers@iecc.cambridge.ma.us or
{ima | spdcc | world}!iecc!compilers.  Meta-mail to compilers-request.
Xref: sparky comp.compilers:1679 comp.parallel:2253