1

I'm trying to call a C function that takes variadic arguments, and I need to pass in a dynamic number of arguments to the function. Additionally, this is an API I can't modify without going to a lot of effort, so if there's any possible to make this work I'll take it.

As far as I can tell, va_list is not sufficient for this purpose since you can't pass a va_list where ... was written in the original function signature.

Answers for other questions like this one talk about how to pass a ... from one function to another, but that's not what I'm doing. I need to actually dynamically generate the list of arguments, I'm not getting it in as a .... Also, all the answers I can find rely being able to modify the function to take a va_list, which is not an option here.

There was also this question, which was marked as a dupe of the previous question despite not actually asking the same question. Again, the only answer suggests converting the call to use va_list which is not an option.

I would understand if the standard didn't include a way to do this, but this seems like a case where I can imagine a perfectly reasonable implementation. This isn't so different from, say, alloca. Anyway, if it's not possible, that's fine, but I'd like the SO community to realize that this isn't a dupe of this question and deserves a definitive answer.

The API I'm calling is in C, but if there's a way to do this in C++ I would consider it.

Edit: If you have these requirements, it looks like this answer using libffi is the best way to go. There does not appear to be a way to do this either in standard C or with GCC extensions otherwise.

Elliott Slaughter
  • 1,455
  • 19
  • 27
  • You might take a look at what libffi does as I think that needs to solve a similar problem. https://sourceware.org/libffi/ – Daniel Schepler Jun 20 '18 at 22:11
  • Are you sure there's no version of the C function that takes an array, similar to the relationship between `execl()` and `execv()`? There's no standard mechanism for dynamically creating a variadic argument list. – Barmar Jun 20 '18 at 23:04
  • This isn't a standard library function, it's an internal API, and the authors apparently didn't consider the use case with dynamic arguments. To be clear, it could be changed with some amount of effort, but due to non-technical factors I'm expecting the required effort to be high.... – Elliott Slaughter Jun 20 '18 at 23:18
  • I thought libffi was going to be a lot of work, but actually [this answer](https://stackoverflow.com/a/12267468/188046) makes it look not so bad. That may actually be the way to go here. – Elliott Slaughter Jun 20 '18 at 23:19
  • 1
    There is definitely no standard way of doing what you want to do, and `libffi` is the non-standard way of choice. But note that you have to use `ffi_prep_cif_var` to call variadic functions. – rici Jun 21 '18 at 00:02

2 Answers2

0

If you use gcc compiler, you can use __builtin_va_arg_pack() or __builtin_apply(), see here.

Examples:

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

int my_printf(const char *fmt, ...)
{
    void *args = __builtin_apply_args();
    void *ret = __builtin_apply((void(*)())printf, args, 100);
    __builtin_return(ret);
}

extern int my2_printf(const char *, ...);
extern inline __attribute__((__always_inline__,__gnu_inline__))
int my2_printf(const char *fmt, ...) {
    return printf(fmt, __builtin_va_arg_pack());
}

int main()
{
    my_printf("%s %s %s %s %s\n", "Hi", "I", "am", "kamil", "!");
    my2_printf("%s %s %s %s\n", "Variadic", "args", "are", "fun");
    return 0;
}

@edit:
As the question is also about on how to construct a dynamic argument list and then pass it to function. You can create a function for each case you need to support and use a big switch to differentiate between them.
A example for printing an array of unknown compile size:

#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>
#include <stdlib.h>
#include <boost/preprocessor/repetition/repeat.hpp>

void print_array(size_t cnt, ...)
{
        va_list va;
        va_start(va, cnt);
        printf("%s cnt=%d :", __func__, cnt);
        for (size_t i = 0; i < cnt; ++i) {
                printf(" %d", va_arg(va, int));
        }
        va_end(va);
        printf("%s\n");
}

// we support an array up to that many arguments
#define CALL_PRINT_ARRAY_MAX  128


#define CALL_PRINT_ARRAY_ARGS(z, n, text) \
        , arr[n]
#define CALL_PRINT_ARRAY_DECLARE(z, n, text) \
void call_print_array_##n (int arr[]) \
{ \
        print_array(n BOOST_PP_REPEAT(n, CALL_PRINT_ARRAY_ARGS, ())); \
}
BOOST_PP_REPEAT(CALL_PRINT_ARRAY_MAX, CALL_PRINT_ARRAY_DECLARE, ())
#undef CALL_PRINT_ARRAY_DECLARE
#undef CALL_PRINT_ARRAY_ARGS

int call_print_array(int arr[], size_t n)
{
        switch(n) {

#define CALL_PRINT_ARRAY_CASE(z, n, text) \
        case n: call_print_array_##n(arr); break;
                BOOST_PP_REPEAT(CALL_PRINT_ARRAY_MAX, CALL_PRINT_ARRAY_CASE, ())
#undef CALL_PRINT_ARRAY_CASE

        default:
                fprintf(stderr, "Array size is too big for what we're prepared\n");
                return -1;
        }
        return 0;
}

int main()
{
        //int a1[5] = {1,2,3,4,5};
        //call_print_array(a1, sizeof(a1)/sizeof(a1[0]));

        size_t size;
        scanf("%zu", &size); // insecure
        int * a2 = calloc(size, sizeof(*a2));
        for (size_t i = 0; i < size; ++i)
                a2[i] = i;
        call_print_array(a2, size);


        return 0;
}
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • This looks like an excellent answer to [this question](https://stackoverflow.com/q/3530771/188046). However, as best I can tell, this only allows you to forward varargs from the current function to a callee. For my use case, I'm actually dynamically building the argument list, it's not coming in as a parameter list. – Elliott Slaughter Jun 20 '18 at 22:48
  • Can you provide an example? I don't know what you mean by "dynamically" building the argument list. You can have one function witch "transposes" the arguments and which then calls the function which calls __builtin_apply(). – KamilCuk Jun 20 '18 at 22:55
  • (This is a toy example, but illustrates the point.) Suppose you have a function that dynamically allocates and returns something like an array of values. The values are computed in some arbitrary way by the inputs to the program. Are the `__builtin_*` functions sufficient to convert an array to varargs? I don't see a way to do that with those APIs. (Note in the real application, the values are not all of the same type, though they do have some structure.) – Elliott Slaughter Jun 20 '18 at 23:26
  • No. Puff, an array is a good example. To do this in C you need to create a switch for each case. Arguments passed to functions are resolved compile time. You can't `push_back` an argument, at least I haven't heard of it. – KamilCuk Jun 21 '18 at 00:05
-1

The easiest way to do this would be with variadic macros, kind of like the way it's done for eprintf:

#define eprintf(…) fprintf (stderr, __VA_ARGS__)

__VA_ARGS__ expands into the full argument list passed into the macro with which you can then call the other function declared with .... Unfortunately what you want to do cannot be done within a function without going through a lot of effort that you seem to want to avoid.

mnistic
  • 10,866
  • 2
  • 19
  • 33
  • 1
    Doesn't this require the argumens to known at compile time? I.e. `__VA_ARGS__` is a feature of the C preprocessor, not of the C language itself. Unfortunately, in my use case, I actually need to be able to build these dynamically. – Elliott Slaughter Jun 20 '18 at 22:38
  • Yep, this is compile time. – mnistic Jun 20 '18 at 22:42