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?

helios
  • 13,574
  • 2
  • 45
  • 55
sami
  • 467
  • 2
  • 6
  • 11

7 Answers7

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • +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? :-) – C. K. Young Sep 02 '10 at 13:03
  • 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. – Puppy Sep 02 '10 at 13:29
  • 1
    @Chris: since I wasn't aware of Java 5's [varargs](http://www.deitel.com/articles/java_tutorials/20060106/VariableLengthArgumentLists.html), I had to go and look to see what it offers. The [Go](http://golang.org/doc/go_spec.html#Passing_arguments_to_..._parameters) facility is essentially isomorphic with what is in Java 5. – Jonathan Leffler Sep 02 '10 at 13:29
  • @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. – C. K. Young Sep 02 '10 at 13:33
  • 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. – Puppy Sep 02 '10 at 14:11
  • @DeadMG: If you want to call it syntactic sugar for arrays, you're welcome to, but understand that the varargs mechanism for Go, Python, Ruby, Scheme, and probably other languages are _all_ handled in the same way, and is in no way unique to Java. Except, perhaps, that the packing of "rest" arguments into an array/list is done by the runtime for the other languages, whereas in Java it's transformed at the caller level. Maybe that was your point? \*shrug\* – C. K. Young Sep 02 '10 at 15:34
  • @Chris: You're totally missing the point. The point is that you can have variable length arguments like in Java using an array all you like. All the rest of what you've said is completely and totally irrelevant. – Puppy Sep 02 '10 at 17:21
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++.

Community
  • 1
  • 1
wilhelmtell
  • 57,473
  • 20
  • 96
  • 131
  • @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? – sami Sep 03 '10 at 08:48
  • 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. – wilhelmtell Sep 03 '10 at 14:06
  • 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. – wilhelmtell Sep 03 '10 at 14:08
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 :)

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 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 – sami Sep 02 '10 at 13:36
  • 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. – Matthieu M. Sep 02 '10 at 15:38
  • is there any compilable example available which demonstrates the variadic template!! – sami Sep 03 '10 at 08:34
  • 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. – Matthieu M. Sep 03 '10 at 11:59
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;
}
C. K. Young
  • 219,335
  • 46
  • 382
  • 435
Pramendra Gupta
  • 14,667
  • 4
  • 33
  • 34
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.

tenfour
  • 36,141
  • 15
  • 83
  • 142
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.

Sudhendu Sharma
  • 383
  • 1
  • 10
0

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

Mark B
  • 95,107
  • 10
  • 109
  • 188