Unpacking a List

Lets say that I have a function that accepts only a type template parameter, I can’t change its definition / implementation.

template < typename T > void do_it(); 

Now I have a typelist, which is defined in the usual way, also cannot change it:

 template< typename ...Ts > struct typelist; 

I want to implement a function that takes a list of types and runs do_it () for each type:

 template< typename List > void do_them(); 

The only solution I have found so far:

 template< typename T > void do_them_impl() { do_it<T>(); } template< typename T, typename Ts...> void do_them_impl() { do_it<T>(); do_them_impl<Ts...>(); } template< template < typename...> class List, typename ...Ts > void do_them_extract( List<Ts...>&& ) { do_them_impl< Ts >(); } template< typename List > void do_them() { do_them_impl( List{} ); } 

But for each case, 4 (!) Functions are required. I want to create one do_them function. I will need a lot of these, and I do not want to write four functions for each. Did I miss something?

C ++ 14 is welcome, also C ++ 17 solutions, but marked as such.

+6
source share
2 answers

In C ++ 14, you can use some terrible tricks to introduce a valid package extension context:

 template< template < typename...> class List, typename ...Ts > void do_them_impl( List<Ts...>&& ) { (void)std::initializer_list<int> { (do_it<Ts>(), 0)... }; } template< typename List > void do_them() { do_them_impl( List{} ); } 

This avoids the recursive instance of the template, which is usually more expensive.

Live demo


In C ++ 17, you can use fold expressions :

 template< template < typename...> class List, typename ...Ts > void do_them_impl( List<Ts...>&& ) { (do_it<Ts>(), ...); } template< typename List > void do_them() { do_them_impl( List{} ); } 

Live demo

+7
source

Here is a solution that uses C ++ 14 generic lambdas:

 template <typename T> struct type_ { using type = T; }; template <typename Type> using type = typename Type::type; template <typename List> struct map_; template <template <typename...> typename Container, typename... Ts> struct map_<Container<Ts...>> { template <typename Fun> void operator()(Fun&& fun) { (void)((int[sizeof...(Ts)]){(fun(type_<Ts>{}), 0)...}); } }; template <typename List> auto map = map_<List>{}; 

Then for each function

 #include <iostream> #include <cxxabi.h> #include <typeinfo> template <typename T> const char * type_name() { return abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, nullptr); } template <typename T> void do_it() { std::cout << type_name<T>() << std::endl; } 

You can write:

 template <typename List> void do_them() { map<List>([](auto v){ do_it<type<decltype(v)>>(); }); } template <typename... Ts> struct typelist {}; int main() { do_them<typelist<int, char, bool>>(); return 0; } 

Compiling with -O3 gives the same build as if we just called do_it<int> , do_it<char> , do_it<bool> in a row.

0
source

Source: https://habr.com/ru/post/1014646/


All Articles