How can I retrieve the last argument of a C99 variadic macro?
c, c++, c-preprocessor, c99
Solution
There's no easy way to get the last macro argument in the fully general case, but you can easily implement a version that gets the last element for argument lists up to some predetermined maximum. Something like 64 arguments is usually enough for real-world code.
All you need to do is count the number of arguments passed, and then return element `N-1` from the list:
// count arguments
#define M_NARGS(...) M_NARGS_(__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define M_NARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
// utility (concatenation)
#define M_CONC(A, B) M_CONC_(A, B)
#define M_CONC_(A, B) A##B
#define M_GET_ELEM(N, ...) M_CONC(M_GET_ELEM_, N)(__VA_ARGS__)
#define M_GET_ELEM_0(_0, ...) _0
#define M_GET_ELEM_1(_0, _1, ...) _1
#define M_GET_ELEM_2(_0, _1, _2, ...) _2
#define M_GET_ELEM_3(_0, _1, _2, _3, ...) _3
#define M_GET_ELEM_4(_0, _1, _2, _3, _4, ...) _4
#define M_GET_ELEM_5(_0, _1, _2, _3, _4, _5, ...) _5
#define M_GET_ELEM_6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define M_GET_ELEM_7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define M_GET_ELEM_8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define M_GET_ELEM_9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define M_GET_ELEM_10(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, ...) _10
// Get last argument - placeholder decrements by one
#define M_GET_LAST(...) M_GET_ELEM(M_NARGS(__VA_ARGS__), _, __VA_ARGS__ ,,,,,,,,,,,)
You can extend this to as large a finite amount as you want with a few moments' copy and paste.
Problem
Visual Studio's error message for a failed static_assert consists entirely of just an error code and the second parameter to the static_assert, without any additional message indicating that it was a static assertion failure. I'd like to make a macro to solve this problem. For example, as a first try: ``` #define STATIC_ASSERT(x) static_assert(x, "static assertion failed: " #x) ``` The first issue that you run into is that the C preprocessor doesn't understand `< >` as being enclosing delimiters, which results in syntax errors with templates. The following becomes illegal: ``` template <typename T, typename U> auto SafeMultiply(T x, U y) -> decltype(x * y) { STATIC_ASSERT(std::is_same<T, U>::value); STATIC_ASSERT(!std::numeric_limits<T>::is_signed); if (x > (std::numeric_limits<decltype(x * y)>::max)()) throw std::overflow_error("multiplication overflow"); return x * y; } ``` This is illegal because the comma between T and U in the first `STATIC_ASSERT` is interpreted as separating two macro parameters, rather than template parameters. The C preprocessor throws an error because the `STATIC_ASSERT` macro only takes one parameter. The two main solutions to this problem are to use double parentheses and, more recently, to use variadic macros: ``` // Invoke the macro this way... STATIC_ASSERT((std::is_same<T, U>::value)); // ...or define it this way: #define STATIC_ASSERT(...) static_assert((__VA_ARGS__), \ "static assertion failed: " #__VA_ARGS__) ``` The latter solution is better, requiring only changes to the macro definition. (The extra parentheses around `__VA_ARGS__` in the new definition are to preserve proper order of operations in some of the weirder cases. It may not matter in this particular macro, but it's a good habit to put parentheses around your macros' parameters in your macro definitions.) Now what if I wanted to change my `STATIC_ASSERT` macro to take a message like the standard C++ `static_assert`, but adding a prefix to the message? Kind of like this, but supporting the use of `std::is_same<T, U>` without the use of double parentheses: ``` // Causes a syntax error :( #define STATIC_ASSERT(expr, msg) static_assert((expr), \ "static assertion failed: " msg) STATIC_ASSERT(std::is_same<T, U>, "x and y are not of the same type"); ``` If I could get the last parameter of a variadic macro, it would work: ``` // I wish this'd work #define STATIC_ASSERT(..., msg) static_assert((__VA_ARGS__), \ "static assertion failed: " msg) STATIC_ASSERT(std::is_same<T, U>, "x and y are not of the same type"); ``` But since that's not legal, how can I legally get the last parameter of a `...` macro parameter set? Sure, I could reverse the parameter order, but then it's not the same as `static_assert`.