The example does not actually use compound literals. (i.e. _arg's initializer would work in C89.) It does use two other C99 features, though -- variadic macros and a for-loop-scoped variable declaration. The biggest compatibility issue, though, are its use of GCC extensions:
- Statement expressions: e.g.: int x = ({ int y = 0; y; });
- If zero-args are allowed (e.g. bar()), then _args is 0-sized, which is also non-standard.
These GCC extensions are somewhat common. Clang has them, and (I think) EDG's frontend does too. MSVC does not have either of them, even in MSVC 2015.
That said, a compound literal might be a good way to remove the extension use, but as long as bar() returns void, the do-while(0) trick is also sufficient for replacing the statement expression.
bar() results in trailing commas. My impression is that C99 allows them in array initializers. (I saw an example in the n1256 C draft.) I don't recall whether C89 also had them. __VA_ARGS__ is expanded twice, but I think that's OK, because sizeof() only evaluates its operand when it has VLA type, and it never will. This code will work in MSVC 2013 (if not earlier).
Using the online MSVC compiler at http://webcompiler.cloudapp.net/ (which runs Visual C++ 19.00.23720.0), it failed with these error message:
Compiled with /EHsc /nologo /W4 /c
main.cpp
main.cpp(20): error C4576: a parenthesized type followed by an
initializer list is a non-standard explicit type conversion syntax
main.cpp(21): error C4576: a parenthesized type followed by an initializer
list is a non-standard explicit type conversion syntax
main.cpp(22): error C4576: a parenthesized type followed by an initializer list
is a non-standard explicit type conversion syntax
main.cpp(23): error C4576: a parenthesized type followed by an initializer list
is a non-standard explicit type conversion syntax
If for some absurd reason you really do need to emulate a "compound statement expression" in MSVC (because you need to declare some temp variables in a macro that can be used for assignment), it turns out that it can be done with a "lambda expression":
#include <stdio.h>
int bar(int n, const char *p[])
{
int total = 0;
for (int i = 0; i < n; i++) {
total += printf("%s\t", p[i]);
}
putchar('\n');
return total;
}
#define bar(...) [](){ \
const char *_args[] = {NULL, __VA_ARGS__}; \
return bar(sizeof(_args)/sizeof(*_args) - 1, _args + 1); \
}()
int main(/* int argc, char **argv */) {
int total = 0;
total += bar();
total += bar("a");
total += bar("a", "b");
total += bar("a", "b", "c");
printf("total: %d\n", total);
return 0;
}
Compound literals were never formally added to C++.
Still, that hasn't stopped various front-ends (including GCC and Clang) from adding them to their C++ dialects. My code compiles with Clang, but not GCC. GCC complains that it's not OK to take the address of the array compound literal.
FWIW, C99 makes it clear that the object has automatic storage duration lasting until the end of the enclosing block. i.e. There shouldn't be an issue with dangling pointers.
My impression is that it's impossible in MSVC's C dialect today. (Obviously there's std::min in C++.)
AFAICT, it could be done using _Generic and inline functions, if/when MSVC adds _Generic. I'd expect to see _Generic before seeing any of the GCC extensions, but I'm not aware of any commitment from MS to add it. There are some issues with _Generic and qualifiers that might slow its adoption. e.g. There's an open defect report regarding qualified rvalues (DR423), but even lvalues seem to behave differently between GCC 5.2.0 and Clang 3.7.1. GCC ignores an lvalue's qualifiers but still allows a "const int" branch. AFAICT, a const-qualified branch is never selected with GCC?
I'm wondering whether it's possible to use _Generic to require that the left and right operands have the same type (either before or after integral promotions). Qualifiers are an obvious nuisance, but the bigger problem is that even the unselected branches must still compile, and C does not have tuples.
The presented example will work only if the actual arguments are all of the same type. For more general usage, namely printf() and its variants, the variadic macro fails.
If you go back to the genesis stack overflow question, the point is to make sure all the variadic arguments are of the same type. No, it wouldn't work for printf but gcc and clang have __attribute__(format) for that.
In principle, it seems you could handle the heterogenous case by designing an `Any` struct and (at least in C++) implicit constructors to take various types (int, T*, ...). At some point this starts to feel like designing a custom ABI and the complexity may not be worth it, but it seems there should be a way :-)
> it seems you could handle the heterogenous case by designing an `Any` struct and (at least in C++) implicit constructors to take various types (int, T*, ...)
In C11 you could also use _Generic. It will be perfect for the year 2045 when Microsoft adds C11 support.
Qualifiers might also be an issue. My impression so far is that every combination of differently qualified type is supposed to require a different _Generic association. (GCC 5.2 and Clang 3.7 differ here.) That would require a huge number of _Generic associations, but it might be possible to strip the qualifiers using a comma operator, _Generic( (0,(x)), ... ). A comma expression is not an lvalue, so perhaps it cannot be qualified. DR423[1] askes that question.
Yeah, but then you have to compile the function in every single cpp file and your linking time increases and you're at risk of violating ODR and you have to give people the full definition of your function if you're making a library rather than just the declaration and a .a/.obj/.so.
You provide a type safe variadic template interface which forwards to a type erased out of line layer. It would compile down to the same code as the macro.
I should note that if you just want to get the number of arguments for a call to a varargs function rather than trust people to terminate the arguments with NULL properly, you can do this with the C99 preprocessor:
where PP_NUM_ARGS is a macro to determine the number of arguments; you can find such a macro easily by searching.
Of course you could just do #define foo(...) foo(__VA_ARGS__, NULL), but this lets you include NULL values in the list.
Since the count takes up the first argument to the actual varargs function, you can call it with zero arguments. No type checking, though as mentioned above you might be able to do something interesting about that with _Generic.
Using macro fails if I happen to pass a variable named _args as one of the arguments to bar(). Is there any clever trick to simulate gensym in C macro?
Usually for those situations I just try to pick something that's a little more unique (maybe __bar_args). For things that end up defining globals I've used this technique before:
#define my_useless_macro(value) \
static int Unique(__macro_var_name) = value
…with the (large?) caveat that it won't work if you put 2 on the same line:
my_useless_macro(0); // works
my_useless_macro(1); my_useless_macro(2); // Fails
If you expect to call my_usless_macro() multiple times on a line (hint, macro expansions are always on the same line), then you might put an extra optional parameter in there with __VA_ARGS__, like this:
#define my_useless_macro2(value,...) \
static int Unique(CAT(__macro_var_name, __VA_ARGS__)) = value
- Statement expressions: e.g.: int x = ({ int y = 0; y; });
- If zero-args are allowed (e.g. bar()), then _args is 0-sized, which is also non-standard.
These GCC extensions are somewhat common. Clang has them, and (I think) EDG's frontend does too. MSVC does not have either of them, even in MSVC 2015.
That said, a compound literal might be a good way to remove the extension use, but as long as bar() returns void, the do-while(0) trick is also sufficient for replacing the statement expression.
I think this compound literal usage works:
bar() results in trailing commas. My impression is that C99 allows them in array initializers. (I saw an example in the n1256 C draft.) I don't recall whether C89 also had them. __VA_ARGS__ is expanded twice, but I think that's OK, because sizeof() only evaluates its operand when it has VLA type, and it never will. This code will work in MSVC 2013 (if not earlier).