Are There Gotchas Using Varargs with Reference Parameters

Are there gotchas using varargs with reference parameters

If you look at what va_start expands out to, you'll see what's happening:

va_start(argptr, format); 

becomes (roughly)

argptr = (va_list) (&format+1);

If format is a value-type, it gets placed on the stack right before all the variadic arguments. If format is a reference type, only the address gets placed on the stack. When you take the address of the reference variable, you get the address or the original variable (in this case of a temporary AnsiString created before calling Broken), not the address of the argument.

If you don't want to pass around full classes, your options are to either pass by pointer, or put in a dummy argument:

AnsiString working_ptr(const AnsiString *format,...)
{
ASSERT(format != NULL);
va_list argptr;
AnsiString buff;

va_start(argptr, format);
buff.vprintf(format->c_str(), argptr);

va_end(argptr);
return buff;
}

...

AnsiString format = "Hello %s";
s1 = working_ptr(&format, "World");

or

AnsiString working_dummy(const AnsiString &format, int dummy, ...)
{
va_list argptr;
AnsiString buff;

va_start(argptr, dummy);
buff.vprintf(format.c_str(), argptr);

va_end(argptr);
return buff;
}

...

s1 = working_dummy("Hello %s", 0, "World");

varargs(va_list va_start) doesn't work with pass-by-reference parameter

You cannot use references with va_start according to C++ Standard 18.7/3:

The restrictions that ISO C places on the second parameter to the va_start() macro in header
are different in this International Standard. The parameter parmN is the identifier of the
rightmost parameter in the variable parameter list of the function definition (the one just before the ...).
If the parameter parmN is declared with a function, array, or reference type, or with a type that is not compatible
with the type that results when passing an argument for which there is no parameter, the behavior is
undefined.

Problem passing a reference as a named parameter to a variadic function

VS2005 also crashes on it.

The problem is that va_start uses the address of the argument given to it, and since str is a reference, its address is the address of the "one" variable defined int the caller, not the address on the stack.

I see no way of getting the address of the stack-variable (the argument that actually contains the address of "one" that is being passed), but there are some work arounds:

  • Instead of "const char * &str", use "const char *str" or "const char **str"
  • Add the next argument also to the "fixed" argument list

This code illustrates the second alternative:

void foo(const char* &str, const char *arg1, ...) {
if (arg1) {
va_list args;
va_start(args, arg1);
printf ("%s\n", arg1);
const char* foo;
while((foo = va_arg(args, const char*)) != NULL) {
printf("%s\n", foo);
}
}
}

When do you use varargs in Java?

Varargs are useful for any method that needs to deal with an indeterminate number of objects. One good example is String.format. The format string can accept any number of parameters, so you need a mechanism to pass in any number of objects.

String.format("This is an integer: %d", myInt);
String.format("This is an integer: %d and a string: %s", myInt, myString);

varargs as input parameter to a function in java 8

You cannot use the varargs syntax in this case as it's not a method parameter.

Depending on what you're using the Function type for, you may not even need it at all and you can just work with your methods as they are without having to reference them through functional interfaces.

As an alternative you can define your own functional interface like this:

@FunctionalInterface
public interface MyFunctionalInterface<T, R> {
R apply(T... args);
}

then your declaration becomes:

MyFunctionalInterface<String, String> doWork = a -> doSomethingWithArray(a);

and calling doWork can now be:

String one = doWork.apply("one");
String two = doWork.apply("one","two");
String three = doWork.apply("one","two","three");
...
...

note - the functional interface name is just a placeholder and can be improved to be consistent with the Java naming convention for functional interfaces e.g. VarArgFunction or something of that ilk.

How to assign variadic/variable arguments in C++

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

Can I pass an array as arguments to a method with variable arguments in Java?

The underlying type of a variadic method function(Object... args) is function(Object[] args). Sun added varargs in this manner to preserve backwards compatibility.

So you should just be able to prepend extraVar to args and call String.format(format, args).

Cause of Variadic Arguments being corrupted/null

According to the C++ standard [support.runtime], when discussing the second parameter to va_start:

The parameter parmN is the identifier of the rightmost parameter in the variable parameter list of the function definition (the one just before the ...). If the parameter parmN is of a reference type, or of a type that is not compatible with the type that results when passing an argument for which there is no parameter, the behavior is undefined.

Emphasis mine. Your format parameter is a reference type, hence its use as the last parameter before the ... results in undefined behavior.

Using Varargs, can I assign values to variables based on the variable name?

I answer your actual question below, but answering the underlying desire of the question, you're probably looking for the Builder pattern:

Foo foo = new Foo.Builder().id(id).name(name).build();
Foo foo = new Foo.Builder().name(name).id(id).isFoo(isFoo).build();

It's one of the original Gang of Four patterns.

(In the above I made Builder a nested class within Foo, as the builder class and the thing it builds tend to be intimately related. But that's not necessary, it's just one way it's done.)


...can I assign values to variables based on the variable name?

No. The variables are not passed to your method. The value contained in the variables is read from them, and that value is passed to your method. By the time it gets to your method, there is no connection whatsoever back to the variable the value came from (if, in fact, it came from a variable).

In your specific example, since each of them has a distinct type, you could check each of the entries in args to see what its type is and work from that, but it would be fairly odd and I wouldn't recommend it.

Emphasizing that I wouldn't recommend it, that would look like:

for (Object arg : args) {
if (arg instanceof Integer) {
this.id = (Integer)arg;
} else if (arg instanceof String) {
this.name = (String)arg;
} else if (arg instanceof Date) {
this.isFoo = (Date)arg;
}
}

But again, I wouldn't recommend it, and if I came across it in a code review I'd want a really, really good justification for it. :-)



Related Topics



Leave a reply



Submit