This isn’t practical, but I think it’s neat that it’s doable in C99. The implementation I present here is incomplete and for illustrative purposes only.
Background
C’s implementation of variadic functions (functions that take a variable-number of arguments) is characteristically bare-bones. Even though the compiler knows the number, and type, of all arguments passed to variadic functions; there isn’t a mechanism for the function to get this information from the compiler. Instead, programmers need to pass an extra argument, like the printf
format-string, to tell the function “these are the arguments I gave you”. This has worked for over 37 years. But it’s clunky — you have to write the same information twice, once for the compiler and again to tell the function what you told the compiler.
Inspecting Arguments in C
Argument Type
I don’t know of a way to find the type of the Nth argument to a varadic function, called with heterogeneous types. If you can figure out a way, I’d love to know. The typeof
extension is often sufficient to write generic code that works when every argument has the same type. (C++ templates also solve this problem if we step outside of C-proper.)
Argument Count (The Good Stuff Starts Here)
By using variadic macros, and stringification (#
), we can actually pass a function the literal string of its argument list from the source code — which it can parse to determine how many arguments it was given.
For example, say f()
is a variadic function. We create a variadic wrapper macro, F()
and call it like so in our source code,
x = F(a,b,c);
The preprocessor expands this to,
x = f("a,b,c",a,b,c)
Or perhaps,
x = f(count_arguments("a,b,c"),a,b,c)
where count_arguments(char *s)
returns the number of arguments in the string source-code string s
. (Technically s
would be an argument-expression-list).
Example Code
Here’s an implementation for, iArray()
, an array-builder for int
values, very much like JavaScript‘s Array()
constructor. Unlike the quirky JavaScript Array()
, iArray(3)
returns an array containing just the element 3, [3]
, not an uninitilized array with 3 elements, [undefined, undefined, undefined]
. Another difference: iArray()
, invoked with no arguments, is invalid, and will not compile.
#define iArray(...) alloc_ints(count_arguments(#__VA_ARGS__), __VA_ARGS__)
This macro is pretty straightforward. It’s given a variable number of arguments, represented by __VA_ARGS__
in the expansion. #__VA_ARGS__
turns the code into a string so that count_arguments
can analyze it. (If you were doing this for real, you should use two levels of stringification though, otherwise macros won’t be fully expanded. I choose to keep things “demo-simple” here.)
unsigned count_arguments(char *s){ unsigned i,argc = 1; for(i = 0; s[i]; i++) if(s[i] == ',') argc++; return argc; }
This is a dangerously naive implementation and only works correctly when iArray()
is given a straightforward non-empty list of values or variables. Basically it’s the least code I could write to make a working demo.
Since iArray
must have at least one argument to compile, we just count the commas in the argument-list to see how many other arguments were passed. Simple to code, but it fails for more complex expressions like f(a,g(b,c))
.
int *alloc_ints(unsigned count, ...){ unsigned i = 0; int *ints = malloc(sizeof(int) * count); va_list args; va_start(args, count); for(i = 0; i < count; i++) ints[i] = va_arg(args,int); va_end(args); return ints; }
Just as you'd expect, this code allocates enough memory to hold count
int
s, and fills it with the remaining count
arguments. Bad things happen if < count
arguments are passed, or they are the wrong type.
Download the code, if you like.
Parsing is Hard, Let's Go Shopping
I didn't even try to correctly parse any valid argument-expression-list in count_arguments
. It's non trivial. I'd rather deal with choosing the correct MAX3
or MAX4
macro in a few places than maintain such a code base.
So this kind of introspection isn't really practical in C. But it's neat that it can be done, without any tinkering with the compiler or language.