iBug
iBug

Reputation: 37307

Is va_list still used in C++? Or is it encouraged to use template<typename... T>?

In C, the only way to define a variable length of arguments is declaring its prototype with a ellipsis and use va_list, va_start, va_arg, va_end to extract them. Just like the printf series and the scanf series do.

In C++11, a new approach was introduced as below.

template <typename T, typename... MoreT>
void func(T arg, MoreT... args){
    // Do some stuff
    func(args);
}

What are the benefits and downsides of each method? Is either of them discouraged to be used or encouraged in C++?

Upvotes: 12

Views: 4466

Answers (4)

pepero
pepero

Reputation: 7513

c varadic functions are implementation specific, and most probably macros, so they have no type safety. more info. varadic, e.g.

#define va_arg(list, mode) ((mode *)(list = (char *)list + sizeof(mode)))[-1]

as developer, you have to define conventions for users, do all these steps with

va_list, va_start, va_arg, va_end.

On the other hand, c++1x provides more facilities to address this, e.g. with type traits, you could check if it is integral, initializer_list to make expansion, etc. All together you could do a lot of powerful work

Upvotes: -1

zneak
zneak

Reputation: 138211

There aren't a lot of benefits to variadic parameter functions, but there are some.

If you use a C variadic function, you get one compiled function that handles all the cases. If you use a C++ function with variadic templates, you get one compiled function per parameter combination. If code size is a consideration for your project, this can be a problem.

C++ variadic templates are type-safe, whereas C variadic functions are not. This means that the compiler usually won't enforce the type of the parameters that a function passes to a variadic function. GCC and Clang support some attributes that address common cases:

  • __attribute__((format())) tells the compiler that the variadic parameters use the printf/scanf convention, and will warn you when parameters don't match the format string that you've passed;
  • __attribute__((sentinel(N))) tells the compiler that the last variadic parameter should be the sentinel value.

It's still very much possible to screw up. For instance, the open function is variadic and requires a mask parameter as a third parameter when O_CREAT is specified, but it's frequently forgotten and neither of these __attribute__ extensions can address that.

The standard permits, but does not mandate, that implementations allow to pass non-POD types to C variadic functions; and when it does work, the semantics are implementation-defined. This means that you shouldn't do it if you want your code to be cross-platform.

Finally, it is typically harder to learn how to do C++ variadic templates right than it is to learn how to do C variadic functions.

Upvotes: 3

NP Rooski  Z
NP Rooski Z

Reputation: 3657

C++ strive to be more type safe than C e.g. nullptr, enum class provide more type-safe code. Compiler is your more intelligent friend.

Variadic template allow you to be more specific about type at compile-time itself while arguments in va_list et.al. C functions are evaluated at run-time

Upvotes: 4

Nir Friedman
Nir Friedman

Reputation: 17714

C style variadic functions are heavily discouraged in C++. Styles vary but writing those kinds of functions will get you stoned in some circles (including mine), unless there's a truly exceptional reason.

As far as the trade-offs go, C style variadic functions are completely type unsafe. You could try to extract something from the variadic pack as the wrong type, which will result in a segfault. C++ variadic templates are strongly typed so this is not possible (unless of course you absolutely force it with a reinterpret_cast or something like that).

Beyond that, the C++ code will also typically (there are rare exceptions due to code bloat) perform better at run time. There's less indirection, more information for the compiler to work with. However, there may be longer compiler times, especially since variadic template functions (like all templates) generally must be defined in the header file, whereas C style variadics can be defined in the .cpp file.

In most C or C++ code (which is written for very high performance applications), the order of priorities would usually be correctness, then performance, then compile time. So most C++ devs believe that variadic templates are clear, clear winners here.

It is basically very similar to the comparison between a proper generic container in C++ using templates, and a void* based container in C++. Type safety + runtime performance, vs compile time performance (and .h vs .cpp).

Upvotes: 7

Related Questions