gcc: Statement Exprs
1
1 6.1 Statements and Declarations in Expressions
1 ==============================================
1
1 A compound statement enclosed in parentheses may appear as an expression
1 in GNU C. This allows you to use loops, switches, and local variables
1 within an expression.
1
1 Recall that a compound statement is a sequence of statements surrounded
1 by braces; in this construct, parentheses go around the braces. For
1 example:
1
1 ({ int y = foo (); int z;
1 if (y > 0) z = y;
1 else z = - y;
1 z; })
1
1 is a valid (though slightly more complex than necessary) expression for
1 the absolute value of 'foo ()'.
1
1 The last thing in the compound statement should be an expression
1 followed by a semicolon; the value of this subexpression serves as the
1 value of the entire construct. (If you use some other kind of statement
1 last within the braces, the construct has type 'void', and thus
1 effectively no value.)
1
1 This feature is especially useful in making macro definitions "safe"
1 (so that they evaluate each operand exactly once). For example, the
1 "maximum" function is commonly defined as a macro in standard C as
1 follows:
1
1 #define max(a,b) ((a) > (b) ? (a) : (b))
1
1 But this definition computes either A or B twice, with bad results if
1 the operand has side effects. In GNU C, if you know the type of the
1 operands (here taken as 'int'), you can define the macro safely as
1 follows:
1
1 #define maxint(a,b) \
1 ({int _a = (a), _b = (b); _a > _b ? _a : _b; })
1
1 Embedded statements are not allowed in constant expressions, such as
1 the value of an enumeration constant, the width of a bit-field, or the
1 initial value of a static variable.
1
1 If you don't know the type of the operand, you can still do this, but
1 you must use 'typeof' or '__auto_type' (⇒Typeof).
1
1 In G++, the result value of a statement expression undergoes array and
1 function pointer decay, and is returned by value to the enclosing
1 expression. For instance, if 'A' is a class, then
1
1 A a;
1
1 ({a;}).Foo ()
1
1 constructs a temporary 'A' object to hold the result of the statement
1 expression, and that is used to invoke 'Foo'. Therefore the 'this'
1 pointer observed by 'Foo' is not the address of 'a'.
1
1 In a statement expression, any temporaries created within a statement
1 are destroyed at that statement's end. This makes statement expressions
1 inside macros slightly different from function calls. In the latter
1 case temporaries introduced during argument evaluation are destroyed at
1 the end of the statement that includes the function call. In the
1 statement expression case they are destroyed during the statement
1 expression. For instance,
1
1 #define macro(a) ({__typeof__(a) b = (a); b + 3; })
1 template<typename T> T function(T a) { T b = a; return b + 3; }
1
1 void foo ()
1 {
1 macro (X ());
1 function (X ());
1 }
1
1 has different places where temporaries are destroyed. For the 'macro'
1 case, the temporary 'X' is destroyed just after the initialization of
1 'b'. In the 'function' case that temporary is destroyed when the
1 function returns.
1
1 These considerations mean that it is probably a bad idea to use
1 statement expressions of this form in header files that are designed to
1 work with C++. (Note that some versions of the GNU C Library contained
1 header files using statement expressions that lead to precisely this
1 bug.)
1
1 Jumping into a statement expression with 'goto' or using a 'switch'
1 statement outside the statement expression with a 'case' or 'default'
1 label inside the statement expression is not permitted. Jumping into a
1 statement expression with a computed 'goto' (⇒Labels as Values)
1 has undefined behavior. Jumping out of a statement expression is
1 permitted, but if the statement expression is part of a larger
1 expression then it is unspecified which other subexpressions of that
1 expression have been evaluated except where the language definition
1 requires certain subexpressions to be evaluated before or after the
1 statement expression. In any case, as with a function call, the
1 evaluation of a statement expression is not interleaved with the
1 evaluation of other parts of the containing expression. For example,
1
1 foo (), (({ bar1 (); goto a; 0; }) + bar2 ()), baz();
1
1 calls 'foo' and 'bar1' and does not call 'baz' but may or may not call
1 'bar2'. If 'bar2' is called, it is called after 'foo' and before
1 'bar1'.
1