(GCC/Clang) Enforce type on variadic argument list?

51 Views Asked by At

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.

1

There are 1 best solutions below

0
Eric Postpischil On BEST ANSWER

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);