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

4

12

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++?

Hobnail answered 26/8, 2017 at 1:34 Comment(1)
Why don't you spend some time learning how variadic parameters work, and then you'll be in a much better position to judge the relative metrics of both approaches. This will give you far more information than a brief answer on stackoverflow.com.Upgrowth
L
7

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).

Large answered 26/8, 2017 at 1:41 Comment(3)
I have a small concern. Must template functions be defined in header files? Can't I declare it in a header and define it elsewhere (that will be linked later)?Hobnail
@iBug: that is another question, but most of the time the answer is no. However, read about extern in templates.Artery
If only messing up the type in varargs always resulted in a segfault...Washstand
B
4

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

Beshore answered 26/8, 2017 at 2:8 Comment(0)
R
3

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.

Rok answered 26/8, 2017 at 2:35 Comment(0)
M
-1

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

Munmro answered 26/8, 2017 at 10:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.