2014-10-08 4 views
5

Следующий код работает:Как вернуть пакет шаблонов, вложенный в другой пакет?

#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 
} 

Но, как указано в моем комментарии выше, я хочу использовать Database и значение N получить диапазон A,B,C (или D,E,F) или любой другой. Но я не знаю, как это сделать? Может ли кто-нибудь помочь? Линия

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

должен быть заменен чем-то вроде

obtainRange<Database, N>() 

поскольку те, во время компиляции известных значений должно быть достаточно информации, чтобы получить диапазон я хочу.

obtainRange<Database, 0>() 

A,B,C должен вернуться и

obtainRange<Database, 1>() 

должен возвращать D,E,F в этом случае.

ответ

1
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


Если вы не можете использовать 14 C++ index_sequence-х, то ниже альтернатива C++ 11-реализация совместим:

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; 

Вы можете пойти дальше и заставить его работать с произвольными шаблонами, подобными Data и Map, т.е. std::tuple (вместо Data) из Map с помощью шаблона шаблонные-параметров:

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
// 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>()(); 
} 

Пользователь либо

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

Или, как

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

Demo.

+0

где вы выбор '' Map' из Database' основанный на его индексе? –

+0

@PiotrS. Опечатка. Исправлена. – Columbo

+0

Я имел в виду, что 'insertInMenu <1, данные , Карта <1,C,D>>' должен привести с 'C, d' (карта индексируется как' 1' в 'Database'), не расширяется' Карта <0,AB>, Карта <1,C,D> ' –

1

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>{}); 
} 
+0

Это короткое решение прекрасно работает с std :: list, но мой контейнер на самом деле является специализированным классом, поэтому мне все равно нужно использовать insertInMenu для обработки этого специального контейнера.Но хорошо видеть хорошее быстрое решение для специального случая контейнера STL. – prestokeys

+0

@prestokeys Это не совсем особый случай - он будет работать с любым контейнером, который может быть построен из скопированного списка инициализации 'Base *', - но справедливо распадается в реальной программе, когда вы подставляете 'unique_ptr ' для 'Base *', так как вы не можете перемещать элементы из 'std :: initializer_list'. – Casey