How to return a template package nested in another package?

The following code works:

#include <iostream> #include <list> struct Base {}; struct A : Base {}; struct B : Base {}; struct C : Base {}; struct D : Base {}; struct E : Base {}; struct F : Base {}; template <int KEY, typename... RANGE> struct Map {}; // one-to-many map (mapping KEY to RANGE...) template <typename...> struct Data {}; using Database = Data< Map<0, A,B,C>, Map<1, D,E,F> >; template <int N, typename FIRST, typename... REST> // N has meaning in my program, but not shown here. void insertInMenu (std::list<Base*>& menu) { menu.push_back(new FIRST); insertInMenu<N, REST...> (menu); } template <int N> void insertInMenu (std::list<Base*>&) {} // End of recursion. template <int N> std::list<Base*> menu() { std::list<Base*> m; insertInMenu<0, A,B,C>(m); // A,B,C should be obtained using N and Database. return m; } int main() { std::list<Base*> m = menu<0>(); std::cout << "m.size() = " << m.size() << std::endl; // 3 } 

But, as stated in my comment above, I want to use Database and a value of N to get a range of A,B,C (or D,E,F ) or whatever. But I do not know how to do this? Can anyone help? Line

 insertInMenu<0, A,B,C>(m); 

need to be replaced with something like

 obtainRange<Database, N>() 

since for those values ​​that are known at compile time, there should be enough information to get the range that I want.

 obtainRange<Database, 0>() 

must return A,B,C and

 obtainRange<Database, 1>() 

should return D,E,F in this case.

+5
source share
3 answers
 template <typename D, int N> struct obtainRange; template <int N, typename... Ts, typename... Maps> struct obtainRange<Data<Map<N, Ts...>, Maps...>, N> { using type = std::tuple<Ts...>; }; template <int N, int M, typename... Ts, typename... Maps> struct obtainRange<Data<Map<M, Ts...>, Maps...>, N> : obtainRange<Data<Maps...>, N> {}; template <int N, typename Tuple, std::size_t... Is> std::list<Base*> menu(std::index_sequence<Is...>) { std::list<Base*> m; insertInMenu<0, typename std::tuple_element<Is, Tuple>::type...>(m); return m; } template <int N> std::list<Base*> menu() { using Tuple = typename obtainRange<Database, N>::type; return menu<N, Tuple>(std::make_index_sequence<std::tuple_size<Tuple>::value>{}); } 

Demo


If you cannot use C ++ 14 index_sequence , the following is an alternative implementation of C ++ 11-compatibile:

 template <std::size_t... Is> struct index_sequence {}; template <std::size_t N, std::size_t... Is> struct make_index_sequence_h : make_index_sequence_h<N - 1, N - 1, Is...> {}; template <std::size_t... Is> struct make_index_sequence_h<0, Is...> { using type = index_sequence<Is...>; }; template <std::size_t N> using make_index_sequence = typename make_index_sequence_h<N>::type; 

You can go further and make it work with arbitrary templates similar to Data and Map , for example. a std::tuple (instead of Data ) Map s, using template template parameters:

 template <typename D, int N> struct obtainRange; template <template <typename...> class DB , template <int, typename...> class MP , typename... Ts , typename... Maps , int N> struct obtainRange<DB<MP<N, Ts...>, Maps...>, N> { using type = std::tuple<Ts...>; }; template <template <typename...> class DB , template <int, typename...> class MP , typename... Ts , typename... Maps , int M , int N> struct obtainRange<DB<MP<M, Ts...>, Maps...>, N> : obtainRange<DB<Maps...>, N> {}; 

Demo 2

+1
source
 // There is no need to take the length here, btw. template <int, typename... Args> void insertInMenu (std::list<Base*>& menu) { // Non-recursive push_backs: std::initializer_list<int>{ (menu.push_back(new Args), 0)... }; } template <int, typename> struct InsertEnv; template <int key, int otherKey, typename... Args, typename... Rest> struct InsertEnv<key, Data<Map<otherKey, Args...>, Rest...>> : InsertEnv<key, Data<Rest...>> {}; template <int key, typename... Args, typename... Rest> struct InsertEnv<key, Data<Map<key, Args...>, Rest...>> { void operator()(std::list<Base*>& menu) { insertInMenu<key, Args...> (menu); } std::list<Base*> operator()() { return {new Args...}; } }; template <int N> void addToMenu (std::list<Base*>& menu) { InsertEnv<N, Database>()(menu); } template <int N> std::list<Base*> menu() { return InsertEnv<N, Database>()(); } 

User either as

 menu<N>() // list with the desired elements in it 

Or how

 std::list<Base*> list; addToMenu<N>(list); // pushes back the desired elements 

Demo

+1
source

Live at coliru

 template <typename, int> struct obtainRange {}; template <int N, typename...Types, typename...Rest> struct obtainRange<Data<Map<N, Types...>, Rest...>, N> : Data<Types...> {}; template <int N, typename T, typename...Rest> struct obtainRange<Data<T, Rest...>, N> : obtainRange<Data<Rest...>, N> {}; template <typename...Types> std::list<Base*> menu(Data<Types...>) { return { new Types{}... }; } template <int N> std::list<Base*> menu() { return menu(obtainRange<Database, N>{}); } 
+1
source

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


All Articles