Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I rebuild parameter pack from TypeList

I have a variadic template typelist:

template <class... Types>
struct typelist {};

Then how can I pass it to some external code that expects a parameter pack, say std::tuple. In other word, I need to store the parameter pack as a member or typedef in my typelist, like

...
struct typelist {
    using types = Types; // Imaginary syntax
}

Yet this is rejected by the compiler saying Types is unexpanded.

Any workaround?

This question is mentioned in another way in comments of this question, yet was not covered by the existing answer.


Details as requested in comments:

If I compile (-std=c++17):

template <class... T>
struct typelist {};

std::tuple<typelist<int, int>> tp{0,1};

g++ gives error: no matching function for call to ‘std::tuple<typelist<int, int> >::tuple(<brace-enclosed initializer list>)’ std::tuple<typelist<int, int>> tp{0,1};

If I compile (-std=c++17):

template <class... T>
struct typelist {
    using types = T;
};

g++ gives error: parameter packs not expanded with ‘...’: using types = T;

like image 578
YiFei Avatar asked Apr 01 '17 11:04

YiFei


2 Answers

You need a bit of boilerplate to get the right tuple specialization out of a typelist, for you can't simply store the parameter pack as it is.
As an example, you could do that by using properly a function declaration and an using declaration:

#include<tuple>
#include<utility>
#include<type_traits>

template <class... T>
struct typelist {};

template<typename... T>
std::tuple<T...> foo(typelist<T...>);

template<typename L>
using tupleFromTypelist = decltype(foo(std::declval<L>()));

int main() {
    using tl = typelist<int, int>;
    tupleFromTypelist<tl> tp{0,1};
    static_assert(std::is_same<tupleFromTypelist<tl>, std::tuple<int, int>>::value, "!");
}

Or an helper class like the one in the following example:

#include<tuple>
#include<utility>
#include<type_traits>

template <class... T>
struct typelist {};

template<typename>
struct helper;

template<typename... T>
struct helper<typelist<T...>> {
    using type = std::tuple<T...>;
};

int main() {
    using tl = typelist<int, int>;
    helper<tl>::type tp{0,1};
    static_assert(std::is_same<helper<tl>::type, std::tuple<int, int>>::value, "!");
}

Otherwise, let typelist expose the tuple specialization and get it directly from it:

#include<tuple>
#include<utility>
#include<type_traits>

template <class... T>
struct typelist {
    using tuple = std::tuple<T...>;
};

int main() {
    using tl = typelist<int, int>;
    tl::tuple tp{0,1};
    static_assert(std::is_same<tl::tuple, std::tuple<int, int>>::value, "!");
}

If it's the only type for which you want to use the parameter pack, this is the easiest approach.

like image 62
skypjack Avatar answered Nov 08 '22 12:11

skypjack


You can't store a parameter pack in a type alias. You need to use template argument deduction to extract the arguments of your type_list for reuse. One way is to use a dummy function like so:

template <typename... Args>
struct type_list {};

template <typename... Args>
std::tuple<Args...> to_tuple(type_list<Args...>);

template <typename TypeList>
struct type_list_to_tuple {
  using type = decltype(to_tuple(std::declval<TypeList>()));
};

template <typename TypeList>
using type_list_to_tuple_t = typename type_list_to_tuple<TypeList>::type;

int main() {
  using my_type_list = type_list<int, float>;
  using my_tuple = type_list_to_tuple_t<my_type_list>;
  static_assert(std::is_same_v<my_tuple, std::tuple<int, float>>);
}
like image 45
Joseph Thomson Avatar answered Nov 08 '22 13:11

Joseph Thomson