Freigeben über


Not a big fan of #ifdef or #ifndef

I am not a big fan of the C/C++ preprocessor directives
#ifdef
or
#ifndef.
I am not denying that they certainly have their place and usage in the language.
I'll first write about where I think they are useful and then about the situations
where I feel they are not.
#ifndef is very useful for preventing multiple
inclusions of a header which will cause redefinition errors.

 
#ifndef __FOO_H__ // only proceed if the token is not yet defined
#define __FOO_H__ // define the token so that subsequent includes have it defined

[...contents of the header...]

#endif // __FOO_H__, end for #ifndef statement

Of course, you can use
#pragma once to do the same thing, but that is a
Microsoft specific extension to the language so not everyone will use it. This
usage is error prone though. I can't count the number of times that I copied one
header to create another header in the same project and forgot to change the
#ifndef token to a new token and couldn't
figure out why my new header wasn't being evaluated properly.

#ifdef is useful in a header to switch between
ANSI and wide versions of the function, such as this example from
winbase.h:

 
#ifdef UNICODE
#define CreateFile  CreateFileW
#else
#define CreateFile  CreateFileA
#endif // !UNICODE

Now, the reason I don't like these 2 directives is that they behave differently
then
#if token
or
#if !token.
#if token evaluates the value of
token.
If
token is not defined, it defaults to zero and the
#if token phrases evaluates to zero. On the other hand,
#ifdef token evaluates to token to see if it is
defined, regardless of the value of
token. That means that the following code

 
#define FOO 0x0

#ifdef FOO
printf("FOO!");
#else
printf("NOT FOO!");
#endif

will print out "FOO!" even though FOO evaluates to zero. As I have written previously
in my blog and in the newsgroups, I am big on maintainance of the code I write
because the code I write will have to be maintained for years and its connection
to me will eventually be lost by the developer who inherits it. What I don't like
about
#ifdef/ifndef
in a source file (vs the header file examples above) is that at first glance, I
mentally evaluate a
#ifdef
as a
#if
and more often then not, pick the wrong result when reviewing the code. Furthermore,
everytime I see
#ifndef
I need to spend a minute or two staring at the entire statement to figure out
what is going on, it is not immediately apparent to me what it is doing. Anytime
I have to stare at something to figure out what it does, it is an indicator to me
that others will probably do the same and it will eventually be a problem spot
in the code in the future.

A lot of you may not agree with me and I am fine with that ;). Please let me
know why though.

Comments

  • Anonymous
    June 01, 2006
    GCC "supports" #import instead of #include. #import is equiv to #pragma once. Personally I find it depressing that preprocessors can create recursive include loops.

  • Anonymous
    June 01, 2006
    The comment has been removed

  • Anonymous
    June 01, 2006
    Personally, I would like to see your second example stricken from the record and replaced with something like

    #ifdef __cplusplus
    inline HWND CreateWindow( ..... ) {
    #ifdef _UNICODE
       return CreateWindowW(.....);
    #else
       return CreateWindowA(.....);
    #endif
    }
    #else
    ... SAME ...
    #endif
    [Actually you would need FORCEINLINE HWND CreateWindow()...inline is not "strong" of a keyword in my experience.  This is not probably not going to happen though.  The man hours required to make this change is probably greater then the benefit from that work...and then there is fear of code not compiling using the new style.]
    The number of times I have been caught out by a DLL interface which really doesn't care whether you have defined _UNICODE or not not working because a class in a namespace has a member function called GetMessage (not an unreasonable name for a member function) and the library builder and client had different ideas about _UNICODE is annoying.

    Probably not the right place to vent this particular irritation, but you mentioned it :) [Fair enough, I understand your pain, I have been hit by it before.]

  • Anonymous
    June 01, 2006
    #if X is just not thesame as
    #ifdef X [that's what I said ;)]

    #ifdef is a shorthand for #if defined(X)

    I like to see the #if defined(X) iso #ifdef bu I am not against a
    defined test as opposed to a value check (you could see it as a NULL check)
    [True.  i would prefer !if defined(X) over #ifdef any day] on another note I dislike the #if X
    if there is no value check behind it Please please tell me what you are checking for, if you just want to know if the value exists use the #ifdef (or better #if defined() ) Otherwise tell me the value you are looking for
    [Well, this falls into style guidelines for if ()'s as a whole.  For instance, some people (myself included) don't tolerate if (pValue), but would rather see if (pValue != NULL)]

  • Anonymous
    June 01, 2006
    The comment has been removed

  • Anonymous
    June 02, 2006
    see my comments inline with the other comments...

    d

  • Anonymous
    July 14, 2006
    Yeah I have been bitten myself once by the #ifdef UNICODE trick, when trying to develop an CString derived class called CPathFileString that would implement all PathFileXXX functions as member functions. You bet I was not happy :-)

    Wouldn't it work out the same if all the neutral names were declared as const function pointers like this:

    extern __declspec(selectany) LPSTR (WINAPI * const lstrcpy)(LPSTR, LPCSTR) = lstrcpyA;

    The function pointer is const but will the compiler/linker eliminate all the function pointer 'variables' thus inducing no additional memory overhead to my program? If it does, then this solution I think would be cool.

    What annoys me most about the UNICODE ifdef is that you have to do two of them. UNICODE (for windows stuff) and _UNICODE (for CRT stuff). Why are there two defines?
    In fact some windows headers do the following 'trick'

    // syncronize UNICODE options
    #if defined(_UNICODE) && !defined(UNICODE)
           #define UNICODE
    #endif
    #if defined(UNICODE) && !defined(_UNICODE)
           #define _UNICODE
    #endif

    Anyway going back to the generic #ifdef issue, now with VS2005 you get all #ifdefs evaluated by the IDE and the code that gets ifdef'ed out is greyed. Totally cool!!! It even works pretty well for "ddkbuild.bat" solutions. You just have to set your defines in the sln property pages and you're on.

  • Anonymous
    July 14, 2006
    The VS feature is pretty neat.  I use visual slickedit as my editor and it does something similar.  

    The 2 different #defines are annoying, but they come from 2 separate organizations.  the CRT's #define might even be dictated by ISO (just a wild guess).

    The __declspec(selectany) trick is pretty cool.  __declspec(selectany) didn't exist when they created these pattern in the headers which is probably one reason it is not used.  As for the additional global constant, I don't know...but a quick test app should tell, no?

    d

  • Anonymous
    July 17, 2006
    A quick look at the assembly output showed that even under various optimization options a global variable is always generated, even if you don't use the function pointer at all in your program.
    So I guess that would be a technique to use in your precompiled header when you have a specific function name conflict (like GetMessage) and you insist on using the same name in your code.

  • Anonymous
    July 18, 2006
    That's interesting that the global is always generated even if it is unreferenced.  In my previous experience with trying to create some constant globals for "type info" in KMDF, I could not convince the linker to leave the global in the image even though there were no references to it.

  • Anonymous
    May 31, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=1954

  • Anonymous
    May 31, 2009
    PingBack from http://outdoorceilingfansite.info/story.php?id=19590