(转)C/C++ 宏详解

1. 宏可以像函数一样被定义,例如:
#define min(x,y) (x 但是在实际使用时,只有当写上min(),必须加括号,min才会被作为宏展开,否则不做任何处理。

2. 如果宏需要参数,你可以不传,编译器会给你警告(宏参数不够),但是这会导致错误。
如C++书籍中所描述的,编译器(预处理器)对宏的语法检查不够,所以更多的检查性工作得你自己来做。

3. 很多程序员不知道的#和##

#符号把一个符号直接转换为字符串,例如:#define STRING(x) #x
const char *str = STRING( test_string ); str的内容就是"test_string",
也就是说#会把其后的符号直接加上双引号。

##符号会连接两个符号,从而产生新的符号(词法层次),例如:
#define SIGN( x ) INT_##x :: int SIGN( 1 ); 宏被展开后将成为:int INT_1;

4. 变参宏,这个比较酷,它使得你可以定义类似的宏:
#define LOG( format, ... ) printf( format, __VA_ARGS__ )
LOG( "%s %d", str, count );
__VA_ARGS__是系统预定义宏,被自动替换为参数列表。

5. 当一个宏自己调用自己时,会发生什么?例如:
#define TEST( x ) ( x + TEST( x ) )
TEST( 1 ); 会发生什么?为了防止无限制递归展开,语法规定,当一个宏遇到自己时,就停止展开,
也就是说,当对TEST( 1 )进行展开时,展开过程中又发现了一个TEST,那么就将这个TEST当作一般的符号。
TEST(1) 最终被展开为: 1 + TEST( 1) 。

6. 宏参数的prescan,
当一个宏参数被放进宏体时,这个宏参数会首先被全部展开(例外情况是,如果PARAM宏里对宏参数使用了#或##,那么宏参数不会被展开)。
当展开后的宏参数被放进宏体时,预处理器对新展开的宏体进行第二次扫描,并继续展开。例如:

#define PARAM( x ) x
#define ADDPARAM( x ) INT_##x

PARAM( ADDPARAM( 1 ) );
因为ADDPARAM( 1 ) 是作为PARAM的宏参数,所以先将ADDPARAM( 1 )展开为INT_1,然后再将INT_1放进PARAM。

例外情况是,如果PARAM宏里对宏参数使用了#或##,那么宏参数不会被展开:

#define PARAM( x ) #x
#define ADDPARAM( x ) INT_##x

PARAM( ADDPARAM( 1 ) ); 将被展开为"ADDPARAM( 1 )"。

使用这么一个规则,可以创建一个很有趣的技术:打印出一个宏被展开后的样子,这样可以方便你分析代码:

#define TO_STRING( x ) TO_STRING1( x )
#define TO_STRING1( x ) #x

TO_STRING首先会将x全部展开( 如果x也是一个宏的话 ),然后再传给TO_STRING1转换为字符串,
现在你可以这样:
const char *str = TO_STRING( PARAM( ADDPARAM( 1 ) ) );
去一探PARAM展开后的样子。

7. 一个很重要的补充:就像我在第一点说的那样,如果一个像函数的宏在使用时没有出现括号,
那么预处理器只是将这个宏作为一般的符号处理( 那就是不处理 )。

 

解析C函数式宏

原文地址:http://hi.baidu.com/bellgrade/blog/item/391c1b2b8cd932325343c1b9.html

#define f(a,b) a##b  
#define g(a)   #a  
#define h(a)   g(a)  

h(f(1,2))  
g(f(1,2))

解析过程。应该是这样的:

对于g( f(1,2) ),预处理器看到的先是 g,然后是 (,说明这是一个函数式的宏,然后替换后面的实参f(1, 2),得到 #f(1,2)
(注:直接这么写非法,这里只是为了表示方便而已),因为它前面有个#,所以下一步是不会替换f的参数的!
所以进一步得到"f(1, 2)",解析结束。
对于h(f(1,2)),预处理器看到的先是 h,然后 (, 对其参数 f(1, 2) 进行替换,得到 g( f(1,2) ),
注意这里的下一步是,预处理器就继续往后走,处理刚得到的f(1,2), 得到12,到了这里我们的得到的是一个:
g(12),然后重新扫描整个宏,替换 g,最后得到 "12",解析结束。

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. 
A parameter in the replacement list, is replaced by the corresponding argument after all macros contained therein have been expanded.
unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below) : #param, ##param, param##

函数式宏的基本替换流程:

  1. 首先要识别出这是一个函数式宏,通过什么?通过调用中出现的 (,没错是左括号!
  2. 到这里后下一步是参数 parameter 替换,就是根据该宏的定义把实参 argument 全部替换进去, argument --> parameter
  3. 然后接着向后走,除非是遇到了 # 和 ##(正如上面例子中的g),把后面替换后的东西中如果还有已知宏的话,进行替换或者同样的展开,直到解析到末尾:
  4. 所有的参数都已经替换完(或者 # 或 ## 已经处理完);
  5. 最后,预处理器还会对整个宏再进行一次扫描,因为前一轮替换中有可能在前面替换出一些新的东西来(比如上面例子中的 h( f(1,2) ) --> g(12))。

这里咋看之下没什么问题,其实问题很多!为什么?因为宏替换不仅允许发生在“调用”宏的时候,而且还发生在它定义时!

问题1:宏的名字本身会被替换吗?也可以这样问:宏允许被重新定义吗?
不允许,但是允许相同的重新定义。标准这样写道:

An identifier currently defined as an object-like macro shall not be redefined by another #define preprocessing directive 
unless the second definition
is an object-like macro definition and the two replacement lists are identical.

Likewise, an identifier currently defined as a function-like macro shall not be redefined by another #define preprocessing directive
unless the second definition
is a function-like macro definition that has the same number and spelling of parameters,
and the two replacement lists are identical.

问题2:宏的参数(形参)会被替换吗?

先举个例子说明这个问题:

#define foo      1
#define bar(foo) foo + 2
bar(a)

我们是得到 a+2 不是 1+2 因为形参是不会被替换掉的,你想想啊,如果形参都被替换掉了这个宏就没什么作用了!
那实参呢?实参会的,因为实参的替换发生在传递这个参数之前:

Before being substituted, each argument’s preprocessing tokens are completely macro replaced as if they formed the rest of the preprocessing file

问题3:宏中参数之外的符号会被替换吗?

会被替换,上面提到过 “after all macros contained therein have been expanded”,也就是说这个发生在参数替换之前。
但是,这里有个非常诡异的问题:如果被替换出来的符号正好和形参一样怎么办?就像下面这个例子:

#define foo      bar
#define baz(bar) bar + foo
baz(1)

我们会得到 1+bar 不是 1+1,因为替换出来的那个bar是不会计算在形参之内的,虽然标准并没有明确这一点。想想吧,如果是的话那个宏的定义也会被破坏了! 另一个例子:

#define foo    bar
#define mac(x) x(foo)
mac(foo)

根据上面所说,我们首先得到 foo( foo ),然后 foo 再被替换成 bar,最后得到 bar( bar )。好了,到这里我们终于可以看一下更复杂的例子了

#define m      !( m ) + n
#define n( n ) n( m )
m( m )

第一步很好走第一个m直接被替换,得到 !(m)+ n( m     ),
别犹豫接着往下走替换最后一个m,得到 !(m)+ n( !(m)+n ),这时这一遍扫描已经完成。
在上次替换中,被替换的是 m,所以 m 在这里 的再次出现将不会被替换,!(m) + n( !(m) + n )
下一步是会替换第一个 n,得到:!(m) + !(m) + n --> !(m) + !(m) + n( m )
注意这里又替换出一个
新的 m 来,这个m会被替换,因为这次扫描没完成!
下一步得到:!(m) + !(m) + n( !(m)+n ),第二遍扫描结束,全部的替换完成。
综上,我们可以总结出两条重要的宏替换规则:
1) 再复杂的宏也只是被扫描两遍,而且递归是不允许发生的,即使在第2遍时;
2) 一个替换完成后如果还没扫描完,要从被替换的那里继续。
递归 - 标准对此的描述是:

If the name of the macro being replaced is found during this scan of the replacement list
( not including the rest of the source file’s preprocessing tokens), it is not replaced.

C preprocessor and concatenation

http://stackoverflow.com/questions/1489932/c-preprocessor-and-concatenation

I am trying to write a code, where name of functions are dependent on the value of a certain macro variable.
To be specific, I am trying to write a macro like this:

#define VARIABLE 3
#define NAME(fun) fun ## _ ## VARIABLE

int NAME(some_function)(int a);

 Unfortunately, the macro NAME() turns that into

int some_function_VARIABLE(int a);

 

 rather than

int some_function_3(int a);

so the this is clearly the wrong way to go about it.
Fortunately, the number of different possible values for VARIABLE is small so I can simply do an
#if VARIABLE == n and list all the cases separately, but I was wondering if there is a clever way to do it.

$ cat xx.c
#define VARIABLE 3
#define PASTER(x,y) x ## _ ## y
#define EVALUATOR(x,y)  PASTER(x,y)
#define NAME(fun) EVALUATOR(fun, VARIABLE)

extern void NAME(mine)(char *x);
$ gcc -E xx.c
# 1 "xx.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "xx.c"

extern void mine_3(char *x);
$

Cade Roux asked why this needs two levels of indirection.
The flippant answer is because that's how the standard requires it to work;
you tend to find you need the equivalent trick with the stringizing operator too.

Section 6.10.3 of the C99 standard covers 'macro replacement', and 6.10.3.1 covers 'argument substitution'.

After the arguments for the invocation of a function-like macro have been identified, argument substitution takes place. 
A parameter in the replacement list, is replaced by the corresponding argument after all macros contained therein have been expanded.
unless preceded by a # or ## preprocessing token or followed by a ## preprocessing token (see below) : #param, ##param, param## 

In the invocation NAME(mine), the argument is 'mine'; it is fully expanded to 'mine'; it is then substituted into the replacement string:

EVALUATOR(mine, VARIABLE) <-- #define NAME(fun) EVALUATOR(fun, VARIABLE)

Now the macro EVALUATOR is discovered, and the arguments are isolated as 'mine' and 'VARIABLE';
the latter is then fully expanded to '3', and substituted into the replacement string:

PASTER(mine,3) <-- #define EVALUATOR(x,y) PASTER(x,y)

The operation of this is covered by other rules (6.10.3.3 'The ## operator'):

If, in the replacement list of a function-like macro, a parameter is immediately preceded or followed by a ## preprocessing token,

##param, param## 
the parameter is replaced by the corresponding argument’s preprocessing token sequence; 
For both object-like and function-like macro invocations, before the replacement list is reexamined for more macro names to replace,

each instance of a ## preprocessing token in the replacement list (not from an argument) is deleted and
the preceding preprocessing token is concatenated with the following preprocessing token.

 

So, the replacement list contains x followed by ## and also ## followed by y; so we have:

mine ## _ ## 3 <-- PASTER(x,y) x ## _ ## y

and eliminating the ## tokens and concatenating the tokens on either side combines 'mine' with '_' and '3' to yield:

mine_3

This is the desired result.

If we look at the original question, the code was (adapted to use 'mine' instead of 'some_function'):

#define VARIABLE 3

#define NAME(fun) fun ## _ ## VARIABLE

NAME(mine)

The argument to NAME is clearly 'mine' and that is fully expanded.
Following the rules of 6.10.3.3, we find:

mine ## _ ## VARIABLE

which, when the ## operators are eliminated, maps to:

mine_VARIABLE

exactly as reported in the question.

#define VARIABLE          3
#define NAME2(fun,suffix) fun ## _ ## suffix
#define NAME1(fun,suffix) NAME2(fun,suffix)
#define NAME(fun)         NAME1(fun,VARIABLE)

int NAME(some_function) (int a);

 

http://www.iar.com/Global/Resources/Developers_Toolbox/C_Cplusplus_Programming/Tips%20and%20tricks%20using%20the%20preprocessor%20(part%20one).pdf

Tips and tricks using the preprocessor (part one) by Anders Lindgren, IAR Systems

The #include directive

The most straight-forward preprocessor directive is #include.
When the preprocessor finds this directive it simply opens the file specified and inserts the content of it,
as though the content of the file would have been written at the location of the directive. It can take two forms, for example:

#include <systemfile.h> 
#include "myfile.h" 

 

The first is used to include standard headers like stdio.h.
The latter is for your own application-specific headers.

Macros

One of most useful features of the preprocessor is to allow the user to define macros,
which simply is an identifier that is mapped to a piece of source code.
Whenever the preprocessor finds the macro in the application source code it replaces the macro with the definition.

Basically, there are two types of macros, object-like macros and function-like macros,
the difference is that function-like macros have parameters.

By convention, macro names are written using upper-case only.
The only exception is when a macro is used to replace something that should have been a function
but is implemented using a macro for the sake of efficiency.

 

The directive #define can be used to define a macro.

In the following example, we define NUMBER_OF_PLAYERS to the constant "2". This is an object-like macro.

#define NUMBER_OF_PLAYERS 2 
int current_score[NUMBER_OF_PLAYERS];

 

Here, the function-like macro PRINT_PLAYER is mapped a more complex piece of source code.

 

#define PRINT_PLAYER(no) printf("Player %d is named %s", no, names[no]) 

 

The definition of a macro should, technically, be specified on a single source line. Fortunately, the C
standard allows you to end a line with a backslash and continue on the next physical line. For example:

#define A_MACRO_THAT_DOES_SOMETHING_TEN_TIMES \ 
for (i = 0; i < 10; ++i)\ 
{ \ 
  do_something(); \ 
} 

 

 

If you ever would like to make a macro name undefined you can use the #undef directive.

Object-like macros

Object-like macros can be used to replace an identifier in the source code with some kind of replacement source code.
Typically, macros can be used to declare a constant that could be configured in one location. Also,
constants could be used to make the source code more readable, even if the value is not intended to change. For example:

#define SQUARE_ROOT_OF_TWO 1.4142135623730950488016887 
double convert_side_to_diagonal(double x) 
{ 
  return x * SQUARE_ROOT_OF_TWO; 
} 
double convert_diagonal_to_side(double x) 
{ 
  return x / SQUARE_ROOT_OF_TWO; 
} 

 

Preprocessor macros could be used for very weird things, since all that the preprocessor does is to
replace an identifier with an arbitrary piece of source code. For example, the following is legal code
(although, you will probably have to answer to your boss if you ever try to write something like this): 

#define BLA_BLA ); 
int test(int x) 
{ 
  printf("%d", x BLA_BLA 
}

 

Function-like macros

Function-like macros are macros that take parameters. When you use them they look like a function call.
A function-like macro could look like the following:

#define SEND(x) output_array[output_index++] = x

 

When the preprocessor finds a function-like macro in your application source code it will replace it with
the definition. The parameters of the macro will be inserted into the resulting source code at the location
of the formal parameter variables.  

So, if you write the following:  SEND(10) 
Then the compiler will see : output_array[output_index++] = 10

Conditional compilation

One of the most powerful features of the preprocessor is the so-called conditional compilation—this
means that portions of the code could be excluded in the actual compilation under the certain conditions.
This means that your source could contain special code for, say, the ARM processor.
Using conditional compilation, this code could be ignored when compiling for all other processors.

The preprocessor directives #ifdef, #ifndef, #if, #elif, and #else are used to control the source code.
The #ifdef (#ifndef) directive includes a section if a preprocessor symbol is defined (undefined).
For example:

#ifdef ARM_BUILD 
__ARM_do_something(); 
#else 
generic_do_something(); 
#endif

The #if directive can handle any type of integer and logical expression, for example:

#if (NUMBER_OF_PROCESSES > 1) && (LOCKING == TRUE) 
  lock_process(); 
#endif 

 

The #elif directive works like a combined #else and #if.
#if and #elif can use the special operator defined to check if a symbol is defined.
This is useful in combination with complex tests, for example:

 

#if defined(VERSION) && (VERSION > 2) 
... 
#endif 

 

Include guards

One typical use for conditional compilation is to ensure that the content of include files are only seen
once. This will not only speed up the compilation, but also ensures that the compiler will not issue an
error (e.g. for redeclaration of a struct) if the header file is included twice.
An include guard typically looks like the following:

#ifndef MYHEADER_H 
#define MYHEADER_H 
/* The content of the header file goes here. */ 
#endif

 

Clearly, the first time the header file is included the symbol MYHEADER_H is not defined and the content is included in the compilation.
The second time the header file is read the symbol is defined and the content is excluded.

 

Error directives

The #error directive can be used to generate a compiler error message. This is useful when performing
consistency checks, for example: 

#if USE_COLORS && !HAVE_DISPLAY 
#error "You cannot use colors unless you have a display" 
#error You cannot use colors unless you have a display 
#endif 

 

The #pragma directive

Another preprocessor directive is #pragma.
This directive allows the programmer to control the behavior of the compiler and gives compiler vendors the opportunity to implement extensions to the C language.
The #pragma directice is not covered further in this article since it has little to do with the main task of the preprocessor.

 

http://www.iar.com/Global/Resources/Developers_Toolbox/C_Cplusplus_Programming/Tips%20and%20tricks%20using%20the%20preprocessor%20(part%20two).pdf

Problems with function-like macros
At first sight, function-like macros seem like a simple and straight-forward construction.
However, when you examine them further you will notice a number of very annoying warts.
I will give a number of examples where each of the problems are clearly visible and suggest ways to handle them.

Always write parentheses around the parameter in the defnition
Consider the follwing, seemingly simple, macro: 

#define TIMES_TWO(x) x * 2 

For trivial cases this will get the job done. For example TIMES_TWO(4) expands to 4 * 2, which will be evaluated to 8.
On the other hand, you would expect that TIMES_TWO(4 + 5) would evaluate to 18, right?
Well, it doesn't since the macro simple replaces "x" with the parameter, as it is written.
This means that the compiler sees "4 + 5 * 2", which it evaluates to 14.
The solution is to always include the parameter in parentheses, for example:

#define TIMES_TWO(x) ( x ) * 2 

 

Macros (resulting in expressions) should be enclosed in parentheses
Assume that we have the following macro:

#define PLUS1(x) (x) + 1 

Here we have correctly placed parentheses around the parameter x. This macro will work in some locations;
for example, the following prints 11, as expected:

printf("%d\n", PLUS1(10)); 

 

However, in other situations the result might surprise you; the following prints 21, not 22

printf("%d\n", 2 * PLUS1(10)); 

So what is going on here? Well, again, this is due to the fact that the preprocessor simply replaces the
macro call with a piece of source code. This is what the compiler sees:

printf("%d\n", 2 * (10) + 1); 

 

Clearly, this is not what we wanted, as the multiplication is evaluated before the addition.
The solution is to place the entire definition of the macro inside parentheses:

#define PLUS1(x) ( (x) + 1 ) 

 

The preprocessor will expand this as follows, and the result "22", will be printed as expected.

printf("%d\n", 2 * ((10) + 1));

 

Macros and parameters with side effects
Consider the following macro and a typical way of using it:

#define SQUARE(x) ((x) * (x)) 
printf("%d\n", SQUARE(++i)); 

 

The user of the macro probably intended to increase i one step and print the squared value after the
increase. Instead, this is expanded to:

printf("%d\n", ((++i) * (++i))); 

 

The problem is that the side effect will take place on every occasion that a parameter is being used.
(As a side note, the resulting expression is not even well-defined C as the expression contains two modifications to "i".)
The rule of thumb here is that, if possible, each parameter should be evaluated exactly once.
If that is not possible this should be documented so that potential users will not be surprised.
So, if you cannot write macros that use each of its parameters exactly once, what could we do?
The straight-forward answer to that question is to avoid using macros—inline functions are supported by all C++ and most C compilers today,
and they will do the job equally well without the parameter side-effect problem of macros.
In addition, it is easier for the compiler to report errors when using functions since they are not type-less like macros.

 

Special macro features

Creating a string using the "#" operator
The "#" operator can be used in function-like macros to convert the parameter into a string.
At first this seems very straight-forward, but if you simply use the naive approach and use it directly in a macro you, unfortunately, will be surprised.
For example:

#define NAIVE_STR(x) #x 
puts(NAIVE_STR(10)); /* This will print "10". */ 

 


An example that will not work as expected is:

#define NAME Anders 
printf("%s", NAIVE_STR(NAME)); /* Will print NAME. */

 

 

This latter example prints NAME, not what NAME is defined to, which isn't what was intended.
So, what can we do about it? Fortunately, there is a standard solution for this:

#define STR_HELPER(x) #x 
#define STR(x) STR_HELPER(x) 

 

The idea behind this bizarre construction is that when STR(NAME) is expanded it is expanded into STR_HELPER(NAME), 
and before STR_HELPER is expanded all object-like macros like NAME are replaced first (as long as there are macros to replace).
When the function-like macro STR_HELPER is invoked the parameter that is passed to it is Anders.

Joining identifiers using the ## operator
The "##" operator is used in the definition of preprocessor macros to indicate that
we should join together small fragments into a larger identifier or number or whatever.
For example, assume that we have a collection of variables:

MinTime, MaxTime, TimeCount.
MinSpeed, MaxSpeed, SpeedCount.

We could define a macro, AVERAGE, with one parameter, that could return the average time, speed, or whatever.
Our first, naive, approach could be something like the following:

#define NAIVE_AVERAGE(x) (((Max##x) - (Min##x)) / (x##Count)) 

 

This will work for typical use cases: 

NAIVE_AVERAGE(Time); 

 

In this case this would expand to: 

return (((MaxTime) - (MinTime)) / (TimeCount)); 

 

However, as with the "#" operator above, when used in the following context this does not work as intended:

#define TIME Time 
NAIVE_AVERAGE(TIME) 

 


This, unfortunately, expands to:

return (((MaxTIME) - (MinTIME)) / (TIMECount)); 

 


The solution to this is as easy as in the STR case above. You will have to expand the macro in two steps.
Typically you could define a generic macro to glue anything together:

#define GLUE_HELPER(x, y)    x##y 
#define GLUE(x, y)           GLUE_HELPER(x, y) 

 


Now we are ready to use this in our AVERAGE macro:

#define AVERAGE(x)       (((GLUE(Max,x)) - (GLUE(Min,x))) / (GLUE(x,Count)))

 

Making macros look like statements, the "do {} while(0)" trick
It is very convenient to implement macros so that they have the same look-and-feel as normal C code.
The first step is simple: use object-like macros only for constants.

Use function-like macros for things that could be placed in a statement of its own, or for expressions that vary over time.
By maintaining the look-and-feel, we would like to let the user write the semicolon after function-like macros.
The preprocessor only replaces the macro with a piece of source code,
so we must ensure that the resulting program will not become surprised by the semicolon that follows.

For example:

void test() 
{ 
  a_function();   /* The semicolon is not part of 
  A_MACRO();     the macro substitution. */ 
} 

 



For macros that consist of one statement this is straight-forward, simply define the macro without a trailing semicolon.

#define DO_ONE() a_function(1,2,3) 

 


However, what happends if the macro contains two statements, for example two function calls? Why is the following not a good idea?

#define DO_TWO() first_function(); second_function() 

 


In a simple context this would work as intended, for example:

DO_TWO();

This expands to:

first_function(); second_function();

But what would happend in a context where a single statement is expected, for example:

if (... test something ...)
  DO_TWO();

Unfortunately, this expands to:

if (... test something ...)
  first_function();
  second_function();

The problem is that only "first_function" will become the body of the "if" statement.
The "second_function" will not be part of the statement, so it will always be called.

So, what about including the two calls inside braces, then the semicolon the user supplies will simply be a an empty statement?

#define DO_TWO() { first_function(); second_function(); }

Unfortunately, this still doesn't work as intended even if we expand the context to an "if" "else" clause.
Consider the following example:

if (... test something ...)
  DO_TWO();
else
  ...

This expands as follows; note the semicolon after the closing brace:

if (... test something ...)
  { first_function(); second_function(); };
else
  ...

The body of the "if" consists of two statements (the compound statement inside the braces and the empty statement consisting only of the semicolon).
This is not legal C as there must be exactly one statement between the "if (...)" and the "else"!
Here is where an old trick of the trades comes in: 

#define DO_TWO() do { first_function(); second_function(); } while(0)

I can't remember where I first saw this, but I remember how bewildered I was before I realized the brilliance of the construction.
The trick is to use a compund statement that expects a semicolon at the end, and there is one such construction in C, namely "do ... while(...);".
Hold on, you might think, this is loop! I want to do something once, not loop over them!
Well, in this case we just got lucky. A "do ... while(...)" loop will loop at least once, and then continue as long as the test expression in true.
Well, lets ensure that the expression never will be true, the trivial expression "0" is always false, and the body of the loop is executed exactly once.
This version of the macro have the look-and-feel of a normal function.
When used inside "if ... else" clauses the macro will expand to the following correct C code:

if (... test something ...)
  do { first_function(); second_function(); } while(0);
else
  ...

To conclude, the "do ... while(0)" trick is useful when creating function-like macros with the same look-and-feel as normal functions.
The downside is that the macro definition could look unintuitive, so I recommend that you comment on the purpose of the "do" and "while"
so that other people that would read the code do not have to become as puzzled as I was the first time I came across this.
Even if you decide not to use this method, hopefully you will be able to recognize it the next time you come across a macro of this form.,


Why you should prefer #ifs over #ifdefs
Most applications need some kind of configuration where portions of the actual source code are excluded.
For example, you could write a library with alternative implementations,
the application could contain code that requires a specific operating system or processor,
or the application could contain trace output that is intended to be used during internal testing.
As we earlier described, both #if and #ifdef can be used to exclude portions of the source code from being compiled.

#ifdefs

If #ifdefs are used the code looks like the following:

#ifdef MY_COOL_FEATURE
... included if "my cool feature" is used ...
#endif

#ifndef MY_COOL_FEATURE
... excluded if "my cool feature" is used ...
#endif

An application that uses #ifdefs normally doesn't have to have any special handling of configuration variables.

#ifs

When you are using #ifs the preprocessor symbols that are used are normally always defined.
The symbols that correponds to the symbols used by #ifdef are either true or false, which could be represented by the integers 1 and 0, respectively.

#if MY_COOL_FEATURE
... included if "my cool feature" is used ...
#endif

#if !MY_COOL_FEATURE
... excluded if "my cool feature" is used ...
#endif

Of course, preprocessor symbols could have more states, for example:

#if INTERFACE_VERSION == 0
  printf("Hello\n");
#elif INTERFACE_VERSION == 1
  print_in_color("Hello\n", RED);
#elif INTERFACE_VERSION == 2
  open_box("Hello\n");
#else
  #error "Unknown INTERFACE_VERSION"
#endif

An application that uses this style typically is forced to specify a default value for all configuration variables.
This could, for example, be done in a file, say, "defaults.h". When configuring the application some symbols could be specified either on the command line,
or preferably in a specific configuration header file, say "config.h". This configuration header file could be left empty if the default configuration should be used.
Example of a defaults.h header file:


/* defaults.h for the application. */
#include "config.h"
/*
* MY_COOL_FEATURE -- True, if my cool feature
* should be used.
*/
#ifndef MY_COOL_FEATURE
#define MY_COOL_FEATURE 0 /* Off by default. */
#endif

#if versus #ifdef which one should we use?
So far both methods seem fairly equal. If you look at real-world applications you will notice that both are commonly used.
At first glance #ifdefs seem easier to handle but experience has taught me that the #ifs are superior in the long run. 

#ifdefs don't protect you from misspelled words, #ifs do Clearly,
an #ifdef can't know whether the identifier is misspelled or not, as all it can tell is whether a certain identifier is defined at all.
For example, the following error will go unnoticed through the compilation:

#ifdef MY_COOL_FUTURE /* Should be "FEATURE". */
... Do something important ...
#endif


On the other hand, most compilers can detect that undefined symbols have been used in an #if directive.
The C standard says that this should be possible, and in that case the symbol should have the value zero.
(For the IAR Systems compiler the diagnostic message Pe193 is issued; by default this is a remark but it could be raised to a warning or, even better, to an error.)

#ifdefs are not future safe

Lets play with the idea that your application is configured using #ifdefs.
What will happen if you want to ensure that a property will be configured in a specific way—for example,
if you should support colors—even if the default value should change in the future? Well, unfortunately you cannot do this.
On the other hand, if you use #ifs to configure an application,
you can set a configuration variable to a specific value to ensure that you are future-safe in case the default value should change.

For #ifdefs, the default value dictates the name

If #ifdefs are used to configure an application the default configuration is to not specify any extra symbols.
For extra features this is straight forward, simply define MY_COOL_FEATURE.
However, should a feature be removed the name of the identifier often becomes DONT_USE_COLORS.

Double negatives are not really positive, are they?
One drawback is that the code becomes harder to read and write as this will introduce double negatives.
For example, some code should be included for color support:

#ifndef DONT_USE_COLORS
... do something ...
#endif

It might sound like a detail, but if you are browsing through large portions of code you will sooner or later become confused—well, at least I do.
I really do prefer the following:

#if USE_COLORS
... do something ...
#endif

You must know the default value when writing code
Another drawback is that when writing the application you must know (or look up) whether a feature is enabled by default.
In conjunction with the fact that you have no protection against misspelled words, this is an accident waiting to happen.

Nearly impossible to change the default value for #ifdefs 

However, the biggest drawback is that when #ifdefs are used, the application cannot change the default value without changing all #ifdefs all over the application.
For #ifs, changing the default value is trivial. All you need to do is to update the file containing the default values.

Migrating from #ifdefs to #ifs

Ok, you might say, this all sounds fine, but we are using #ifdefs in our application right now and I guess we have to live with them.
No, I would respond, you do not have to do that! It is more or less trivial to migrate from #ifdefs to #ifs.
In addition, you could provide backward compatibility for your old configuration variables.

First decide that you should name your new configuration variables.
A variable with a negative name (e.g. DONT_USE_COLORS) should be renamed to a positive form (e.g. USE_COLORS).
Variables with positive names can keep their names or you could rename them slightly.
If you keep the name of configuration variables, and the user has defined them as empty (as in "#define MY_COOL_FEATURE")
he will get a compilation error at the first #if that uses that symbol. Simply tell the user the define them to 1 instead.

Create a defaults.h header file, as described above, and ensure that all source files include this.
(If they do not, you will notice this, as soon as they use a configuration variable you will get an error since it will be undefined.)
In the beginning of the header file you could map the old #ifdef names to the new, for example:

/* Old configuration variables, ensure that they still work. */ 
#ifdef DONT_USE_COLORS 
#define USE_COLORS 0 
#endif 
/* Set the default. */ 
#ifndef USE_COLORS 
#define USE_COLORS 1 
#endif 

 


Then rename all occurences of #ifdefs to #ifs in all source files, accordingly:

From:                To: 
#ifdef MY_COOL_FEATURE      #if MY_COOL_FEATURE 
#ifndef MY_COOL_FEATURE     #if !MY_COOL_FEATURE 
#ifdef DONT_USE_COLORS      #if !USE_COLORS 
#ifndef DONT_USE_COLORS     #if USE_COLORS 

 

The end result is an application where all configuration variables are defined in one central location, 
together with the current default setting. Now, this is the perfect location to place those comments you
always planned to write but never got around to...

Conclusion

This is the end of the second and final part in this series of articles covering the C preprocessor.
In the first part we covered the basics. This, the second part, was dedicated to more advanced topics.

Hopefully, this series or articles have taught you how to effectively use the features of the preprocessor.
Even if you or your organization decide not to use the most exotic preprocessor techniques,
understanding them is still useful, for example, when reading code written by others.

 

 

posted @ 2013-05-25 23:02  IAmAProgrammer  阅读(1859)  评论(0编辑  收藏  举报