This may be a matter of style, but there\'s a bit of a divide in our dev team and I wondered if anyone else had any ideas on the matter...
Basically, we have some de
For the purposes of performing conditional compilation, #if and #ifdef are almost the same, but not quite. If your conditional compilation depends on two symbols then #ifdef will not work as well. For example, suppose you have two conditional compilation symbols, PRO_VERSION and TRIAL_VERSION, you might have something like this:
#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif
Using #ifdef the above becomes much more complicated, especially getting the #else part to work.
I work on code that uses conditional compilation extensively and we have a mixture of #if & #ifdef. We tend to use #ifdef/#ifndef for the simple case and #if whenever two or more symbols are being evaluation.
That is not a matter of style at all. Also the question is unfortunately wrong. You cannot compare these preprocessor directives in the sense of better or safer.
#ifdef macro
means "if macro is defined" or "if macro exists". The value of macro does not matter here. It can be whatever.
#if macro
if always compare to a value. In the above example it is the standard implicit comparison:
#if macro !=0
example for the usage of #if
#if CFLAG_EDITION == 0
return EDITION_FREE;
#elif CFLAG_EDITION == 1
return EDITION_BASIC;
#else
return EDITION_PRO;
#endif
you now can either put the definition of CFLAG_EDITION either in your code
#define CFLAG_EDITION 1
or you can set the macro as compiler flag. Also see here.
Using #if means that you created a "define" macro, i.e., something that will be searched in the code to be replaced by "(0)". This is the "macro hell" I hate to see in C++, because it pollutes the code with potential code modifications.
For example:
#define MY_MACRO (0)
int doSomething(int p_iValue)
{
return p_iValue + 1 ;
}
int main(int argc, char **argv)
{
int MY_MACRO = 25 ;
doSomething(MY_MACRO) ;
return 0;
}
gives the following error on g++:
main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|
Only one error.
Which means that your macro successfully interacted with your C++ code: The call to the function was successful. In this simple case, it is amusing. But my own experience with macros playing silently with my code is not full of joy and fullfilment, so...
Using #ifdef means you "define" something. Not that you give it a value. It is still polluting, but at least, it will be "replaced by nothing", and not seen by C++ code as lagitimate code statement. The same code above, with a simple define, it:
#define MY_MACRO
int doSomething(int p_iValue)
{
return p_iValue + 1 ;
}
int main(int argc, char **argv)
{
int MY_MACRO = 25 ;
doSomething(MY_MACRO) ;
return 0;
}
Gives the following warnings:
main.cpp||In function ‘int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function ‘int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|
So...
I'd rather live without macros in my code, but for multiple reasons (defining header guards, or debug macros), I can't.
But at least, I like to make them the least interactive possible with my legitimate C++ code. Which means using #define without value, using #ifdef and #ifndef (or even #if defined as suggested by Jim Buck), and most of all, giving them names so long and so alien no one in his/her right mind will use it "by chance", and that in no way it will affect legitimate C++ code.
Now, as I'm re-reading my post, I wonder if I should not try to find some value that won't ever ever be correct C++ to add to my define. Something like
#define MY_MACRO @@@@@@@@@@@@@@@@@@
that could be used with #ifdef and #ifndef, but not let code compile if used inside a function... I tried this successfully on g++, and it gave the error:
main.cpp|410|error: stray ‘@’ in program|
Interesting. :-)
#ifdef
just checks if a token is defined, given
#define FOO 0
then
#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"
A little OT, but turning on/off logging with the preprocessor is definitely sub-optimal in C++. There are nice logging tools like Apache's log4cxx which are open-source and don't restrict how you distribute your application. They also allow you to change logging levels without recompilation, have very low overhead if you turn logging off, and give you the chance to turn logging off completely in production.
We have had this same problem across multiple files and there is always the problem with people forgetting to include a "features flag" file (With a codebase of > 41,000 files it is easy to do).
If you had feature.h:
#ifndef FEATURE_H
#define FEATURE_H
// turn on cool new feature
#define COOL_FEATURE 1
#endif // FEATURE_H
But then You forgot to include the header file in file.cpp:
#if COOL_FEATURE
// definitely awesome stuff here...
#endif
Then you have a problem, the compiler interprets COOL_FEATURE being undefined as a "false" in this case and fails to include the code. Yes gcc does support a flag that causes a error for undefined macros... but most 3rd party code either defines or does not define features so this would not be that portable.
We have adopted a portable way of correcting for this case as well as testing for a feature's state: function macros.
if you changed the above feature.h to:
#ifndef FEATURE_H
#define FEATURE_H
// turn on cool new feature
#define COOL_FEATURE() 1
#endif // FEATURE_H
But then you again forgot to include the header file in file.cpp:
#if COOL_FEATURE()
// definitely awseome stuff here...
#endif
The preprocessor would have errored out because of the use of an undefined function macro.