Home > Net >  How to assign variadic/variable arguments in C
How to assign variadic/variable arguments in C

Time:02-21

I'm trying to create a function to assign default or input values to several (scalar) parameters using variadic/variable input arguments as:

void set_params(const vector<double> &input, int n, ...) {  
  va_list args;
  va_start (args, n);
  for (int i = 0; i < n; i  ) {
    if (i < input.size()) {
      va_arg(args, int) = input[i];
    }
  }
  va_end(args);
}

int a = 1, b = 2, c = 3;
set_params({10, 20}, 3, a, b, c);

However, I'm getting the error on the assignment va_arg(args, int) = input[i]. Is it possible somehow to do assignment with variable arguments, or is there a better way to achieve this?

CodePudding user response:

Instead of using C's va_ stuff, C has it's own variadic template arguments, which you should preferably use

I'm no expert on this, but it could look a little bit like

#include <vector>
#include <iostream>

template <typename... Arg>
void set_params(const std::vector<double> &input, Arg&... arg) {
    unsigned int i{0};
    (
        [&] {
            if (i < size(input)) {
                arg = input[i  ];
            }
        }(), // immediately invoked lambda/closure object
        ...); // C  17 fold expression
}

int main() {
    int a = 1, b = 2, c = 3;
    set_params({10, 20}, a, b, c);

    std::cout
        << a << ' '
        << b << ' '
        << c << '\n';
}

CodePudding user response:

If you want to change a variable, that is passed as function parameter, you must use a pointer or a reference. For example the following function will have no effect, since x is passed by value and not by reference.

void set_zero(int x) {
    x = 0;
}

In C you have two options to pass something by reference. Either pass a pointer or pass a lvalue reference:

void set_zero(int *x) {
    *x = 0;
}

void set_zero(int &x) {
    x = 0;
}

Both of these will change the original variable to zero. I haven't found a way of passing a lvalue reference though a variadic function call, so I suggest you use pointers. In your case the code would look like

void set_params(const vector<double> &input, int n, ...) {  
    va_list args;
    va_start (args, n);
    for (int i = 0; i < n; i  ) {
        if (i < input.size()) {
            va_arg(args, int*) = input[i];
        }
    }
    va_end(args);
}

int a = 1, b = 2, c = 3;
set_params({10, 20}, 3, &a, &b, &c);

That beeing set, variadic arguments are a very unsafe way of passing variables. In your example, if someone changes the type of a, b, and c to another type, you will run into serious problems, as the type used in the set_params function no longer matches the type of the variables passed. You could do the whole function in a more safe way by e.g. using a vector also for the pointers.

template<typename T>
void set_params(const vector<T> &input, const vector<T*> &variables) {
    for ( int i = 0; i < variables.size(); i   ) {
        if ( i < input.size() ) {
            *variables[i] = input[i];
        } else {
            break;
        }
    }
}

int a = 1, b = 2, c = 3;
set_params<int>({10,20}, {&a, &b, &c});
  • Related