Fog Creek Software
g
Discussion Board




Conditional Compilation

Hi all

It's very common to want to conditionally compile code; one simple example is including logging for debugging purposes.

In C and C++, I have usually seen this achieved via the preprocessor mechanism. For example, in C:

#define DEBUG

/* ... some code */
#ifdef DEBUG
printf("Got here!")
#endif

(I know that one can define a GOT_HERE macro and the like, but I'll ignore this herein.)

However, macro use tempts one to move ever more functionality onto the preprocessor, making the code hard to debug (as debuggers don't generally display macro expansions).

In Java, there is no (standard) preprocessor, and the use of constant global variables to test upon is encouraged, for example (C-like pseudocode):

const debug = true; // global scope

// ... some code

if(debug) print("Got here!");

The advantage of this approach is that the program is non-modal: when reading the code you don't have to mentally run two different programs (in the case of a single DEBUG symbol), or 4 or 8 or 16... You just treat the 'debug' variable like any other variable in your code. Your code also looks neater (IMHO), as you don't have ugly MACRO symbols all OVER the PLACE.

However, my C compiler (GCC 3.x) *does not* realise that when a global constant 'debug' is initialised as false, the conditional statements should be excluded in the executable (as revealed by reading the assembly output). A cheat is to define a DEBUG macro and use it as follows:

#define DEBUG (1==1)

/* ... some code */

if(DEBUG) printf("Got here!");

In this case the compiler *does* realise that when 'DEBUG' is false, it should exclude the conditional code.

Does anyone have any thoughts on this issue. How do you handle conditional compilation in your projects or your language? Are other compilers smart enough to figure out when to omit code that can be proven to be obsolete at compile-time?

C Rose
Saturday, January 10, 2004

"Are other compilers smart enough to figure out when to omit code that can be proven to be obsolete at compile-time?"

I should be noted that it's not possible to prove that the code is obsolete in C.  You can change that variable via a pointer or if global from a different compilation unit linked in after the compilation stage.  So, what C is doing there is correct for that language.

I find that most languages have different standard practices for the same purposes.  Using global variables is Java's way and using preprocessor instructions is how it's done in C.  I recommend that you do it the C way!

Almost Anonymous
Saturday, January 10, 2004

One really neat debug macro I read recently (though I can't remember where) was the following:

// btw, _DEBUG is the MSVC standard
// "debug mode" directvie... don't know
// if that's how it is with GCC

#ifdef _DEBUG
#define DEBUG_CODE(x) { x }
#else
#define DEBUG_CODE(x)
#endif

then to use it you'd just go...

DEBUG_CODE(
  printf( "Got here!\n" );
)

you can have variable declarations and everything in there.  Dunno if that helps, but its a nice trick. ^_^

Jonathan MacAlpine
Sunday, January 11, 2004

In our code all our debug based logging is non-conditional at the point where you use it. That is, the calls to write debug data to the log are always there.

We can configure our logging object to throw them away.

The downside to this is that the code is always there and you're making a function call to the log object even if you've got debugging turned off. For us the cost of instantiating the logging object is minimal anyway; debug info is just one of about 6 types of entry that gets written into our logfile.

The upside is that you can turn the debug code on any time, any place. This has saved us on several occasions: customers who report problems that we can't reproduce. Ask them to fire up their configuration utility, tick the "Record debug information" checkbox, and run again. Email the logfile to us (via an alias that FogBUGZ picks up) and you've got something to work with.

The minimal cost of the often wasted function call is trivial on a modern machine.

Andrew Lighten
Sunday, January 11, 2004

.NET provides a nifty "Conditional(symbol)" attribute that can be applied to method definitions. When you write a call to a method with this attribute, the compiler checks the value of the symbol mentioned in the attribute (typically DEBUG or TRACE). If it's false, the entire call is dropped from the binary; otherwise it's included as usual. Good combination of preprocessor efficiency and uncluttered code.

Chris Nahr
Sunday, January 11, 2004

Are you using -O to tell gcc to optimize? If not, gcc compiles what you give it, with no attempts to throw away unused code.

I just did a small test:

#include <stdio.h>
const int debug = 0;
int main()
{
    if (debug)
        printf("starting...\n");
    printf("Hello, world!\n");
    if (debug)
        printf("finished\n");
    return 0;
}

Compiled with 'gcc -S debug.c', there are indeed three calls to printf in the output file. Compiled with 'gcc -O -S debug.c' however, the first and the last call have disappeared (and the remaining one is converted to a call to puts, apparently).

Roel Schroeven
Sunday, January 11, 2004

-O is very basic optimization of course. Feel free to use -O2, -O3, -Os or whatever suits your needs.

If it still doesn't work, check your compiler flags: I'm surprised to find out that the unused code isn't stripped if I use -pedantic.

Roel Schroeven
Sunday, January 11, 2004

Roel,

OK, I'm close to solving the problem:

I'm writing my dummy code on OS X using Apple's XCode (used to be called ProjectBuilder). Setting GCC to use -O, -O2 and -O3 doesn't optimise out the debugging stuff.

However, doing a 'gcc -O -S test.c' at the shell *does*.

XCode has some funky "ZeroLink" technology that lets you write code, build it and run it in debug mode; then you can edit the code, XCode will compile in the background (without a linking step being required), and your changes take place in the already running app. (Apple identified the link step as being a bottleneck in the development process and decided to get rid of it as far as they could.)

I'm guessing that this ZeroLink stuff is messing with the compilation.

Thanks for all your comments.

C Rose
Sunday, January 11, 2004

*  Recent Topics

*  Fog Creek Home