Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check the type of passed arguments to variadic function

I'm new to variadic templates and for the sake of learning consider the following function

template <typename T, typename... args>
T* make_arr(args... arg) {

   // Code to check if passed args are of the same type

   T* arr = new T[sizeof...(arg)]{ arg... };
   return arr;
}

I have two questions:

  1. I want the function to be templated and I want the passed arguments to be of the same type so the question: is it possible to check if passed arguments are of the same type ?
  2. Is it possible to deduce the type of the array pointer by deducing the type of args..., I mean without using <typename T>? ... I used decltype(arg) but it didnt work ...

Note: please edit the title question if it is not appropriate ... thanks

like image 253
Laith Avatar asked Feb 08 '23 00:02

Laith


2 Answers

The only way I found is to make a helper function using SFINAE

//Basic function
template<typename T>
void allsame(T) {}

//Recursive function
template<typename T, typename T2, typename... Ts, 
typename = std::enable_if_t<std::is_same<T, T2>::value>>
void allsame(T arg, T2 arg2, Ts... args)
{
    allsame(arg2, args...);
}

You can then call it like so:

allsame(arg...);

The compiler will then throw an error if the types are not the same.


For 2), you could modfiy allsame to return the type. The only drawback to this function is that it won't work if the type isn't default constructable.

template<typename T>
T allsame(T) { return{}; }

T allsame(T arg, T2 arg2, Ts... args)

Then, you can decltype(allsame(args...)) to get the type

like image 198
Rakete1111 Avatar answered Apr 28 '23 00:04

Rakete1111


First of all, you'll need these includes:

#include <type_traits>
#include <tuple>

then, let us declare variadic template to detect whether types are same or not:

template <typename ... args>
struct all_same : public std::false_type {};


template <typename T>
struct all_same<T> : public std::true_type {};


template <typename T, typename ... args>
struct all_same<T, T, args...> : public all_same<T, args ... > {};

now we can use static_assert to detect if parameters types are same:

template <typename T, typename... args>
T* make_arr(args... arg) {

   // Code to check if passed args are of the same type
   static_assert(all_same<args ...>::value, "Params must have same types");

   T* arr = new T[sizeof...(arg)]{ arg... };
   return arr;
};

Finally, let us take return type of your function as first type of parameters - if all types are same we can take any of them. We use std::tuple for this

template <typename... args>
typename std::tuple_element<0, std::tuple<args...> >::type * make_arr(args... arg) {

   // Code to check if passed args are of the same type
   static_assert(all_same<args ...>::value, "Params must have same types");

   typedef typename std::tuple_element<0, std::tuple<args...> >::type T;

   T* arr = new T[sizeof...(arg)]{ arg... };
   return arr;
};
like image 28
user2807083 Avatar answered Apr 28 '23 00:04

user2807083