[...]
I've personally used conditional compilation in Boost, for instance,
either because I wasn't aware of better alternatives at that time and
because, in any case, that is the Boost "way". However it just
requires a minimal infrastructure to elevate everything to much higher
engineering standards, as you can see from James Kanze code. I learned
this from him, as a lot of other things. "C++ Gotchas" by Steve
Dewhurst also has an item which briefly covers the problem:
The idea is not new. I got it from a paper published by Henry
Spencer and Geoff Collyer, way back in 1992, see
http://www.literateprogramming.com/ifdefs.pdf. And in fact, the
only #ifdef's in my code are normally header guards. The
results are much more readable than, say, Boost, but are far
from perfect, either. In the end, no matter how you cut it, you
have two (or more) versions of the same code; all have to be
understood, all have to be tested, and all have to be validated.
(The combinatorial is far easier to understand, however, which
at least makes it clearer what is or is not being validated.)
There are doubtlessly exceptions to the rule, as well. For
example, most of the code I write is application code, in a
production environment, where I can write to a lowest common
denominator of a small set of production compilers. Some new
feature isn't suported by one of the compilers, just don't use
it anywhere. Library purveyors may not always have this
liberty; if I think back to the days when many compilers still
did not implement member templates, for example, a third party
vendor of the standard library would certainly want to support
them where the targetted compiler did. No matter how much more
difficult it made maintenance for them.
There are different degrees of difficulty. A simple, binary
#ifdef at the top of the file, around, say, an include and a
#define, doesn't bother me too much. Where as #ifdef's in the
body of a function, or nested #ifdef's, are simply not
acceptable (although common) in professional code. When I see
such things, I generally throw the code out and start over,
because it is less work than trying to figure out what is
actually going on. (Regretfully, you can't always do that, and
on more than one occassion, I've had to run the code through the
preprocessor in order to figure out what definitions I was
getting from include files under /usr/include under Solaris.)