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的要簡潔了很多。當然還缺少匿名占位符這樣的手法,這里提供一個簡易的思路,望你有所得。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM