cpp: Duplication of Side Effects

1 
1 3.10.4 Duplication of Side Effects
1 ----------------------------------
1 
1 Many C programs define a macro 'min', for "minimum", like this:
1 
1      #define min(X, Y)  ((X) < (Y) ? (X) : (Y))
1 
1    When you use this macro with an argument containing a side effect, as
1 shown here,
1 
1      next = min (x + y, foo (z));
1 
1 it expands as follows:
1 
1      next = ((x + y) < (foo (z)) ? (x + y) : (foo (z)));
1 
1 where 'x + y' has been substituted for 'X' and 'foo (z)' for 'Y'.
1 
1    The function 'foo' is used only once in the statement as it appears
1 in the program, but the expression 'foo (z)' has been substituted twice
1 into the macro expansion.  As a result, 'foo' might be called two times
1 when the statement is executed.  If it has side effects or if it takes a
1 long time to compute, the results might not be what you intended.  We
1 say that 'min' is an "unsafe" macro.
1 
1    The best solution to this problem is to define 'min' in a way that
1 computes the value of 'foo (z)' only once.  The C language offers no
1 standard way to do this, but it can be done with GNU extensions as
1 follows:
1 
1      #define min(X, Y)                \
1      ({ typeof (X) x_ = (X);          \
1         typeof (Y) y_ = (Y);          \
1         (x_ < y_) ? x_ : y_; })
1 
1    The '({ ... })' notation produces a compound statement that acts as
1 an expression.  Its value is the value of its last statement.  This
1 permits us to define local variables and assign each argument to one.
1 The local variables have underscores after their names to reduce the
1 risk of conflict with an identifier of wider scope (it is impossible to
1 avoid this entirely).  Now each argument is evaluated exactly once.
1 
1    If you do not wish to use GNU C extensions, the only solution is to
1 be careful when _using_ the macro 'min'.  For example, you can calculate
1 the value of 'foo (z)', save it in a variable, and use that variable in
1 'min':
1 
1      #define min(X, Y)  ((X) < (Y) ? (X) : (Y))
1      ...
1      {
1        int tem = foo (z);
1        next = min (x + y, tem);
1      }
1 
1 (where we assume that 'foo' returns type 'int').
1