将动态可变参数集传递给C函数

时间:2018-06-20 22:07:45

标签: c variadic-functions

我试图调用一个带有可变参数的C函数,我需要将动态数量的参数传递给该函数。另外,这是一个我不费吹灰之力就无法修改的API,因此,如果有可能进行此工作,我会接受。

据我所知,va_list不足以实现此目的,因为您无法传递在原始函数签名中写入了va_list的{​​{1}}。

诸如this one之类的其他问题的答案讨论了如何将...从一个函数传递给另一个函数,但这不是我正在做的。我实际上需要动态生成参数列表,而没有以...的形式获得它。另外,我能找到的所有答案都取决于能否将函数修改为...,在这里不是一个选择。

还有this question,尽管实际上没有问相同的问题,但它被标记为上一个问题的重复。同样,唯一的答案是建议将呼叫转换为使用va_list,这不是一个选择。

我会理解该标准是否不包含执行此操作的方法,但是这似乎是我可以想象完全合理的实现的情况。与va_list没什么不同。无论如何,如果不可能的话,那很好,但是我希望SO社区意识到这不是对this question的欺骗,应该给出明确的答案。

我正在调用的API在C语言中,但是如果有一种方法可以在C ++中使用,我会考虑的。

编辑:如果您有这些要求,看来this answer using libffi是最好的选择。在标准C或GCC扩展中,似乎没有办法做到这一点。

2 个答案:

答案 0 :(得分:0)

最简单的方法是使用variadic macros,有点像对eprintf进行的操作:

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

__VA_ARGS__扩展到传递到宏的完整参数列表,然后您可以使用该宏调用用...声明的另一个函数。不幸的是,如果不付出很多您似乎想避免的努力,就无法在一个函数中完成您想做的事情。

答案 1 :(得分:0)

如果使用gcc编译器,则可以使用__builtin_va_arg_pack()或__builtin_apply(),请参见here

示例:

#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:
问题还在于如何构造动态参数列表,然后将其传递给函数。您可以为需要支持的每种情况创建一个函数,并使用较大的开关来区分它们。
打印未知编译大小的数组的示例:

#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;
}