Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are braces needed for preprocessor in order to have statement?

Having this code:

#define GREATER(a, b, res) ( \ /* no braces here */
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); )

Error: c.c:4:2: error: expected expression before ‘asm’ asm("cmp %1, %2\n\t" \

But this one (only changed braces, everything else is left - the code is otherwise correct):

#define GREATER(a, b, res) ({ \ /* used braces here - correct */
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); })

Will compile with no error. But the only changed is added braces. So why are they needed? What does the preprocessor assume to be statement? (If only means being in braces.) I have seen other macro functions declared only in parenthesis (no curly braces), so why this one should have ones?

like image 765
Herdsman Avatar asked Nov 30 '25 16:11

Herdsman


2 Answers

The preprocessor directives are not involved in this issue. The issues are:

  • asm(…) is a GCC extension to the C language. GCC treats asm, which must be followed by a ;, as a statement.
  • Parentheses are parts of expressions. When you write (…), the contents of the parentheses should be an expression. Since asm is not an expression, (asm(…);) is an error.
  • GCC has an extension called statement expressions, in which ({…}) has a value like an expression but may contain statements. Inside ({…}), you may put statements inside the braces, and GCC will evaluate them and use the value of the last expression statement1 in them as the value of the ({…}) expression. (The last statement in ({…}) should be an expression statement, as opposed to some other kind of statement, just as a for loop.)

Thus ({ asm(…); }) is accepted as an expression.

However, although GCC accepts it, it is violating the statement in GCC’s documentation that “The last thing in the compound statement should be an expression followed by a semicolon…”. It does not look like your macro is intended to be used as an expression; it puts a result in res but does not itself have a value. In this case, you can make it simply a statement by removing the parentheses from your original code:

#define GREATER(a, b, res) \
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b));

Additionally, people often prefer to leave off the final ; in such macros, because then the macro can be written like a statement when used in source code:

GREATER(a, b, res);

instead of looking odd to people who are accustomed to statements ending with ;:

GREATER(a, b, res)

(Although with the ; in the definition, you can still write GREATER(a, b, res);, but this expands to have ;;, and that can cause problems, because if (foo) GREATER(a, b, res); else… will fail to associate that else with that if because of the extra ;.)

Footnote

1 A statement expression is an expression followed by a ;.

like image 161
Eric Postpischil Avatar answered Dec 02 '25 05:12

Eric Postpischil


If you compile the code fragment as posted, you will indeed get an error as the macro is defined as #define GREATER(a, b, res) ( \ /* no braces here */ and the remainder of the fragment is parsed as a separate line of source code because the \ is not the last character on the #define line.

You should be careful with the comment placement.

More generally, the asm statement is not an expression, it is a statement, hence it cannot occur between parentheses. If you wish to use the macro in an expression context, you must convert the statement to an statement-expression by surrounding it with ({ and }), a gcc extension, just like the asm statement. For the result to be used in an expression context, you must specify the value:

/* used braces here - correct */
#define GREATER(a, b, res) ({ \
    asm("cmp %1, %2\n\t" \
         "jge 0f\n\t" \
         "movl %1, %0\n\t" \
         "jmp 1f\n" \
         "0:\n\t" \
         "movl %2, %0\n" \
         "1:" \
         : "=r"(res) \
         : "r"(a), "r"(b)); res; })

Finally, the assembly code to compute the greater of a and b is inefficient. The compiler will likely generate better code from res = greater(a, b) with greater defined as static inline int greater(int a, int b) { return a > b ? a : b; }

like image 29
chqrlie Avatar answered Dec 02 '25 05:12

chqrlie



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!