Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

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

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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.

>Solution :

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);
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading