根据模板参数中的另一个包创建参数包

时间:2013-09-19 18:15:13

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

假设有几个模板类(结构),其对象形成序列 - 让我们称之为 N_mod (特定类的N - 说明符)和特殊类,它们定义序列中的第一个元素 - First_mod 。除 First_mod 外,每个类都有自己的"界面构建器" - N_builder - 模板类:

template<int targ>
struct First_mod //First element in sequence - without appropriate builder interface
{   };

//Let's consider two types of N_mod - A_mod and B_mod
template<int targ, int param>
struct A_mod
{   };

template<int param>
struct A_builder//Builder for A_mod objects
{   };

template<int targ, int param>
struct B_mod
{   };

template<int param>
struct B_builder//Builder for B_mod objects
{   };

现在我需要根据简单的规则从适当的 N_builders 的现有顺序生成 First_mod N_mod 对象的序列:

  

如果 N_Mod(i) 等于 A_mod 那么 targ(i) = param(i) - targ(i-1),

     

else (即 N_Mod(i) 等于 B_mod )targ(i)= param(i) * targ(i-1)

为了清晰起见,我的草图:

template<typename...builders>
struct mod_seq_gen
{
    typedef /*generated First_mod, A_mod and B_mod sequence pack. How to do it?*/ modseq;
};

template<typename...builders>
struct Container
{
    std::tuple</*here must be mod-sequence generator that creates
                mod parameters pack and unpacks them:*/
                mod_seq_gen<builders...>::modseq
              > mod_sequence;
};

int main()
{   
    /*In this case must be generated next sequence and stored in 
     * mod_sequence tuple:
     * First_mod<3> - A_mod<5-3, 5> - B_mod<2*(5-3), 2>
     */
    Container<First_mod<3>, A_builder<5>, B_builder<2>> obj;
}

我要求帮助实施 mod_seq_gen ,或者整个任务的其他一些提示。

1 个答案:

答案 0 :(得分:4)

首先,我将转储(编译)解决方案:

#include <tuple>
#include <utility>
#include <iostream>

template<int targ>
struct First_mod //First element in sequence - without appropriate builder interface
{  void print() { std::cout << "First_mod["<<targ<<"]" << std::endl; }  };

//Let's consider two types of N_mod - A_mod and B_mod
template<int targ, int param>
struct A_mod
{  void print() { std::cout << "A_mod["<<targ<<", "<<param<<"]" << std::endl; }  };

template<int param>
struct A_builder//Builder for A_mod objects
{
    // publish the template parameter (not necessary)
    static const int param_value = param;

    // provide a way to compute the current targ
    static constexpr int calc_targ(int prev_targ)
    {
        return param - prev_targ;
    }

    // provide a way to build the type
    template < int targ >
    using type = A_mod<targ, param>;
};

template<int targ, int param>
struct B_mod
{  void print() { std::cout << "B_mod["<<targ<<", "<<param<<"]" << std::endl; }  };

template<int param>
struct B_builder//Builder for B_mod objects
{
    static const int param_value = param;
    static constexpr int calc_targ(int prev_targ)
    {
        return prev_targ * param;
    }

    template < int targ >
    using type = B_mod<targ, param>;
};


// just a helper, wonder if there's something in the Standard Library o.O
template < typename... Tuples >
using tuple_cat_types = decltype(tuple_cat( std::declval<Tuples>()... ));


// the generator of the tuple
template < typename TFirst_mod, typename... TBuilders >
struct gen;

// restrict the first type to a specialization of `First_mod`
// isn't necessary, strictly speaking. We just need the first targ.
// Could as well require a nested `static const int targ = ..;`
template < int first_targ, typename... TBuilders >
struct gen < First_mod<first_targ>, TBuilders... >
{
    // recursive helper for the types to be built
    // general case for no template arguments in the pack
    template < int prev_targ, typename... TBuilders2 >
    struct helper { using type = std::tuple<>; };

    // specialized case for recursion
    // note: the recursion here occurs as a nested typedef, not inheritance
    //       (simplifies use of calculated targ)
    template < int prev_targ, typename TBuilder, typename... TBuilders2 >
    struct helper<prev_targ, TBuilder, TBuilders2...>
    {
        // build type using builder
        static const int targ = TBuilder::calc_targ(prev_targ);
        using built_type = typename TBuilder::template type<targ>;

        // recurse
        using further_types = typename helper<targ, TBuilders2...>::type;

        // concatenate tuple
        using type = tuple_cat_types<std::tuple<built_type>, further_types>;
    };

    // concatenate tuple with First_mod
    using type = tuple_cat_types<std::tuple<First_mod<first_targ>>,
                     typename helper<first_targ, TBuilders...>::type>;
};


int main()
{
    gen<First_mod<3>, A_builder<5>, B_builder<2>>::type x;
    static_assert(std::tuple_size<decltype(x)>::value == 3, "!");
    std::get<0>(x).print();
    std::get<1>(x).print();
    std::get<2>(x).print();
}

使用First_mod的构建器稍微容易一些:

template<int param>
struct First_builder
{
    static constexpr int calc_targ(int /* discarded */)
    {
        return param;
    }

    template < int targ >
    using type = First_mod<targ>;
};

/* ... */

// the generator of the tuple
template < int prev_targ, typename... TBuilders >
struct gen
{  using type = std::tuple<>;  };

template < int prev_targ, typename TBuilder, typename... TBuilders2 >
struct gen<prev_targ, TBuilder, TBuilders2...>
{
    // build type using builder
    static const int targ = TBuilder::calc_targ(prev_targ);
    using built_type = typename TBuilder::template type<targ>;

    // recurse
    using further_types = typename gen<targ, TBuilders2...>::type;

    // concatenate tuple
    using type = tuple_cat_types<std::tuple<built_type>, further_types>;
};


int main()
{
    const int discarded = 0;
    gen<discarded, First_builder<3>, A_builder<5>, B_builder<2>>::type x;
    static_assert(std::tuple_size<decltype(x)>::value == 3, "!");
    std::get<0>(x).print();
    std::get<1>(x).print();
    std::get<2>(x).print();
}