Skip to content

Instantly share code, notes, and snippets.

@aprell
Last active March 9, 2024 08:44
Show Gist options
  • Save aprell/3722962 to your computer and use it in GitHub Desktop.
Save aprell/3722962 to your computer and use it in GitHub Desktop.
Counting arguments in variadic macros
// RUN: cc -Wall -Wextra %s && ./a.out
#include <assert.h>
#if defined(__GNUC__) || defined(__clang__)
// Supports 0-10 arguments
#define VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
// ## deletes preceding comma if _VA_ARGS__ is empty (GCC, Clang)
#define VA_NARGS(...) VA_NARGS_IMPL(_, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#else
// Supports 1-10 arguments
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#endif
#define VA_NARGS2(...) ((int)(sizeof((int[]){ __VA_ARGS__ })/sizeof(int)))
#define TEST(...) VA_NARGS(__VA_ARGS__)
#define TEST2(...) VA_NARGS2(__VA_ARGS__)
void test(void)
{
assert(TEST() == 0);
assert(TEST(1) == 1);
assert(TEST(1, 2) == 2);
assert(TEST(1, 2, 3) == 3);
assert(TEST(1, 2, 3, 'a') == 4);
assert(TEST(1, 2, 3, 'a', 'b') == 5);
assert(TEST(1, 2, 3, 'a', 'b', 'c') == 6);
assert(TEST(1, 2, 3, 'a', 'b', 'c', 4.0) == 7);
assert(TEST(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0) == 8);
assert(TEST(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0, 6.0) == 9);
assert(TEST(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0, 6.0, 0) == 10);
//assert(TEST(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0, 6.0, 0, 0) == 11); // fails
assert(TEST2() == 0);
assert(TEST2(1) == 1);
assert(TEST2(1, 2) == 2);
assert(TEST2(1, 2, 3) == 3);
assert(TEST2(1, 2, 3, 'a') == 4);
assert(TEST2(1, 2, 3, 'a', 'b') == 5);
assert(TEST2(1, 2, 3, 'a', 'b', 'c') == 6);
assert(TEST2(1, 2, 3, 'a', 'b', 'c', 4.0) == 7);
assert(TEST2(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0) == 8);
assert(TEST2(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0, 6.0) == 9);
assert(TEST2(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0, 6.0, 0) == 10);
assert(TEST2(1, 2, 3, 'a', 'b', 'c', 4.0, 5.0, 6.0, 0, 0) == 11);
}
int main(void)
{
test();
return 0;
}
@ntrel
Copy link

ntrel commented Jun 24, 2020

@bbap-7fat Sounds like your compiler doesn't support C99.

@aprell
Copy link
Author

aprell commented Jun 24, 2020

I'm not familiar with Visual Studio, but maybe this helps (if it's not a C99 issue): https://stackoverflow.com/questions/33270731/error-c4576-in-vs2015-enterprise

@sammonius
Copy link

sammonius commented Jun 12, 2022

Small improvement:

#if defined(__GNUC__) || defined(__clang__) // supports passing 0 arguments using gcc/clang non-standard features
#define VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define VA_NARGS(...) VA_NARGS_IMPL(_, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#else
#define VA_NARGS_IMPL(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N // 1 or more arguments only
#define VA_NARGS(...) VA_NARGS_IMPL(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#endif

@aprell
Copy link
Author

aprell commented Jun 16, 2022

Thanks, @sammonius, I'll update the gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment