2014-10-08 61 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


如果您不能使用C++ 14的index_sequence,然後下面是替代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; 

您可以繼續前進,並使其可以使用類似於Data和的任意模板,例如一個std::tuple(而不是Data)的Map S,使用模板的模板參數

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

你在哪裏'Database'選擇'Map'基於其指數? – 2014-10-08 16:31:37

+0

@PiotrS。錯字。固定。 – Columbo 2014-10-08 16:52:14

+0

我的意思是'insertInMenu <1,數據,地圖<1,C,D>>'應該由'C,D'(在'數據庫'中索引爲'1'的地圖)產生,不擴展'Map <0,AB>,Map <1,C,D>' – 2014-10-08 16:56:52

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 2014-10-10 14:11:43

+0

@prestokeys這不是一個特殊的情況 - 它可以與任何可以從'Base *'的braced-init-list構建的容器一起使用 - 但是當你用'unique_ptr '代替真正的程序時,公平性會崩潰'Base *',因爲你不能從'std :: initializer_list'移動元素。 – Casey 2014-10-10 15:00:19