程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> c++11模擬boost元占位符

c++11模擬boost元占位符

編輯:C++入門知識

c++11模擬boost元占位符


准備實現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>::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 STRINGLIZE_CAT_TOKEN(s1, s2, token) s1##token##s2 #define STRINGLIZE_CAT(s1, s2) s1##s2   #define ENUM_SHIFTED_PARAMS_MPL(n, prefix)                      \     ENUM_SHIFTED_PARAMS_MPL_##n(prefix)   #define ENUM_SHIFTED_PARAMS_MPL_1(prefix) prefix##1 #define ENUM_SHIFTED_PARAMS_MPL_2(prefix) prefix##1, prefix##2 #define ENUM_SHIFTED_PARAMS_MPL_3(prefix) prefix##1, prefix##2, prefix##3 #define ENUM_SHIFTED_PARAMS_MPL_4(prefix) prefix##1, prefix##2, prefix##3, prefix##4 #define ENUM_SHIFTED_PARAMS_MPL_5(prefix) prefix##1, prefix##2, prefix##3, prefix##4, prefix##5     #define SUPPORT_LAMBDA(classname, n, prefix)                     \     SUPPORT_LAMBDA_##n(classname, prefix)     #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)         #endif 復制代碼 在每個你希望支持占位符的類定義後邊,加上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的要簡潔了很多。當然還缺少匿名占位符這樣的手法,這裡提供一個簡易的思路,望你有所得。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved