3

Which disadvantages could I have if I want to use the function

foo(int num, ...)

to implement the variable number of arguments?

I do know the first disadvantage that you can only use one data type.

Is there any way else to do that?

0

7 Answers 7

7

You are not restricted to arguments of one data type; the printf() family of functions in C (and C++) belies that rumour.

The primary disadvantage of the ellipsis notation is that you lose type safety; the compiler cannot tell you when you are using an argument of the wrong type. (The Go programming language allows you to specify that a function takes an arbitrary number of parameters of a single type - which is an interesting idea.)

Inside the function, there must be some way for it to tell how many arguments were provided and what the types are. Referring back to printf() again, the format string tells it what other arguments are expected. Modern compilers know about these format strings and can check that the arguments given match the format string (when the format string is a literal). This allows for some type safety after all - but that won't be available to you. Using a count is one way of handling it - but then you wonder why you aren't using a vector<T> or something similar to pass the data in. Another classic way is to have a marker value - typically a null pointer - at the end of the list of inputs.

So, you often don't need the variadic argument list. When you do use one, you typically leave yourself open to making errors that other mechanisms avoid.

Sign up to request clarification or add additional context in comments.

7 Comments

+1 Also, re arbitrary number of parameters of a single type---is that any different from Java 5 varargs, and in other words, how interesting is it compared to the latter? :-)
Java 5 doesn't actually have varargs. All it does is turn them into an array. It's syntactic sugar. In C or C++ you can also take an array and if you want multiple types you can take an array of boost::variant/any.
@Chris: since I wasn't aware of Java 5's varargs, I had to go and look to see what it offers. The Go facility is essentially isomorphic with what is in Java 5.
@DeadMG: Java's handling of varargs isn't so different from how Go (thanks Jonathan!), Python, Ruby, Scheme, etc. handle it. So while it's not like how it's done in C, that's no disadvantage.
I didn't mean to suggest that it's a disadvantage. C's varargs are the worst idea ever because they couldn't come up with anything vaguely intelligent to handle the problem. I'm just saying that Java 5's varargs aren't varargs at all- they're arrays that are syntactically transformed. You can achieve the same effect in C by taking a char*, int.
|
4

See this question. The biggest problem here is type-safety. You're going to extract the parameters at runtime rather than compile-time. You will implement the logic for that, rather than the compiler. This means that there's a phenomenally higher chance for error. Not only that, but your code is going to be infested with irrelevant logic that should really be done for you by the compiler. You are not limited to one parameter type, but nevertheless this is not an advantage.

In C++ there are a number of alternative ways to go about this, many of which are better in every way than the ellipsis notation. See that other question for a few ideas. The classical example is in the C++ iostreams, in contrast of printf() of C:

std::cout << 'I' << " love h" << 3 << "r\n";

Never mind the flaws of the library, its use of the insertion operator is one its brightest uses of C++.

3 Comments

@wihelmetell: I agree what you said. About type-safety, if you know you have only one data type. Is there still any problem by type-safety?
If the compiler knows the type of a value then there is no need to cast, and therefore there is no type-safety issue. But in C++ the compiler doesn't know the type of varargs, simply because the language doesn't guarantee that. C++ doesn't even provide a syntactic way for you to tell the compiler that a sequence of varargs is of a single type. One type or many, you still have to cast inside the function called, and therefore you have a type-safety issue.
That said, printf() is smarter than any other function in this regard. The compiler understands the special tokens in the argument string, if the string is a literal, and so can check the types for safety. But this is not a standard guarantee, and you can't get this sort of compiler checks in your own code if you use varargs.
2

There are multiple ways NOT to use ellipsis notation.

Why ? Because of type safety a hazardous manipulations of the primitives (va_start, va_arg, va_next) that you can't really forward to another function etc...

However, contrary to C, C++ provides template methods, which offer type safety and generic behavior, and this can be cumulated with overloads:

template <typename Arg0>
void foo(int num, Arg0 const& arg0);

template <typename Arg0, typename Arg1>
void foo(int num, Arg0 const& arg0, Arg1 const& arg1);

// ... etc

This is the current state of the art, which is generally helped by a subtle application of Preprocessor Programming (check out Boost.Preprocessor).

With the new C++0x standard, come the variadic templates, which offer the same facilities than the C variadic methods, with type safety offered (yeeha)

template <typename Arg0, typename... Args>
void foo(Arg0 arg0, Args... args)
{
  // Do something with arg0
  foo(args);
}

template <typename Arg0>
void foo(Arg0 arg0)
{
  // Do something with arg0
}

This also allows to define tuple classes much more easily :)

4 Comments

this seems really a pretty solution. However, I still can't understand how I can use with this approach to deal with arbitrary parameters. related to your code, how can I call the foo function with arbitrary parameters
Because it's a template function, and thus it works with multiple different types. From the signature though we note the type need to be copyable, but you can use Arg0 const& if your usecase works with it.
is there any compilable example available which demonstrates the variadic template!!
It's a C++0x feature, so first your compiler need to implement it and second you need to turn on C++0x compatibility (on gcc there should be something like --std=c++0x or something). Afterward you can check wikipedia for example, I mostly work from the top off my head.
0
void myprintf(char* fmt, ...)
{
    va_list args;
    va_start(args,fmt);
    vprintf(fmt,args);
    va_end(args);
}

int _tmain(int argc, _TCHAR* argv[])
{
    int a = 9;
    int b = 10;
    char v = 'C'; 
    myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n",a, v, b);
    return 0;
}

Comments

0

A decent C++ alternative is something like

foo(blah, ArgList(a)(b)(c));

where ArgList is a class which has an overloaded operator (), and foo is a function which takes a ArgList. This is a concise way to pass variable arguments to a function. Depending on your requirements for different types you can design it however you want.

Or something like

foo(blah)(a)(b)(c);

where foo is a class with overloaded operator (). Here you create a temporary, and the destructor will be called after the semicolon.

Comments

0

You can use Loki's Functor Library that uses type list for variable number of argument to a function that is type safe as well.

Comments

0

In addition to the type safety issues already touched on, you can't pass non-POD types as a vararg at all.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.