cfunctionfunction-pointersvariadic-functionshigher-order-functions

Passing a variadic function to a variadic function via a pointer


I'm kinda confused with the usage of <stdarg.h> features. I can't figure out how to properly pass a va_list to the argument function. Here's a simplified example of what I'm trying to achive:

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

void caller(int (*func)(size_t, ...), size_t n_args, ...) {
    va_list args;
    va_start(args, n_args);
    va_list args_for_func;
    va_copy(args_for_func, args);
    printf("(");
    for (size_t i = 0; i < n_args-1; i++) {
        printf("%i, ", va_arg(args, int));
    }
    printf("%i) => ", va_arg(args, int));
    int result = func(n_args, args_for_func);
    va_end(args_for_func);
    printf("%i", result);
    va_end(args);
}

int sum(size_t n_args, ...) {
    va_list args;
    va_start(args, n_args);
    int result = 0;
    for (size_t i = 0; i < n_args; i++) {
        result += va_arg(args, int);
    }
    va_end(args);
    return result;
}

int main() {
    // expected: (1, 2, 3, 4, 5) => 15
    // got: (1, 2, 3, 4, 5) => gibberish
    caller(sum, 5llu, 1, 2, 3, 4, 5);
}

Solution

  • A function can only process its variadic parameters using a va_list, and the parameters referenced by the va_list cannot be expanded out in a call to another variadic function. However, the va_list value can be passed by value to another function, so the way to solve the problem is to change the function pointer to point to a function that uses a va_list:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    
    void caller(int (*func)(size_t, va_list), size_t n_args, ...) {
        va_list args;
        va_start(args, n_args);
        va_list args_for_func;
        va_copy(args_for_func, args);
        printf("(");
        for (size_t i = 0; i < n_args-1; i++) {
            printf("%i, ", va_arg(args, int));
        }
        printf("%i) => ", va_arg(args, int));
        int result = func(n_args, args_for_func);
        va_end(args_for_func);
        printf("%i", result);
        va_end(args);
    }
    
    int vsum(size_t n_args, va_list args) {
        int result = 0;
        for (size_t i = 0; i < n_args; i++) {
            result += va_arg(args, int);
        }
        return result;
    }
    
    int sum(size_t n_args, ...) {
        va_list args;
        va_start(args, n_args);
        int result = vsum(n_args, args);
        va_end(args);
        return result;
    }
    
    int main() {
        caller(vsum, 5llu, 1, 2, 3, 4, 5);
    }