假设我有一个C函数,它有一个可变数量的参数:我如何调用另一个函数,它期望从它内部有一个可变数量的参数,传递进入第一个函数的所有参数?

例子:

void format_string(char *fmt, ...);

void debug_print(int dbg_lvl, char *fmt, ...) {
    format_string(fmt, /* how do I pass all the arguments from '...'? */);
    fprintf(stdout, fmt);
 }

当前回答

我不确定这是否适用于所有编译器,但到目前为止对我来说是有效的。

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

你可以添加…到inner_func()如果你想,但你不需要它。它可以工作,因为va_start使用给定变量的地址作为起始点。在这种情况下,我们在func()中给它一个变量的引用。所以它使用这个地址并在堆栈中读取后面的变量。inner_func()函数从func()的堆栈地址中读取。因此,只有当两个函数使用相同的堆栈段时,它才有效。

如果你给va_start和va_arg宏一个变量作为起始点,它们通常会工作。如果你愿意,你也可以把指针传递给其他函数。您可以很容易地创建自己的宏。宏所做的就是对内存地址进行类型转换。然而,让它们适用于所有编译器和调用约定是很烦人的。所以使用编译器自带的通常更容易。

其他回答

可以对函数调用使用内联汇编。(在这段代码中,我假设参数是字符)。

void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
    {
        va_list argumentsToPass;
        va_start(argumentsToPass, fmt);
        char *list = new char[numOfArgs];
        for(int n = 0; n < numOfArgs; n++)
            list[n] = va_arg(argumentsToPass, char);
        va_end(argumentsToPass);
        for(int n = numOfArgs - 1; n >= 0; n--)
        {
            char next;
            next = list[n];
            __asm push next;
        }
        __asm push fmt;
        __asm call format_string;
        fprintf(stdout, fmt);
    }

我不确定这是否适用于所有编译器,但到目前为止对我来说是有效的。

void inner_func(int &i)
{
  va_list vars;
  va_start(vars, i);
  int j = va_arg(vars);
  va_end(vars); // Generally useless, but should be included.
}

void func(int i, ...)
{
  inner_func(i);
}

你可以添加…到inner_func()如果你想,但你不需要它。它可以工作,因为va_start使用给定变量的地址作为起始点。在这种情况下,我们在func()中给它一个变量的引用。所以它使用这个地址并在堆栈中读取后面的变量。inner_func()函数从func()的堆栈地址中读取。因此,只有当两个函数使用相同的堆栈段时,它才有效。

如果你给va_start和va_arg宏一个变量作为起始点,它们通常会工作。如果你愿意,你也可以把指针传递给其他函数。您可以很容易地创建自己的宏。宏所做的就是对内存地址进行类型转换。然而,让它们适用于所有编译器和调用约定是很烦人的。所以使用编译器自带的通常更容易。

你也可以试试宏。

#define NONE    0x00
#define DBG     0x1F
#define INFO    0x0F
#define ERR     0x07
#define EMR     0x03
#define CRIT    0x01

#define DEBUG_LEVEL ERR

#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...)  fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...)  if((DEBUG_LEVEL & X) == X) \
                                      DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)

int main()
{
    int x=10;
    DEBUG_PRINT(DBG, "i am x %d\n", x);
    return 0;
}

在华丽的c++ 11中,你可以使用可变模板:

template <typename... Ts>
void format_string(char *fmt, Ts ... ts) {}

template <typename... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts... ts)
{
  format_string(fmt, ts...);
}

罗斯的解决方案清理了一点。只有当所有参数都是指针时才有效。此外,如果__VA_ARGS__为空,语言实现必须支持省略前面的逗号(Visual Studio c++和GCC都支持)。

// pass number of arguments version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}


// NULL terminated array version
 #define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}