传递变量数量的参数

时间:2022-02-03 04:18:27

Say I have a C function which takes a variable number of arguments: How can I call another function which expects a variable number of arguments from inside of it, passing all the arguments that got into the first function?

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

Example:

例子:

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

9 个解决方案

#1


187  

To pass the ellipses on, you have to convert them to a va_list and use that va_list in your second function. Specifically;

要传递省略号,必须将它们转换为va_list,并在第二个函数中使用va_list。特别地;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}

#2


51  

Variadic Functions can be dangerous. Here's a safer trick:

可变的函数是危险的。这里有一个安全的技巧:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

#3


50  

There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.

如果不知道要传递多少参数,就无法调用printf(例如),除非您想玩一些淘气的、不可移植的技巧。

The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.

普遍采用的解决方案是提供另一种形式的可变长度的函数,所以printf函数需要一个va_list ....的…版本只是围绕va_list版本的包装器。

#4


28  

In magnificent C++0x you could use variadic templates:

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

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...);
}

#5


6  

You can use inline assembly for the function call. (in this code I assume the arguments are characters).

可以使用内联程序集进行函数调用。(在这段代码中,我假设参数是字符)。

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

#6


1  

Ross' solution cleaned-up a bit. Only works if all args are pointers. Also language implementation must support eliding of previous comma if __VA_ARGS__ is empty (both Visual Studio C++ and GCC do).

罗斯的溶液洗干净了一点。只有当所有的args都是指针时才有效。如果__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);}

#7


1  

You can try macro also.

你也可以试试宏。

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

#8


-1  

Let's say you have a typical variadic function you've written. Because at least one argument is required before the variadic one ..., you have to always write an extra argument in usage.

假设你有一个典型的变量函数。因为在变量1之前至少需要一个参数。在使用时,您必须编写一个额外的参数。

Or do you?

还是你?

If you wrap your variadic function in a macro, you need no preceding arg. Consider this example:

如果在宏中包装变量函数,就不需要前面的arg。考虑一下这个例子:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

This is obviously far more convenient, since you needn't specify the initial argument every time.

这显然要方便得多,因为您不必每次都指定初始参数。

#9


-4  

I'm unsure if this works for all compilers, but it has worked so far for me.

我不确定这是否适用于所有的编译器,但是到目前为止它对我来说已经起作用了。

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

You can add the ... to inner_func() if you want, but you don't need it. It works because va_start uses the address of the given variable as the start point. In this case, we are giving it a reference to a variable in func(). So it uses that address and reads the variables after that on the stack. The inner_func() function is reading from the stack address of func(). So it only works if both functions use the same stack segment.

你可以加上…如果您想要,可以使用inner_func(),但是不需要它。之所以有效,是因为va_start使用给定变量的地址作为起点。在本例中,我们将它引用到func()中的变量。它使用那个地址然后读取堆栈上的变量。inner_func()函数正在读取func()的堆栈地址。因此,只有当两个函数使用相同的堆栈段时,它才能工作。

The va_start and va_arg macros will generally work if you give them any var as a starting point. So if you want you can pass pointers to other functions and use those too. You can make your own macros easily enough. All the macros do is typecast memory addresses. However making them work for all the compilers and calling conventions is annoying. So it's generally easier to use the ones that come with the compiler.

如果您将任何var作为起点,那么va_start和va_arg宏通常都可以工作。如果你想,你可以把指针传递给其他函数,也可以用它们。您可以轻松地创建自己的宏。所有的宏所做的就是排版内存地址。然而,让它们为所有编译器工作和调用约定是令人讨厌的。所以使用编译器附带的代码通常更容易。

#1


187  

To pass the ellipses on, you have to convert them to a va_list and use that va_list in your second function. Specifically;

要传递省略号,必须将它们转换为va_list,并在第二个函数中使用va_list。特别地;

void format_string(char *fmt,va_list argptr, char *formatted_string);


void debug_print(int dbg_lvl, char *fmt, ...) 
{    
 char formatted_string[MAX_FMT_SIZE];

 va_list argptr;
 va_start(argptr,fmt);
 format_string(fmt, argptr, formatted_string);
 va_end(argptr);
 fprintf(stdout, "%s",formatted_string);
}

#2


51  

Variadic Functions can be dangerous. Here's a safer trick:

可变的函数是危险的。这里有一个安全的技巧:

   void func(type* values) {
        while(*values) {
            x = *values++;
            /* do whatever with x */
        }
    }

func((type[]){val1,val2,val3,val4,0});

#3


50  

There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.

如果不知道要传递多少参数,就无法调用printf(例如),除非您想玩一些淘气的、不可移植的技巧。

The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.

普遍采用的解决方案是提供另一种形式的可变长度的函数,所以printf函数需要一个va_list ....的…版本只是围绕va_list版本的包装器。

#4


28  

In magnificent C++0x you could use variadic templates:

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

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...);
}

#5


6  

You can use inline assembly for the function call. (in this code I assume the arguments are characters).

可以使用内联程序集进行函数调用。(在这段代码中,我假设参数是字符)。

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

#6


1  

Ross' solution cleaned-up a bit. Only works if all args are pointers. Also language implementation must support eliding of previous comma if __VA_ARGS__ is empty (both Visual Studio C++ and GCC do).

罗斯的溶液洗干净了一点。只有当所有的args都是指针时才有效。如果__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);}

#7


1  

You can try macro also.

你也可以试试宏。

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

#8


-1  

Let's say you have a typical variadic function you've written. Because at least one argument is required before the variadic one ..., you have to always write an extra argument in usage.

假设你有一个典型的变量函数。因为在变量1之前至少需要一个参数。在使用时,您必须编写一个额外的参数。

Or do you?

还是你?

If you wrap your variadic function in a macro, you need no preceding arg. Consider this example:

如果在宏中包装变量函数,就不需要前面的arg。考虑一下这个例子:

#define LOGI(...)
    ((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))

This is obviously far more convenient, since you needn't specify the initial argument every time.

这显然要方便得多,因为您不必每次都指定初始参数。

#9


-4  

I'm unsure if this works for all compilers, but it has worked so far for me.

我不确定这是否适用于所有的编译器,但是到目前为止它对我来说已经起作用了。

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

You can add the ... to inner_func() if you want, but you don't need it. It works because va_start uses the address of the given variable as the start point. In this case, we are giving it a reference to a variable in func(). So it uses that address and reads the variables after that on the stack. The inner_func() function is reading from the stack address of func(). So it only works if both functions use the same stack segment.

你可以加上…如果您想要,可以使用inner_func(),但是不需要它。之所以有效,是因为va_start使用给定变量的地址作为起点。在本例中,我们将它引用到func()中的变量。它使用那个地址然后读取堆栈上的变量。inner_func()函数正在读取func()的堆栈地址。因此,只有当两个函数使用相同的堆栈段时,它才能工作。

The va_start and va_arg macros will generally work if you give them any var as a starting point. So if you want you can pass pointers to other functions and use those too. You can make your own macros easily enough. All the macros do is typecast memory addresses. However making them work for all the compilers and calling conventions is annoying. So it's generally easier to use the ones that come with the compiler.

如果您将任何var作为起点,那么va_start和va_arg宏通常都可以工作。如果你想,你可以把指针传递给其他函数,也可以用它们。您可以轻松地创建自己的宏。所有的宏所做的就是排版内存地址。然而,让它们为所有编译器工作和调用约定是令人讨厌的。所以使用编译器附带的代码通常更容易。