Counting arguments

Counting arguments#

Work In Progress

This page is a draft

If you’ve made it this far, congratulations ! You’ve completed the introduction.

We will now start abusing macros and delve into black magic.

The pivotal trick is going to be able to count the number of arguments given to a variadic macro.

Objective#

We want a macro ARG_COUNT(...) that expands to an integer literal representing the number of arguments passed to it. It shall be usable from other macros

Usage#
int zero  = ARG_COUNT();
int one   = ARG_COUNT(A);
int three = ARG_COUNT(A, B, C);

#define FOO(...) printf("FOO was called with %i arguments", ARG_COUNT(__VA_ARGS__))

FOO("one", "two");
Expected result#
int zero  = 0;
int one   = 1;
int three = 3;

printf("Foo was called with %i arguments", 2);

Solution#

Let’s look at the simplest solution:

 1#include <stdio.h>
 2
 3#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
 4#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
 5
 6int main()
 7{
 8    printf("()         : %i\n", ARG_COUNT());
 9    printf("(foo)      : %i\n", ARG_COUNT(foo));
10    printf("('A', 'B') : %i\n", ARG_COUNT('A', 'B'));
11    printf("(1,2,3,4,5): %i\n", ARG_COUNT(1, 2, 3, 4, 5));
12}
 1#include <stdio.h>
 2
 3#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
 4#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
 5
 6int main()
 7{
 8    printf("()         : %i\n", ARG_COUNT());
 9    printf("(foo)      : %i\n", ARG_COUNT(foo));
10    printf("('A', 'B') : %i\n", ARG_COUNT('A', 'B'));
11    printf("(1,2,3,4,5): %i\n", ARG_COUNT(1, 2, 3, 4, 5));
12}
int main()
{
    printf("()         : %i\n", 0);
    printf("(foo)      : %i\n", 1);
    printf("('A', 'B') : %i\n", 2);
    printf("(1,2,3,4,5): %i\n", 5);
}
()         : 0
(foo)      : 1
('A', 'B') : 2
(1,2,3,4,5): 5

Two macros are involved:

  • The ARG_10 macro takes any number of arguments, and expands to the tenth argument.

  • The ARG_COUNT macro takes any number of arguments, forwards them to ARG_10, adding the 10 arguments 9, 8, 7, 6, 5, 4, 3, 2, 1, 0.

We can then see that if no arguments is passed to ARG_COUNT, the 10th argument is 0, and for every argument added, the extra arguements are pushed to the right.

It is quite similar to the optional argument trick shown in the previous chapter: a variadic macro allows the user not to provide enough arguement to another macro, that in turn discards extraneous arguments.

Usage example#

OK, great, we have a macro that can count the number of arguments given to it. What does this enable us to do ?

 1#include <stdio.h>
 2
 3#include "blackmagic/token.h" // STRINGIZE
 4
 5#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
 6#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
 7
 8#define SPELL_0 "zero"
 9#define SPELL_1 "one"
10#define SPELL_2 "two"
11#define SPELL_3 "three"
12#define SPELL_4 "four"
13#define SPELL_5 "five"
14
15#define SPELL_(N) SPELL_ ## N
16#define SPELL(N) SPELL_(N)
17
18#define FOO(...) printf("FOO was called with " SPELL(ARG_COUNT(__VA_ARGS__)) " (" STRINGIZE(ARG_COUNT(__VA_ARGS__)) ") arguments\n")
19
20int main()
21{
22    FOO();
23    FOO(1, 2);
24    FOO(1, 2, 3, 4, 5);
25}
 1#include <stdio.h>
 2
 3#include "blackmagic/token.h" // STRINGIZE
 4
 5#define ARG_10(A, B, C, D, E, F, G, H, I, J, ...) J
 6#define ARG_COUNT(...) ARG_10(__VA_ARGS__ __VA_OPT__(,) 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
 7
 8#define SPELL_0 "zero"
 9#define SPELL_1 "one"
10#define SPELL_2 "two"
11#define SPELL_3 "three"
12#define SPELL_4 "four"
13#define SPELL_5 "five"
14
15#define SPELL_(N) SPELL_ ## N
16#define SPELL(N) SPELL_(N)
17
18#define FOO(...) printf("FOO was called with " SPELL(ARG_COUNT(__VA_ARGS__)) " (" STRINGIZE(ARG_COUNT(__VA_ARGS__)) ") arguments\n")
19
20int main()
21{
22    FOO();
23    FOO(1, 2);
24    FOO(1, 2, 3, 4, 5);
25}
int main()
{
    printf("FOO was called with "
           "zero"
           " ("
           "0"
           ") arguments\n");
    printf("FOO was called with "
           "two"
           " ("
           "2"
           ") arguments\n");
    printf("FOO was called with "
           "five"
           " ("
           "5"
           ") arguments\n");
}
FOO was called with zero (0) arguments
FOO was called with two (2) arguments
FOO was called with five (5) arguments