Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Specifying one type for all arguments passed to variadic function or variadic template function w/out using array, vector, structs, etc?

This question is about guaranteeing all arguments are of the same type while exhibiting a reject-early behavior with a clean compiler error, not a template-gibberish error

I'm creating a function (possibly member function, not that it matters... maybe it does?) that needs to accept an unknown number of arguments, but I want all of them to be the same type. I know I could pass in an array or vector, but I want to be able to accept the list of args directly without extra structure or even extra brackets. It doesn't look like variadic functions by themselves are typesafe, and I wasn't sure how to go about this w/ variadic template functions. Here's essentially what I'm aiming for (more than likely not correct code, and totally not for the purpose of getting lists of dragons, lol):

//typedef for dragon_list_t up here somewhere.  enum Maiden {     Eunice     , Beatrice     , Una_Brow     , Helga     , Aida };  dragon_list_t make_dragon_list(Maiden...) {     //here be dragons } 

OR

template<Maiden... Maidens> dragon_list_t make_dragon_list(Maidens...) {     //here be dragons } 

USAGE

dragon_list_t dragons_to_slay     = make_dragon_list(Maiden.Eunice, Maiden.Helga, Maiden.Aida) ; 

Tried a few things similar to the above already, no dice. Suggestions? Obvious oversights I may have made? I know it may not be a huge deal to do this instead:

dragon_list_t make_dragon_list(std::array<Maiden> maidens) {     //here be dragons. } dragon_list_t dragons_to_slay     = make_dragon_list({Maiden.Eunice, Maiden.Helga, Maiden.Aida}) ; 

but I'd much rather be able to do it the first way if possible.

like image 225
Brett Rossier Avatar asked Sep 13 '10 19:09

Brett Rossier


People also ask

What is variadic template in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration. However, variadic templates help to overcome this issue.

What is variadic function in Golang?

A variadic function is a function that accepts a variable number of arguments. In Golang, it is possible to pass a varying number of arguments of the same type as referenced in the function signature.

What is a variadic function in Swift?

Variadic functions are functions that accept any number of parameters. The most common one in Swift is print() – most people use it to print a single value, but you can actually pass as many as you want, like this: print(1, 2, 3, 4, 5) To make a variadic function of your own, just add ... after any parameter.


2 Answers

You can just accept the arguments by the variadic template and let typechecking check the validity later on when they are converted.

You can check convertibility on the function interface level though, to make use of overload resolution for rejecting outright wrong arguments for example, by using SFINAE

template<typename R, typename...> struct fst { typedef R type; };  template<typename ...Args> typename fst<void,    typename enable_if<     is_convertible<Args, ToType>::value   >::type... >::type  f(Args...); 

For your use-case if you know the steps to go from an std::array<> to your dragon_list_t then you have already solved it though according to the first option above ("convert-later"):

template<typename ...Items> dragon_list_t make_dragon_list(Items... maidens) {     std::array<Maiden, sizeof...(Items)> arr = {{ maidens ... }};     // here be dragons } 

If you combine this with the above is_convertible approach you have a reject-early template that also does overload resolution on arguments and rejects them if not applicable.

like image 79
Johannes Schaub - litb Avatar answered Oct 09 '22 09:10

Johannes Schaub - litb


If you don't use template on the parameter not in the pack the variadic function will resolve to have all arguments of the same type.

Here's an example for an extended max function that only accepts ints (or types convertible to int).

int maximum(int n) // last argument must be an `int` {     return n; }  template<typename... Args> int maximum(int n, Args... args) // first argument must be an int {     return std::max(n, maximum(args...)); } 

Explanation: When you unpack the argument pack (args...) the compiler looks for the best overload. If the pack had only one parameter then the best candidate is maximum(int) so the only parameter must be and of type int (or convertible to int). If there are more than one elements in the pack then the only candidate is maximum(int, typename...) so the first argument must be of type int (or convertible to int). It's simple to prove by induction that all the types in the pack must be of a type convertible to int.

like image 40
Motti Avatar answered Oct 09 '22 09:10

Motti