Is #ifdef still preferred?

Started by
8 comments, last by ToohrVyk 16 years, 7 months ago
Reading "Effective C++" by Scott Meyers, he talks about the disadvantages of #define and how you should almost always use const or inline instead, relying on the compiler to optimize your code for the same result. That got me wondering, what about things like #ifdef? For example, will the compiler optimize the following two examples into the same thing? If so, I wonder if there are any advantages of one over the other. The second seems more friendly to me, less hackish than the first.
#define IS_SERVER 1

// ...
#ifdef IS_SERVER
    // ... server code ...
#endif
// ...
const int isServer = 1;

// ...
if ( isServer ) {
    // .. server code ...
}
// ...
Advertisement
Quote:Original post by Nairou
For example, will the compiler optimize the following two examples into the same thing?

For most modern compilers, yes. To make sure of it, though, it's a good idea to force internal linkage of constants used in this way (by declaring the variable as static, or putting it in an anonymous namespace). The compiler is much less likely to be able to do this with constants defined in a different translation unit.

However, the code still needs to be syntactically and semantically correct. So using const variables may not be the right approach if you're implementing different functionality for each branch, as you seem to be doing here.
The idea of using #define for constants is bad. But using pre-processor things for determining platform and such is what you must do if you want easily configured code.

to explain further, this is often considered "bad":
#define PI 3.14156


it is now generally preferred to be
const FLOAT PI = 3.14159f;



however, your example of using const for isServer is horrible. That's the kind of thing you want to control in your compiler setttings (i.e. you set up a configuration for client, one for server, one for linux, etc). With the const example you have to actually change code every time you want to compile for a different platform.

-me
Meyers is talking about #define for constants, not for preprocessor control over code generation, which is what your example is. The first method is the correct way to handle the situation.
Quote:Original post by Nairou
That got me wondering, what about things like #ifdef? For example, will the compiler optimize the following two examples into the same thing? If so, I wonder if there are any advantages of one over the other. The second seems more friendly to me, less hackish than the first.

Most popular compilers will elide the unreachable code, making the two implementations act as if they do the same thing.

There are fundamental differences, however, and most of the advantages go to the preprocessor construct in this case.

(1) A preprocessor value can be set extralingually (usually through a command-line option to the compiler, such as -DIS_SERVER=1). Thus, you can autodetect and set various compile-time options, something you can't do using C++ constants.

(2) Code within an #ifdef block is never even seen by the compiler. That means if you have, say, functions calls that do not exist on some platforms then you can still compile your code. For example,
  #ifdef _WIN32    int istat = WSAStartup( ... );  #endif

You can't do that at all using C++ constants. You would have to revert to some kind of wacky template specialization.

Quote:Original post by SneftelFor most modern compilers, yes. To make sure of it, though, it's a good idea to force internal linkage of constants used in this way (by declaring the variable as static, or putting it in an anonymous namespace).

Simply putting it in an anonymous namespace forces external linkage. If you want internal linkage you will have to make it static, regardless of the namespace in which you put it (don't forget "global" is just a namespace like any other).

--smw

Stephen M. Webb
Professional Free Software Developer

Quote:
To make sure of it, though, it's a good idea to force internal linkage of constants used in this way (by declaring the variable as static, or putting it in an anonymous namespace).

Quote:
Simply putting it in an anonymous namespace forces external linkage.


Ok. 2 things I thought I knew about C++ questioned in this thread.

1) I thought const variables had internal linkage by default (I thought this is why you can place them in headers).
2) I also thought anything in an anonymous namespace had internal linkage. (how does one refer to something in an anonymous namespace in a different source file anyway?)

I could easily be wrong, but can anyone clarify?
It looks like I was partially wrong about anonymous namespaces. Names declared in an anonymous namespace are only visible to the translation unit, but have external linkage. Of course, for this particular purpose name visibility is the important thing, so it'll work fine--as will just declaring it const, since that defaults to internal linkage.
To state all the above great things as a general guidline:

In the old days of C / C++, people used the preprocessor to do anything it could do, often doing things with the preprocessor that could be done in code simply to improve compiler performance, or make the generated output more efficient (when compilers weren't as advanced).

Now, the preference is:

Use the preprocessor for anything to do with controlling compilation.

Use the compiler and language constructs for anything to do with controlling program logic and data.

Simple, easy to remember, and correct almost every time.
If you have certain code for server and other code for client - maybe you can put the code in different files and use the project settings (ie. makefile) to build the right files.
In my project I have "common" files, server files and client files, and several makefiles (one for server and one for client).

But of course, it works if the code is clearly separated and doesn't solve the entire need for ifdef style compilation control.
As a side note, Visual C++ 2005 will "grey out" code within #ifdef X #endif whenever X is not defined. It obviously doesn't do this when using if(x).

Also, to expand on what Bregma said, not only do you have problems with platform-specific functions, you'll also have trouble with including platform-specific headers.

This topic is closed to new replies.

Advertisement