Search code examples
ctypestype-conversionargumentsvariadic-functions

Variadic function with arguments of different type passed to it


Say I declare a variadic function in the following way:

#include <stdio.h>
#include <stdarg.h>

void variadic(char *def, ...) {
    va_list args;
    va_start (args, def);
    char *arg;
    while (arg = va_arg(args, char*)) {
        printf("%s\n", arg);
    }
    va_end (args);
}

int main(int argc, char **argv) {
    variadic("x", "abcdef", "ghijklmnop", "qrstuv", "wxyz");
}

Is it possible to call the function variadic by passing arguments of a different type other than char* like so:

variadic("blah", 200, 45.3, "some string", some_struct, some_file_descriptor);

If this is possible could someone provide an example of the variadic function performing this logic.


Solution

  • Yes, passing the expected types as first parameter, you have a nice example in https://en.cppreference.com/w/c/variadic

    #include <stdio.h>
    #include <stdarg.h>
     
    void simple_printf(const char* fmt, ...)
    {
        va_list args;
        va_start(args, fmt);
     
        while (*fmt != '\0') {
            if (*fmt == 'd') {
                int i = va_arg(args, int);
                printf("%d\n", i);
            } else if (*fmt == 'c') {
                // A 'char' variable will be promoted to 'int'
                // A character literal in C is already 'int' by itself
                int c = va_arg(args, int);
                printf("%c\n", c);
            } else if (*fmt == 'f') {
                double d = va_arg(args, double);
                printf("%f\n", d);
            }
            ++fmt;
        }
     
        va_end(args);
    }
     
    int main(void)
    {
        simple_printf("dcff", 3, 'a', 1.999, 42.5); 
    }
    

    As pointed out by @goodvibration, a more flexible approach can be an array, and since C99 you can use compound literals, an enum can do the job:

    #include <stdio.h>
    #include <stdarg.h>
    
    typedef enum {
        VAR_END,
        VAR_CHAR,
        VAR_INT,
        VAR_DOUBLE,
        /* ...  VAR_YOUR_OWN_TYPES */
    } VAR_TYPE;
    
    void simple_printf(const VAR_TYPE type[], ...)
    {
        va_list args;
        va_start(args, type);
     
        int counter = 0;
    
        while (type[counter] != VAR_END) {
            switch (type[counter]) {
                case VAR_INT:
                {
                    int i = va_arg(args, int);
                    printf("%d\n", i);
    
                }
                break;
                case VAR_CHAR:
                {
                    int c = va_arg(args, int);
                    printf("%c\n", c);
                }
                break;
                case VAR_DOUBLE:
                {
                    double d = va_arg(args, double);
                    printf("%f\n", d);
                }
                break;
                case VAR_END:
                    break;
            }
            ++counter;
        }
     
        va_end(args);
    }
     
    int main(void)
    {
        simple_printf(
            (VAR_TYPE[]){VAR_INT, VAR_CHAR, VAR_DOUBLE, VAR_DOUBLE, VAR_END} ,
            3, 'a', 1.999, 42.5
        ); 
    }