P99

◆ P99_CALL_VA_ARG

#define P99_CALL_VA_ARG (   NAME,
  M,
  T,
  ... 
)    NAME(__VA_ARGS__)

Assure type safety for variadic functions.

Variadic functions in C have a big loophole in that they don't enforce the types for parameters that are given to the ... list. This may have severe consequences when you pass constants as arguments to such functions and the resulting type differs from what is expected.

A classical example is NULL, which may be an integral 0 of any type or (void*)0. Suppose a function

void toto_raw(size_t len, ...);

expects the parameter len, followed by a list of arguments expected to be void*. Consider the following three calls to toto_raw:

toto_raw(2, NULL, malloc(23));
toto_raw(2, 0, malloc(23));
toto_raw(2, (void*)0, malloc(23));

Depending on the compiler, the first two might both result in an (int)0 being put on the stack. sizeof(int) might for example be 4 and sizeof(void*) be 8, so the program would crash. C has no automatic way to detect that since the interface of toto_raw simply can't specify which types the function expects.

P99_CALL_VA_ARG allows you to "declare" the type of the ... arguments.

#define toto(...) P99_CALL_VA_ARG(toto_raw, 1, void*, __VA_ARGS__)
Parameters
NAMEis the name of the variadic function that is to be called
Mis the number of arguments of NAME before the ...
Tis the conversion type of the ... arguments

All the following calls of toto result in valid calls of toto_raw and only put void* values in the variadic list:

toto(0);
toto(1, NULL);
toto(1, 0);
toto(1, (void*)0);
toto(2, NULL, malloc(23));
toto(2, 0, malloc(23));
toto(2, (void*)0, malloc(23));

The conversion of the arguments to type T is done with P99_RVAL. So all values passed in the variadic list must be assignment compatible with type T.

This macro also allows you to declare default values for the M first arguments. We could for example do

#define toto_defarg_0() 0

Then calling toto without arguments would be valid:

toto();

The convention for these symbols is that the number at the end corresponds to the position of the argument, starting from 0. This may be a macro, as above, or a function. We could have achieved the same effect by declaring

inline size_t toto_defarg_0(void) { return 0; }

The arguments in the variadic list may have not only a default type but may have a default value, too.

#define toto_defarg() ((void*)0)

By that

toto(1,);

would expand to something equivalent to

toto(1, (void*)0);

The naming convention is similar to that stated above for the numbered arguments, only that the suffix "_N" is omitted from the name of the function or macro.

Definition at line 424 of file p99_defarg.h.