Use TYPE from a list of arguments of type tempate

I would like to use the type information from the argument list of the class template.

Working example with quick workaround:

struct NoParam {}; template< typename A = NoParam, typename B = NoParam, typename C = NoParam, typename D = NoParam, typename E = NoParam, typename F = NoParam > struct TypeList { typedef A T1; typedef B T2; typedef C T3; typedef D T4; typedef E T5; typedef F T6; }; template<typename... Types> class Application { Application() { // the actual code will store the created instances in a tuple or map.. std::make_unique< TypeList<Types...>::T1 > (); std::make_unique< TypeList<Types...>::T2 > (); std::make_unique< TypeList<Types...>::T3 > (); std::make_unique< TypeList<Types...>::T4 > (); std::make_unique< TypeList<Types...>::T5 > (); std::make_unique< TypeList<Types...>::T6 > (); } } 

Is there a general way ...

  • to iterate over types and get type information (to create instances)
  • no hard coding for only 6 types in this example
+6
source share
3 answers

Do not reinvent the wheel, you can use std::tuple and std::tuple_element_t for this:

 template<typename... T> using TypeList = std::tuple<T...>; template<int I, typename T> using Type = std::tuple_element_t<I, T>; template<typename... Types> class Application { Application() { std::make_unique<Type<0, TypeList<Types...>>> (); std::make_unique<Type<1, TypeList<Types...>>> (); // and so on... } } 

Type iteration is pretty simple right now. It follows a minimal working example to show you how you can do this:

 #include <tuple> #include <functional> #include <memory> template<typename... T> using TypeList = std::tuple<T...>; template<int I, typename T> using Type = std::tuple_element_t<I, T>; template<typename... Types> class Application { using MyTypeList = TypeList<Types...>; template<std::size_t... I> void iterate(std::index_sequence<I...>) { // demonstration purposes, here I'm simply creating an object of the i-th type int _[] = { 0, (Type<I, MyTypeList>{}, 0)... }; (void)_; } public: void iterate() { iterate(std::make_index_sequence<sizeof...(Types)>{}); } Application() { std::make_unique<Type<0, MyTypeList>> (); std::make_unique<Type<1, MyTypeList>> (); // and so on... } }; int main() { Application<int, float> app; app.iterate(); } 

Note that std::index_sequence and std::make_index_sequence are available with C ++ 14. In any case, you can find several implementations on the Internet that will be used if you are limited to C ++ 11.
Otherwise, you can also iterate over types with a pair of recursive _sfinae'd_functions, which check if sizeof...(Types) reached.

+3
source

You can avoid structuring typeList by indexing a variational type using something like

 struct noTypeInList { }; template <std::size_t, typename ...> struct typeSel; template <typename T0, typename ... Ts> struct typeSel<0U, T0, Ts...> { using type = T0; }; template <std::size_t N, typename T0, typename ... Ts> struct typeSel<N, T0, Ts...> { using type = typename typeSel<N-1U, Ts...>::type; }; template <std::size_t N> struct typeSel<N> { using type = noTypeInList; }; 

So,

 std::make_unique< TypeList<Types...>::T1 > (); 

to become

 std::make_unique< typeSel<0, Types...>::type > (); 

Below is a complete C ++ 11 example if you want std::tuple std:unique_ptr

 #include <tuple> #include <memory> struct noTypeInList { }; template <std::size_t, typename ...> struct typeSel; template <typename T0, typename ... Ts> struct typeSel<0U, T0, Ts...> { using type = T0; }; template <std::size_t N, typename T0, typename ... Ts> struct typeSel<N, T0, Ts...> { using type = typename typeSel<N-1U, Ts...>::type; }; template <std::size_t N> struct typeSel<N> { using type = noTypeInList; }; template <std::size_t ...> struct range { }; template <std::size_t N, std::size_t ... Next> struct rangeH { using type = typename rangeH<N-1U, N-1U, Next ... >::type; }; template <std::size_t ... Next > struct rangeH<0U, Next ... > { using type = range<Next ... >; }; template<typename... Types> class Application { private: std::tuple<std::unique_ptr<Types>...> tpl; template <std::size_t ... rng> Application (const range<rng...> &) : tpl{std::make_tuple(std::unique_ptr< typename typeSel<rng, Types...>::type>()...)} { } public: Application () : Application(typename rangeH<sizeof...(Types)>::type()) { } }; int main() { Application<int, float, int, std::tuple<double, long>> a; } 

This is just an example of using typeSel , because Application can simply be written as

 template<typename... Types> class Application { private: std::tuple<std::unique_ptr<Types>...> tpl; public: Application () : tpl{std::make_tuple(std::unique_ptr<Types>()...)} { } }; 

If you can use the C ++ 14 compiler, you can use std::index_sequence and std::make_index_sequence (and remove range and rangeH ), and Application can become

 template<typename... Types> class Application { private: std::tuple<std::unique_ptr<Types>...> tpl; template <std::size_t ... rng> Application (const std::index_sequence<rng...> &) : tpl{std::make_tuple(std::unique_ptr< typename typeSel<rng, Types...>::type>()...)} { } public: Application () : Application(std::make_index_sequence<sizeof...(Types)>()) { } }; 
0
source

The easiest and cleanest way: a recursive template class :

 template<class...> class Application; template<class T> class Application<T>{ public: Application() { std::make_unique<T> (); } }; template<class T,class...Others> class Application<T,Others...> : public Application<Others...>{ public: Application():Application<Others...>() { std::make_unique<T> (); } }; 
0
source

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


All Articles