如何返回嵌套在其他包中的模板包?

时间:2014-10-08 16:12:44

标签: c++ templates c++11 variadic-templates pack

以下代码有效:

#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

3 个答案:

答案 0 :(得分: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

答案 1 :(得分: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兼容实现:

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;

您可以更进一步,使其与使用类似于DataMap的任意模板一起使用,例如使用模板模板参数 std::tuple(而不是DataMap 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

答案 2 :(得分: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>{});
}