Skip to content

Instantly share code, notes, and snippets.

@royratcliffe
Last active May 9, 2026 09:37
Show Gist options
  • Select an option

  • Save royratcliffe/6d8f58b0f518a56fa55974f030f7dfdc to your computer and use it in GitHub Desktop.

Select an option

Save royratcliffe/6d8f58b0f518a56fa55974f030f7dfdc to your computer and use it in GitHub Desktop.
va-nargs
/*
* SPDX-License-Identifier: MIT
* SPDX-FileCopyrightText: 2026, Roy Ratcliffe, Northumberland, United Kingdom
*/
/*!
* \file va.h
* \brief Header file for variadic argument counting macros.
* \details This file contains macros for counting the number of variadic
* arguments passed to a macro. It can count up to 63 arguments and returns the
* count as a compile-time constant. If there are no arguments, it returns 0. If
* there are more than 63 arguments, it returns 63.
*/
#ifndef VA_H
#define VA_H
/*!
* \brief Count number of variadic arguments.
* \details This macro can count up to 63 arguments. If the number of arguments
* is more than 63, it will return 63.
* \param ... Variadic arguments.
* \return Number of variadic arguments.
* \note If there are no arguments, returns 0. Achieved by using ##__VA_ARGS__
* which removes the comma if there are no arguments.
* \note The use of ##__VA_ARGS__ is not supported by all compilers, such as
* CCRX. For compilers that do not support ##__VA_ARGS__, the macro is defined
* without it, which may lead to incorrect counting when no arguments are
* provided.
* \note The maximum number of arguments that can be counted is 63 due to the
* use of reverse sequential numbers from 62 to 0 as padding. If more than 63
* arguments are provided, the count will be capped at 63.
* \note Why the initial argument is _0: The pasting operator \c ## concatenates
* the initial argument _0 with the variadic arguments. This ensures that when
* there are no variadic arguments, the macro still expands correctly and
* returns 0. If there were no initial argument, the macro would not expand
* correctly when no arguments are provided, leading to a compilation error.
*/
#if !defined(__CCRX__)
#define VA_NARGS(...) _VA_NARGS(_0, ##__VA_ARGS__, _VA_RSEQ63())
#else
#define VA_NARGS(...) _VA_NARGS(_0, __VA_ARGS__, _VA_RSEQ63())
#endif
/*!
* \brief Expands variadic arguments and selects the 64th.
* \param ... Variadic arguments padded with reverse sequential numbers.
* \return Number of variadic arguments.
*/
#define _VA_NARGS(...) _VA_ARG64(__VA_ARGS__)
/*!
* \brief Answers the 64th argument.
* \details The 64th argument is determined by the position of the arguments.
* Achieved by using reverse sequential numbers from 62 to 0 as padding. The
* 64th position will correspond to the number of arguments.
* \param ... Variadic arguments padded with reverse sequential numbers.
* \return The 64th argument from the variadic arguments.
*/
#define _VA_ARG64(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, \
_31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, \
_59, _60, _61, _62, _63, _64, ...) \
_64
/*!
* \brief Reverse sequential numbers from 62 to 0.
* \details Used to add padding to the variadic arguments so that the 64th
* position corresponds to the number of arguments.
*/
#define _VA_RSEQ63() \
62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, \
23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
#endif /* VA_H */
#include <va.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void test_VA_NARGS(void) {
assert(0 == VA_NARGS());
assert(1 == VA_NARGS(1));
assert(2 == VA_NARGS(1, 2));
assert(3 == VA_NARGS(1, 2, 3));
assert(4 == VA_NARGS(1, 2, 3, 4));
assert(5 == VA_NARGS(1, 2, 3, 4, 5));
assert(6 == VA_NARGS(1, 2, 3, 4, 5, 6));
assert(7 == VA_NARGS(1, 2, 3, 4, 5, 6, 7));
assert(8 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8));
assert(9 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9));
assert(10 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
assert(11 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11));
assert(12 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12));
assert(13 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13));
assert(14 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14));
assert(15 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15));
assert(16 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16));
assert(17 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17));
assert(18 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18));
assert(19 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19));
assert(20 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20));
assert(21 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21));
assert(22 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22));
assert(23 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23));
assert(24 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24));
assert(25 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25));
assert(26 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26));
assert(27 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27));
assert(28 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28));
assert(29 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29));
assert(30 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30));
assert(31 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31));
assert(32 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32));
assert(62 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 1..10 */
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, /* 11..20 */
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, /* 21..30 */
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 31..40 */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, /* 41..50 */
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, /* 51..60 */
61, 62));
assert(63 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 1..10 */
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, /* 11..20 */
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, /* 21..30 */
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 31..40 */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, /* 41..50 */
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, /* 51..60 */
61, 62, 63));
assert(63 == VA_NARGS(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, /* 1..10 */
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, /* 11..20 */
21, 22, 23, 24, 25, 26, 27, 28, 29, 30, /* 21..30 */
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 31..40 */
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, /* 41..50 */
51, 52, 53, 54, 55, 56, 57, 58, 59, 60, /* 51..60 */
61, 62, 63, 64));
}
int main(void) {
(void)printf("Hello, World from %s!!!\n", "va_test");
test_VA_NARGS();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment