• c++11模拟boost元占位符placeholder


    准备实现meta programming的fold函数,发现自己缺少占位符实现,这样传入fold的transform op类(元函数)都不得不另外写个外覆类,其实我觉得没啥不好,简单直接,说实话干扰什么的没那么严重,一个功能块里能用fold的地方能有几次?但动了占位符这个念头,就想尝试实现一下。

    看一下实际情景:

    template<typename TList, typename Init, class TransformOp>
    struct fold_s  {};

    我们可能会希望把push_back作为运算子传入fold_s中,从而实现循环迭代TList的每一个元素,对其应用push_back。如:

    using type = fold_s<typelist<int, float, char>, nullist, push_back>::type;

    问题是,push_back并不是一个类,只是一个声明,push_back<somelist, t>如此才是一个真正的类,而一般只有类才能作为实参传入。

    最直接的做法是写个外覆类:

        struct push_back_wrap
        {
            template<typename TList, typename T>
            struct apply
            {
                using type = typename mpl::push_back<TList, T>::type;
            };
        };

    传入fold_s然后调用apply:

        template<typename TList, typename Init, class TransformOp>
        struct fold_s
        {
            using sometype = typename TransformOp::apply<TList, T>::type;
        };

        using type = fold_s<typelist<int, float, char>, nullist, push_back_wrap>::type;
    
    

    我们知道很多函数语言的一个特征就是延迟计算。此处push_back_wrap中的嵌套类apply,使得push_back_wrap也具有延迟的特性,类型计算直到fold_s真正应用apply时

    才发生。这就是meta programming中实现lambada的手法。缺点是我们必须要在使用lambda元类的地方都默认假设apply存在。相比于它的强大功能,因为c++ mpl的

    限制导致这个小不便,我们就忍忍吧。

    以上说明了一个占位符将要应用的情境。下面就开始no zuo no die的处理吧。其实就是有些人不希望每次用flod_s时都要写个外覆类,他们希望当flod_s需要传入push_back

    时就直接传入push_back,好看好记些。很明显那只能传入一个push_back的特化了。

    fold< vector<int, float, char>, vector<>, push_back<_1, _2> >::type;

    上边的_1,_2就是占位符了。push_back<_1, _2>就是我们所讨论的特化的。显然_1, _2是个类,在上述语句中分别指vector<>,int,总之占位符将指定你需要指定的位置。

    这个特化既然取代了外覆类,那它必然提供了相似的功能。也就是push_back必然是个类型延迟的元函数类,它具有类似下面的结构:

        struct push_back<...>
        {
            struct apply
            {
                type...
            };
        };

    那么在fold_s内当调用push_back::apply时,显然push_back必须要具备从参数列表中挑选指定参数的能力,自然的,这个任务就交给_1,_2占位符了。实现的办法你可以

    去查看boost mpl库的做法,也可使用我下边的做法(需要c++11支持):

    #ifndef HI_MPL_PLACEHOLDERS_H_INCLUDED
    #define HI_MPL_PLACEHOLDERS_H_INCLUDED
    
    //////////////////////////////////////////////////////////////////////
    namespace hi { namespace mpl {
        
        //surport palceholders is too painful 
        namespace placeholders
        {
            namespace helper 
            {
                template<typename... TList> struct arglist { };
                typedef arglist<> nullargs;
    
                template<unsigned int N, typename... TList> struct at;
    
                template<unsigned int N, typename T, typename... TList>
                struct at< N, arglist<T, TList...> >
                {
                    typedef typename at< N - 1, arglist<TList...> >::type type;
                };
    
                template<typename T, typename... TList>
                struct at< 0, arglist<T, TList...> >
                {
                    typedef T type;
                };
    
            } // end of placeholders::helper
    
            template<int n> struct Arg
            {
                template<typename ... TList>
                struct apply
                {
                    using type = typename helper::at<n - 1, helper::arglist<TList...> >::type;
                };
    
            private:
    
            };
    
            using _1 = Arg<1>;
            using _2 = Arg<2>;
            using _3 = Arg<3>;
            using _4 = Arg<4>;
        } // end of placeholders
    
    }
    }
    #endif

    如上,_1::apply<int, char, float>::type为int,  _2::apply<int, char, float>::type为char。若不太清楚原理请参考:
    http://www.cnblogs.com/flytrace/p/3551414.html

    以上要注意的arglist是从0开始索引的,而外部Arg是从1开始索引的。

    至此让我们把push_back<_1, _2>完成:

        template<>                                                                
        struct push_back< _1, _2 >                                            
        {                                                                        
            template<typename... TList>                                            
            struct apply                                                        
            {                                                                    
                using type = typename push_back<                                
                   typename _1::apply<TList...>::type,                            
                   typename _2::apply<TList...>::type>::type;                    
            };                                                                    
        };

    fold_s把固定的一堆参数传入时,push_back总能挑选到正确位置的参数。下面我们来看看一个奇妙的改变,这将会让你恍然大悟_1, _2占位符的设计和来历。

    让我们把上面的代码中所有_1,_2的地方全部调换位置,得到一个新的特化:

        template<>
        struct push_back< _2, _1 >
        {
            template<typename... TList>
            struct apply
            {
                using type = typename push_back<
                typename _2::apply<TList...>::type,
                typename _1::apply<TList...>::type>::type;
            };
        };

    使用这个新特化时,fold_s传入的第二个参数将被放到push_back的第一个参数位置,而_2位于push_back第一个参数的样子正好很形象的描述了这个行为。

    现在你明白了吧,push_back<_1,_2>和push_back<_2,_1>这2个特化组合在一起,让我们有了能够指称第一,第二个参数的能力。

    这确实非常帅。很可惜当参数个数n增长时,你需要覆盖n!种特化。参数为5时你将不得不写120个特化。boost使用preprocessor来自动生成这些类,你仔细观察上述类的结构,

    确实都是可以自动生成的。我表示看了preprocessor几眼就要瞎掉,有兴致再研究。下面是我写的更简单的自动构造宏:

    #ifndef HI_MPL_SUPPORT_LAMBDA_H_INCLUDE
    #define HI_MPL_SUPPORT_LAMBDA_H_INCLUDE
    
    
    #define SUPPORT_LAMBDA_1_IMPL(classname, A1)                        
        template<>                                                      
        struct classname##< A1 >                                        
        {                                                               
            template<typename... TList>                                 
            struct apply                                                
            {                                                           
                using type = typename classname##<                      
                typename A1::apply<TList...>::type>::type;              
            };                                                          
        };
    
    #define SUPPORT_LAMBDA_2_IMPL(classname, A1, A2)                            
        template<>                                                                
        struct classname##< A1, A2 >                                            
        {                                                                       
            template<typename... TList>                                            
            struct apply                                                        
            {                                                                    
                using type = typename classname##<                                
                   typename A1::apply<TList...>::type,                            
                   typename A2::apply<TList...>::type>::type;                    
            };                                                                    
        };
    
    #define SUPPORT_LAMBDA_3_IMPL(classname, A1, A2, A3)                        
        template<>                                                                
        struct classname##< A1, A2, A3 >                                        
        {                                                                        
            template<typename... TList>                                            
            struct apply                                                        
            {                                                                    
                using type = typename classname##<                                
                   typename A1::apply<TList...>::type,                            
                   typename A2::apply<TList...>::type,                            
                   typename A3::apply<TList...>::type>::type;                    
            };                                                                    
        };
    
    #define SUPPORT_LAMBDA_4_IMPL(classname, A1, A2, A3, A4)                    
        template<>                                                                
        struct classname##< A1, A2, A3, A4 >                                    
        {                                                                        
            template<typename... TList>                                            
            struct apply                                                        
            {                                                                    
                using type = typename classname##<                                
                   typename A1::apply<TList...>::type,                            
                   typename A2::apply<TList...>::type,                            
                   typename A3::apply<TList...>::type                            
                   typename A4::apply<TList...>::type>::type;                    
            };                                                                    
        };
    
    #define SUPPORT_LAMBDA_5_IMPL(classname, A1, A2, A3, A4, A5)                
        template<>                                                                
        struct classname##< A1, A2, A3, A4, A5 >                                
        {                                                                        
            template<typename... TList>                                            
            struct apply                                                        
            {                                                                    
                using type = typename classname##<                                
                   typename A1::apply<TList...>::type,                            
                   typename A2::apply<TList...>::type,                            
                   typename A3::apply<TList...>::type                            
                   typename A4::apply<TList...>::type                            
                   typename A5::apply<TList...>::type>::type;                    
            };                                                                    
        };
    
    
    #define SUPPORT_LAMBDA_1(classname, P)                            
        SUPPORT_LAMBDA_1_IMPL(classname, P##1)
    
    #define SUPPORT_LAMBDA_2(classname, P)                              
                SUPPORT_LAMBDA_2_IMPL(classname, P##1, P##2)          
                SUPPORT_LAMBDA_2_IMPL(classname, P##2, P##1)
    
    #define SUPPORT_LAMBDA_3(classname, P)                              
        SUPPORT_LAMBDA_3_IMPL(classname, P##1, P##2, P##3)            
        SUPPORT_LAMBDA_3_IMPL(classname, P##1, P##3, P##2)            
        SUPPORT_LAMBDA_3_IMPL(classname, P##2, P##1, P##3)            
        SUPPORT_LAMBDA_3_IMPL(classname, P##2, P##3, P##1)            
        SUPPORT_LAMBDA_3_IMPL(classname, P##3, P##1, P##2)            
        SUPPORT_LAMBDA_3_IMPL(classname, P##3, P##2, P##1)
    
    #define SUPPORT_LAMBDA_4(classname, P)                                    
        SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##2, P##3, P##4)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##2, P##4, P##3)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##3, P##2, P##4)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##3, P##4, P##2)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##4, P##3, P##2)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##4, P##2, P##3)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##1, P##3, P##4)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##1, P##4, P##3)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##3, P##1, P##4)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##3, P##4, P##1)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##4, P##1, P##3)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##4, P##3, P##1)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##1, P##2, P##4)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##1, P##4, P##2)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##2, P##1, P##4)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##2, P##4, P##1)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##4, P##1, P##2)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##4, P##2, P##1)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##1, P##2, P##3)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##1, P##3, P##2)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##2, P##1, P##3)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##2, P##3, P##1)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##3, P##1, P##2)            
        SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##3, P##2, P##1)
    
    
    #define SUPPORT_LAMBDA(classname, n, prefix)                     
        SUPPORT_LAMBDA_##n(classname, prefix)
    
    #endif
    View Code

    在每个你希望支持占位符的类定义后边,加上SUPPORT_LAMBDA这句宏,填入参数总数,占位符前缀(可包含命名空间,默认占位符必须以本身数字结束)。如下例子

        template<typename T, typename... TList> struct push_back;
    
        template<typename T, typename... TList>
        struct push_back< typelist<TList...>, T> 
        {
            typedef typelist<TList..., T> type;
        };
    
        template<>
        struct push_back< nulllist >
        {
            typedef nulllist type;
        };
        
        SUPPORT_LAMBDA(push_back, 2, placeholders::_); 

    以上这一套实现占位符的办法,比boost的要简洁了很多。当然还缺少匿名占位符这样的手法,这里提供一个简易的思路,望你有所得。

  • 相关阅读:
    go语言入门(3)运算符及流程控制
    go语言入门(2)数据类型
    go语言入门(1)
    ubuntu上软件下载慢,github下载慢
    密码基础知识(2)以RSA为例说明加密、解密、签名、验签
    让你减少焦虑的一首英文小诗
    使用脚本启动fabric时出错
    Hyperledger Fabric(5)ChainCode的编写步骤
    Hyperledger Fabric(4)链码ChainCode
    设计题专题总结
  • 原文地址:https://www.cnblogs.com/flytrace/p/4063714.html
Copyright © 2020-2023  润新知