Interfacing C variadic functions
Variadic convention is supported by
GNAT Community Edition 2020
In C, variadic functions take a variable number of arguments and an ellipsis as the last parameter of the declaration. A typical and well-known example is:
int printf (const char* format, ...);
Usually, in Ada, we bind such a function with just the parameters we want to use:
procedure printf_double (format : Interfaces.C.char_array; value : Interfaces.C.double) with Import, Convention => C, External_Name => "printf";
Then we call it as a normal Ada function:
printf_double (Interfaces.C.To_C ("Pi=%f"), Ada.Numerics.π);
%rax— with variable arguments passes information about the number of vector registers used;
%xmm0–%xmm1— used to pass and return floating point arguments.
This means, if we write (in C):
The compiler will place 0 into
%rax, because we don't pass any float
argument. But in Ada, if we write:
procedure printf_int (format : Interfaces.C.char_array; value : Interfaces.C.int) with Import, Convention => C, External_Name => "printf"; printf_int (Interfaces.C.To_C ("d=%d"), 5);
the compiler won't use the
%rax register at all. (You can't include
any float argument because there's no float parameter in the Ada
wrapper function declaration.) As result, you will get a crash, stack
corruption, or other undefined behavior.
To fix this, Ada 2022 provides a new family of calling convention
C_Variadic_nis the calling convention for a variadic C function taking n fixed parameters and then a variable number of additional parameters.
Therefore, the correct way to bind the
printf function is:
procedure printf_int (format : Interfaces.C.char_array; value : Interfaces.C.int) with Import, Convention => C_Variadic_1, External_Name => "printf";
And the following call won't crash on any supported platform:
printf_int (Interfaces.C.To_C ("d=%d"), 5);
Without this convention, problems cause by this mismatch can be very hard to debug. So, this is a very useful extension to the Ada-to-C interfacing facility.
Here is the complete code snippet: