This might be kind of a weird question, but I am a novice with __builtin_va_list
and how it works.
I am creating a function with the following signature:
typedef signed char i8;
typedef long long signed int i64;
typedef long long unsigned int u64;
i64 f ( u64 arg_count , ... );
Ideally, the function is supposed to work like this under the hood:
i64 f ( u64 arg_count , u64* args );
I like that the ...
syntax lets me take any number of arguments without explicitly defining an array first, but for the purposes of the function, I really want to explicitly cast each of the them all to u64
before parsing them in my function, and the __builtin_va_list
does not allow me to do that without explicitly casting each argument .
For example, if I call:
i8 c = 27;
u64 i = 27;
f ( 2 , c , i );
and within the function pop an argument with __builtin_va_arg
, I have to correctly predict the size of the incoming type in order to get the correct value. For example,
i64 f ( u64 arg_count , ... )
{
__builtin_va_list args;
__builtin_va_start ( args , arg_count );
u64 c = __builtin_va_arg ( args , u64 ); // This will be wrong because the first parameter was 8 bits, not 64
...
}
Explicit casting like this would work I suppose:
i8 c = 27;
u64 i = 27;
f ( 2 , ( u64 ) c , ( u64 ) i );
...but it is annoying to type for long lists.
Is there any way to accomplish the desired syntax using plain C / preprocessor? I usually just compile with Clang, but a fairly cross-platform solution would be ideal.
Instead of passing the values as arguments in a variable argument list, you can pass them as an array, using a compound literal:
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
/* Define a macro to convert its variable arguments to a compound literal
array and to pass that array to f.
*/
#define F(n,...) f((n), (uint64_t []) { __VA_ARGS__ })
// Print the n values pointed to by p.
static void f(uint64_t n, uint64_t *p)
{
printf("f(%" PRIu64, n);
for (uint64_t i = 0; i < n; ++i)
printf(", %" PRIu64, p[i]);
printf(")\n");
}
int main(void)
{
F(3, 0, 1, 2);
F(2, 9, 8);
}
(Additionally, I changed the code to use the standard type uint64_t
. There is no need to create user-defined types such as u64
. It adds clutter and opportunity for confusion and bugs.)
You can also have the macro count the arguments:
/* Define a macro to convert its variable arguments to a compound literal
array and to pass that array to f.
*/
#define F(...) f(sizeof (uint64_t []) { __VA_ARGS__ } / sizeof (uint64_t), (uint64_t []) { __VA_ARGS__ })
…
F(0, 1, 2);
F(9, 8);