代码之家  ›  专栏  ›  技术社区  ›  t.g.

是否可以使用所有模板参数组合生成类型?

  •  7
  • t.g.  · 技术社区  · 15 年前

    我有一个模板类

    template<class U, class V, class W> 
    class S
    {
    //... implementations 
    };
    

    以及类型的一些库存类型实现 U , V W :

    typedef boost::mpl::vector<U0, U1> u_types;
    typedef boost::mpl::vector<V0, V1, V2, V3, V4> u_types;
    typedef boost::mpl::vector<W0, W1, W2, W3, W4> w_types;
    

    我想用所有可能的模板参数组合测试类S,

    typedef boost::mpl::vector<
        S<U0,V0,W0>, 
        S<U0,V0,W1>,
        // ...
        S<U1,V4,W4>,
        > s_types;
    

    这样地:

    boost::mpl::for_each<s_types>(test_func).
    

    唯一的问题是有2**5**5=50个组合,我不希望逐一输入。

    有没有办法生成所有的组合( s_types )使用Boost::mpl还是Boost.预处理器?

    谢谢


    添加了我最初失败的尝试:

    我试图求助于索引(因此定义u_类型等)和类似的部分模板专门化

    namespace wrapper
    {
      template <int Uidx, int Vidx, int Widx> 
      struct S_Wrapper
      {
        typedef S<Uidx, Vidx, Widx> type;
    
        S_Wrapper() // auto test in the ctor
        {
          cout << "test result = " << test(type());
        }
    
        // test with S<Uidx, Vidx, Widx>
        static bool test(type t)
        {
          // implementations
        }
    
        // get stuck here, 
        S_Wrapper<Uidx-1, Vidx, Widx> s; // temp varible to invoke recursive-ness
        // what else to complete all recursive path?
      };
    
      // specializations       
      template <0, 0, 0> 
      struct S_Wrapper
      {
        typedef S<0, 0, 0> type;
    
        // test with S<Uidx, Vidx, Widx>
        //
        static bool test(type t)
        {
          // implementations
        }  
      };
    
      // get stuck here, too
      // what other specializations are ?
      // other specializations
    }
    

    然后

    wrapper::S_Wrapper< 
      mpl::size<u_types>::type::value, 
      mpl::size<v_types>::type::value, 
      mpl::size<w_types>::type::value
    > s; 
    

    所有S型应进行分类和测试;

    然而,我未能通过确定

    1) 适当的专业和
    2) 结构S_包装器中的递归ness触发器

    我的所有试验要么在运行时部分覆盖了这些组合,要么在编译时演绎失败。

    有什么想法吗?


    解决方案

    受Matthieu的启发,我设计了一个模板类 Combine

    typedef Combine<
                u_types,
                v_types,
                w_type,
                print_typeid
            >::Generate<> base_generator_type;
    base_generator_type::Run();
    

    它将打印所有生成的类型。


    密码

    // example test implementation
    struct print_typeid
    {
        template<
            class U, 
            class V, 
            class W
        >
        static void run()
        {
            // print the typeinfo
            std::cout 
                <<  total_recursions << ":"
                << typeid(U).name() << ","
                << typeid(V).name() << ","
                << typeid(W).name()
                << std::endl;  
        }            
    }; 
    
    // solution implemented in one wrapper class
    namespace argument_combination
    {
        using boost::is_same;
        using boost::mpl::begin;
        using boost::mpl::end;
        using boost::mpl::next;        
        using boost::mpl::if_;  
        using boost::mpl::deref;    
    
        unsigned int total_recursions = 0;
    
        struct end_of_recursion_tag 
        {
            static void Run()
            {
                std::cout << "end of " 
                     << total_recursions 
                     << " recursions\n"
                    ;
            }
        };
    
        template <
            class UTypes,    // Forward Sequence, e.g. boost::mpl::vector
            class VTypes,    // Forward Sequence, e.g. boost::mpl::vector
            class WTypes,    // Forward Sequence, e.g. boost::mpl::vector
            class TestFunc  // class type that has a nested templated run() member function
        >
        struct Combine
        {
            // forward declaration
            template <
                class UIterator,
                class VIterator,
                class WIterator 
            >   
            class Generate;
    
            // this class implements recursion body 
            template <
                class UIterator,
                class VIterator,
                class WIterator
            >            
            struct Next
            {
                // u_begin is not necessary ;)
                // it would be cheaper not to pre-declare all of them since we force evaluation
                // however this dramatically increase the readability
                typedef typename begin<VTypes>::type v_begin;
                typedef typename begin<WTypes>::type w_begin;
    
                typedef typename end<UTypes>::type u_end;
                typedef typename end<VTypes>::type v_end;
                typedef typename end<WTypes>::type w_end;
    
                typedef typename next<UIterator>::type u_next;
                typedef typename next<VIterator>::type v_next;
                typedef typename next<WIterator>::type w_next;
    
                typedef typename if_< is_same<typename w_next, w_end>,
                                    typename if_< is_same<v_next, v_end>,
                                        typename if_< is_same<u_next, u_end>,
                                            end_of_recursion_tag,
                                            Generate< 
                                                u_next, 
                                                v_begin, 
                                                w_begin 
                                            >
                                        >::type,
                                        Generate< 
                                            UIterator, 
                                            v_next,
                                            w_begin 
                                        >
                                    >::type,
                                    Generate< 
                                        UIterator, 
                                        VIterator, 
                                        w_next
                                        >
                                >::type type;
            };
    
            //  this class run test on generated types in thos round and go to next*/
            template <
                class UIterator = typename begin<UTypes>::type,
                class VIterator = typename begin<VTypes>::type,
                class WIterator = typename begin<WTypes>::type
            >
            struct Generate
            {
                //  generate <<next>> target type         
                typedef typename Next<
                        UIterator, 
                        VIterator, 
                        WIterator 
                    >::type next_type;
    
                static void Run()
                {
                    // increment recursion counter                
                    ++total_recursions;
    
                    // test on the generated types of this round of recursion
                    TestFunc::run<
                         typename deref<UIterator>::type,
                         typename deref<VIterator>::type,
                         typename deref<WIterator>::type
                     >();
    
                    // go to the next round of recursion
                    next_type::Run();
                }
            };
        };
    
    }//  namespace argument_combination
    
    2 回复  |  直到 13 年前
        1
  •  9
  •   Matthieu M.    15 年前

    如果你真正想做的是生成所有可能解的向量,然后测试它们,那么你必须使用预处理器为你生成所有可能解。

    然而,另一个解决方案是使用生成器:一个包装器类,它将实例化所有解决方案并测试它们。您可能需要参考Loki的层次生成器(在本书中有详细介绍)。

    // never remember where they put boost::same_type :x
    #include <boost/mpl/if.hpp>
    #include <boost/mpl/deref.hpp>
    #include <boost/mpl/begin.hpp>
    #include <boost/mpl/end.hpp>
    #include <boost/mpl/next.hpp>
    
    using namespace boost::mpl;
    
    struct None
    {
       static void test() {}
    };
    
    template <class UIterator, class UTypes,
              class VIterator, class VTypes,
              class WIterator, class WTypes>
    class Generator;
    
    template <class UIterator, class UTypes,
              class VIterator, class VTypes,
              class WIterator, class WTypes>
    struct Next
    {
      // u_begin is not necessary ;)
      // it would be cheaper not to pre-declare all of them since we force evaluation
      // however this dramatically increase the readability
      typedef typename begin<VIterator>::type v_begin;
      typedef typename begin<WIterator>::type w_begin;
    
      typedef typename next<UIterator>::type u_next;
      typedef typename next<VIterator>::type v_next;
      typedef typename next<WIterator>::type w_next;
    
      typedef typename end<UIterator>::type u_end;
      typedef typename end<VIterator>::type v_end;
      typedef typename end<WIterator>::type w_end;
    
    
      typedef if_< boost::same_type<w_next, w_end>,
                   if_< boost::same_type<v_next, v_end>,
                        if_< boost::same_type<u_next, u_end>,
                             None,
                             Generator< u_next, UTypes,
                                        v_begin, VTypes,
                                        w_begin, WTypes >
                        >,
                        Generator< UIterator, UTypes,
                                   v_next, VTypes,
                                   w_begin, WTypes >
                    >,
                    Generator< UIterator, UTypes,
                               VIterator, VTypes,
                               w_next, WTypes>
               >::type type;
    };
    
    template <class UIterator, class UTypes,
              class VIterator, class VTypes,
              class WIterator, class WTypes>
    struct Generator
    {
       typedef S< deref<UIterator>::type,
                  deref<VIterator>::type,
                  deref<WIterator>::type > S_type;
    
       typedef Next<UIterator, UTypes,
                    VIterator, VTypes,
                    WIterator, WTypes>::type next_type;
    
       static void test()
       {
         // test my variation of S
         S_Type my_S;
         test_func(my_S);
    
         // test the variations of my next and its next and... you get the idea :)
         next_type::test();
       }
    };
    
    // And finally
    int main(int argc, char* argv[])
    {
      typedef Generator< begin<u_types>::type, u_types,
                         begin<v_types>::type, v_types,
                         begin<w_types>::type, w_types > base_generator_type;
    
      base_generator_type::test();
    }
    

    免责声明:此代码尚未编译,可能缺少一些include/typename/use指令。。。不过,我希望你明白我的意思。

    如果您知道设计模式是什么,那么它在每个步骤层添加另一轮测试的方式上与“decorator”或“composite”设计非常相似。

        2
  •  1
  •   etjazz    10 年前

    对于我的框架,我需要一种更通用的方法来实现类型组合,所以我开发了一些不同的东西,似乎可以工作。 它提供类型序列的组合,作为特殊的组合视图和组合迭代器:

    template < class Seq >
    class combine_view {
        typedef typename mpl::transform<Seq, mpl::begin<_1> >::type Pos_begin;
        typedef typename mpl::transform<Seq, mpl::end<_1> >::type   Pos_end;
    public:
        typedef combine_iterator<Seq, Pos_begin> begin;
        typedef combine_iterator<Seq, Pos_end>  end;
        typedef combine_view type;
    };
    

    这将从随机访问序列序列创建一个新的fwd序列。 提供序列开始和结束的combine_迭代器必须知道序列和提供位置的迭代器,例如以下实现:

     template < typename Seq, typename Itrs >
     struct combine_iterator {
         typedef mpl::forward_iterator_tag category;
         typedef Seq  seq;
         typedef typename transform <
             Itrs,
             deref<_1>
           >::type
         type;
     };
    

    现在,您必须告诉boost mpl如何达到下一个位置,即mpl::next operation。

    namespace boost {
    namespace mpl {
    
    template <class Seq, class Pos>
    struct next< combine_iterator<Seq, Pos> > {
        typedef typename SequenceCombiner<Seq,Pos>::next next_Pos;
        typedef combine_iterator< Seq, next_Pos > type;
    };
    
    } // mpl
    } // boost
    

    最后,可以使用mpl::fold实现组合器技巧,如在此类中:

    template <class Seq, class ItrSeq>
    class SequenceCombiner {
    
       template < class _Seq = mpl::vector<int_<1> > >
       struct StateSeq {
           typedef typename pop_front<_Seq>::type sequence;
           typedef typename mpl::at< _Seq, int_<0> >::type state;
           typedef _Seq type;
       };
    
       template < class _Seq, class _State >
       struct set_state {
           typedef StateSeq< typename push_front<_Seq, _State >::type > type;
       };
    
       struct NextOp {
    
           template < typename Out, typename In, typename Enable = typename Out::state >
           class apply {
               typedef typename Out::sequence seq;
               typedef typename Out::state new_state;
               typedef typename mpl::at<In,int_<0> >::type in_seq;
            typedef typename mpl::at<In,int_<1> >::type in_itr;
    
            typedef typename mpl::push_back<seq, in_itr >::type new_seq;
        public:
            typedef typename set_state<new_seq, int_<0> >::type type;
        };
    
        template < typename Out, typename In >
        class apply<Out,In,mpl::int_<1> > {
            typedef typename Out::sequence seq;
            typedef typename Out::state state;
            typedef typename mpl::at<In,int_<0> >::type in_seq;
            typedef typename mpl::at<In,int_<1> >::type in_itr;
    
            typedef typename mpl::begin<in_seq>::type Itr_begin;
            typedef typename mpl::next<in_itr>::type  Itr_next;
            typedef typename mpl::end<in_seq>::type   Itr_end;
    
            typedef typename mpl::if_< boost::is_same<Itr_next,Itr_end>,
            typename mpl::push_back<seq,Itr_begin>::type,
            typename mpl::push_back<seq,Itr_next>::type
            >::type
            new_seq;
    
            typedef typename mpl::if_< boost::is_same<Itr_next,Itr_end>,
            mpl::int_<1>,
            mpl::int_<0>
            >::type
            new_state;
        public:
            typedef typename set_state<new_seq, new_state>::type type;
        };
    };
    
    typedef typename mpl::fold<
                                typename mpl::zip_view< mpl::vector<Seq, ItrSeq > >::type,
                                StateSeq<>,
                                NextOp
                              >::type
    StateResult;
    
    public:
    
    typedef typename mpl::if_< boost::is_same< typename StateResult::state, int_<1> >,
                               typename mpl::transform< Seq, mpl::end<_1> >::type,
                               typename StateResult::sequence >::type
    next;
    };
    

    让我向您展示如何在测试应用程序中使用生成的新序列视图。

    struct A {};
    struct B {};
    struct C {};
    struct D {};
    struct E {};
    struct F {};
    struct G {};
    struct H {};
    struct I {};
    
    namespace {        
    struct PrintTypeId {
        template <class T>
        void operator()(T) const
        { std::cout << typeid(T).name() << "  "; }
    };
    struct PrintSeq {
        template < typename T >
        void operator()(T) {
            mpl::for_each<T>( PrintTypeId() );
            std::cout << "\n";
        }
    };
    }
    
    int main() {
        BEGIN_TESTING( Mpl Sequence Combiner Test);
    
        typedef mpl::vector<A,B,C>   seq1;
        typedef mpl::vector<D,E,F>   seq2;
        typedef mpl::vector<G,H,I>   seq3;
    
        typedef mpl::combine_view< mpl::vector<seq1,seq2,seq3> > cv;
        mpl::for_each< cv >( PrintSeq() );
    
        END_TESTING;
    }
    

    结果应该是这样的:

    ..:: Testing Mpl Sequence Combiner Test ::..
    1A  1D  1G  
    1B  1D  1G  
    1C  1D  1G  
    1A  1E  1G  
    1B  1E  1G  
    1C  1E  1G  
    1A  1F  1G  
    1B  1F  1G  
    1C  1F  1G  
    1A  1D  1H  
    1B  1D  1H  
    1C  1D  1H  
    1A  1E  1H  
    1B  1E  1H  
    1C  1E  1H  
    1A  1F  1H  
    1B  1F  1H  
    1C  1F  1H  
    1A  1D  1I  
    1B  1D  1I  
    1C  1D  1I  
    1A  1E  1I  
    1B  1E  1I  
    1C  1E  1I  
    1A  1F  1I  
    1B  1F  1I  
    1C  1F  1I  
    

    谢谢你的关注。

    安德里亚·里戈尼·加罗拉