212 C++ C weenies will tell you that one of the best features of C is the pre- processor. Actually, it is probably the worst. Many C programs are unintelligible rats’ nests of #ifdefs. (Almost none of which would be there if the various versions of Unix were actually compatible.) But that’s only the beginning. The worst problem with the C preprocessor is that it locks the Unix world into the text-file prison and throws away the key. It is virtually impossible to usefully store C source code in any form other than lin- ear text files. Why? Because it is all but impossible to parse unpre- processed C code. Consider, for instance: #ifdef BSD int foo() { #else void foo() { #endif /* ... */ } Here the function foo has two different beginnings, depending on whethe the macro ‘BSD’ has been defined or not. To parse stuff like this in its original form is all but impossible (to our knowledge, it’s never been done). Why is this so awful? Because it limits the amount of intelligence we can put into our programming environments. Most Unix pro- grammers aren’t used to having such environments and don’t know what they’re missing, but there are all kinds of extremely useful fea- tures that can easily be provided when automated analysis of source code is possible. Let’s look at an example. For most of the time that C has been around, the preprocessor has been the only way to get expressions open-coded (compiled by being inserted directly into the instruction stream, rather than as a function call). For very simple and com- monly used expressions, open-coding is an important efficiency tech- nique. For instance, min, which we were just talking about above, is commonly defined as a preprocessor macro: #define min(x,y) ((x) (y) ? (x) : (y)) Suppose you wanted to write a utility to print a list of all functions in some program that reference min. Sounds like a simple task, right?
C++ Is to C as Lung Cancer Is to Lung 213 But you can’t tell where function boundaries are without parsing the program, and you can’t parse the program without running it through the preprocessor, and once you have done that, all occurrences of min have been removed! So you’re stuck with running grep. There are other problems with using the preprocessor for open-cod- ing. In the min macro just displayed, for instance, you will notice a number of apparently redundant parentheses. In fact, these parenthe- ses must all be provided, or else when the min macro is expanded within another expression, the result may not parse as intended. (Actually, they aren’t all necessary -- which ones may be omitted, and why, is left as an exercise for the reader.) But the nastiest problem with this min macro is that although a call to it looks like a function call, it doesn’t behave like a function call. Consider: a = min(b++, c) By textual substitution, this will be expanded to: a = ((b++) (c) ? (b++) : (c)) So if ‘b’ is less than ‘c’, ‘b’ will get incremented twice rather than once, and the value returned will be the original value of ‘b’ plus one. If min were a function, on the other hand, ‘b’ would get incremented only once, and the returned value would be the original value of ‘b’. C++ Is to C as Lung Cancer Is to Lung “If C gives you enough rope to hang yourself, then C++ gives you enough rope to bind and gag your neighborhood, rig the sails on a small ship, and still have enough rope to hang yourself from the yardarm” —Anonymous