Compound literals and function-like macros: bug in gcc or the C standard?
Asked Answered
K

3

8

In C99, we have compound literals, and they can be passed to functions as in:

f((int[2]){ 1, 2 });

However, if f is not a function but rather a function-like macro, gcc barfs on this due to the preprocessor parsing it not as one argument but as two arguments, "(int[2]){ 1" and "2 }".

Is this a bug in gcc or in the C standard? If it's the latter, that pretty much rules out all transparent use of function-like macros, which seems like a huge defect...

Edit: As an example, one would expect the following to be a conforming program fragment:

fgetc((FILE *[2]){ f1, f2 }[i]);

But since fgetc could be implemented as a macro (albeit being required to protect its argument and not evaluate it more than once), this code would actually be incorrect. That seems surprising to me.

Ked answered 5/4, 2011 at 20:33 Comment(6)
It's normal and standard. Don't forget, macros have existed since long before any of this other gubbins, and they are not as intelligent.Allen
gcc 4.5.2 with the -std=c99 option accepts MACRO_FX( ( (int[2]){1, 2} ) ) to group the expression in your example into one argument.Clerestory
I'm aware of the workaround. But this means function-like macros cannot truly be "function-like" in C99. Prior to compound literals, I don't think the language had any construct where this issue mattered.Ked
...and Steve goes and proves me wrong!Ked
This isn't a bug in any way. C and CPP are separate languages that happen to have an integrated toolchain, and CPP isn't (and isn't allowed to be) aware of C's constructs. If it were, you wouldn't be able to do things like the do/while trick, custom blocks based on for, or macros that eat types/operators (at least one of which - offsetof - is in the standard).Whitebeam
@Leushenko: In the sense it's arguably a bug in the C standard, in that C allows function-like-macro definitions for the standard functions despite them not actually behaving the same as functions. But as Steve's answer shows, it was already the case that these could behave in unexpected and weird ways unless you use extra parentheses, so if anything in the standard should be changed it's probably just a matter of adding language cautioning that conforming programs need to be aware of this and use parentheses where the issue could affect them.Ked
B
10

This "bug" has existed in the standard since C89:

#include <stdio.h>

void function(int a) {
    printf("%d\n", a);
}

#define macro(a) do { printf("%d\n", a); } while (0)

int main() {
    function(1 ? 1, 2: 3); /* comma operator */
    macro(1 ? 1, 2: 3);    /* macro argument separator - invalid code */
    return 0;
}

I haven't actually looked through the standard to check this parse, I've taken gcc's word for it, but informally the need for a matching : to each ? trumps both operator precedence and argument list syntax to make the first statement work. No such luck with the second.

Bring answered 5/4, 2011 at 22:28 Comment(3)
+1 excellent find for a case where this issue exists pre-C99. I suppose extra parentheses are just needed wherever a function could actually be defined as a function-like macro...Ked
@R..: yes, macros and code generators probably already put extra parentheses around anything they treat as an expression, out of paranoia, concern over operator precedence and silly semi-colons, etc. Real humans in practice will add them when the compiler tells them to, which I think it always will barring variadic function-like macro.Bring
Hmm, I suppose a variadic function-like macro is actually a solution to this problem! That is, an implementation could do #define fgets(...) __inline_fgets(__VA_ARGS__) and have it work in all cases. (#define fgets __inline_fgets on the other hand is not valid for an implementation to do because it will make the lone function name with no function-call operator evaluate to the wrong function pointer, i.e. different pointers in different translation units.)Ked
I
5

This is per the C Standard, similar to how in C++, the following is a problem:

f(ClassTemplate<X, Y>) // f gets two arguments:  'ClassTemplate<X' and 'Y>'

If it is legal to add some extra parentheses there in C99, you can use:

f(((int[2]){ 1, 2 }));
  ^                ^

The rule specifying this behavior, from C99 §6.10.3/11, is as follows:

The sequence of preprocessing tokens bounded by the outside-most matching parentheses forms the list of arguments for the function-like macro.

The individual arguments within the list are separated by comma preprocessing tokens, but comma preprocessing tokens between matching inner parentheses do not separate arguments.

Izzo answered 5/4, 2011 at 20:35 Comment(0)
J
1

To the extent that it's a bug at all, it's with the standard, not gcc (i.e., in this respect I believe gcc is doing what the standard requires).

Jejunum answered 5/4, 2011 at 20:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.