boost::function で Loki Chain のような処理
ここまでは考えた。
#include <iostream> #include <boost/function.hpp> #include <boost/functional.hpp> #include <boost/function_types/result_type.hpp> #include <boost/bind.hpp> #include <boost/utility/result_of.hpp> #include <boost/fusion/include/vector.hpp> #include <boost/fusion/include/at_c.hpp> // boost::bind() の戻り値が boost::function_type::result_type に対応してないので自前で実装 template<typename func_t> struct result_of{ typedef typename boost::function_types::result_type<func_t>::type type; }; template<class R, class F, class L> struct result_of<boost::_bi::bind_t<R, F, L> >{ typedef typename boost::_bi::bind_t<R, F, L>::result_type type; }; // 2つの func を保持するクラス // 戻り値は、fusion::vector<> に詰め込む template< typename func1_t, typename func2_t > struct chainer{ private: typedef typename result_of<func1_t>::type func1_result; typedef typename result_of<func2_t>::type func2_result; public: typedef typename boost::mpl::if_< boost::is_void<func1_result>, void, boost::fusion::vector< func1_result, func2_result > >::type result_type; chainer(func1_t& f1, func2_t& f2) : func1(f1), func2(f2){} result_type operator ()(){ return call<result_type>(); } template<typename T1, typename T2> result_type operator ()(T1 t1, T2 t2){ return call<result_type>(t1, t2); } private: // 戻り値の有無で切り替え template<typename result_type> result_type call(typename boost::enable_if_c<!boost::is_void<result_type>::value>::type* = NULL){ return result_type(func1(), func2()); } template<typename result_type> result_type call(typename boost::enable_if<boost::is_void<result_type> >::type* = NULL){ func1(), func2(); } template<typename result_type, typename T1, typename T2> result_type call(T1 t1, T2 t2, typename boost::enable_if_c<!boost::is_void<result_type>::value>::type* = NULL){ return result_type(func1(t1, t2), func2(t1, t2)); } template<typename result_type, typename T1, typename T2> result_type call(T1 t1, T2 t2, typename boost::enable_if<boost::is_void<result_type> >::type* = NULL){ func1(t1, t2), func2(t1, t2); } func1_t& func1; func2_t& func2; }; // chainer の特殊化 template<typename func1_t, typename func2_t> struct result_of<chainer<func1_t, func2_t> >{ typedef typename chainer<func1_t, func2_t>::result_type type; }; template< typename func1_t, typename func2_t > chainer<func1_t, func2_t> chain(func1_t& f1, func2_t& f2){ return chainer<func1_t, func2_t>(f1, f2); } int add(int n, int m){ return n + m; } int sub(int n, int m){ return n - m; } float division(int n, int m){ return static_cast<float>(n) / static_cast<float>(m); } struct hoge{ void call(){ std::cout << "hoge::call()" << std::endl; } }; int main(){ namespace fusion = boost::fusion; typedef boost::fusion::vector<int, int> result_t; // 2つの関数をぶっこむ // 戻り値は、fusion::vector<int, int> boost::function<result_t(int, int)> func = chain(add, sub); result_t result = func(10, 5); assert( fusion::at_c<0>(result) == add(10, 5) ); assert( fusion::at_c<1>(result) == sub(10, 5) ); // バインドしてみたり boost::function<result_t()> func2 = chain(boost::bind(add, 3, 5), boost::bind(sub, 8, 2) ); result_t result2 = func2(); assert( fusion::at_c<0>(result2) == boost::bind(add, 3, 5)() ); assert( fusion::at_c<1>(result2) == boost::bind(sub, 8, 2)() ); // 引数型が同じなら戻り値型が違っても OK typedef boost::fusion::vector<int, float> result2_t; boost::function<result2_t(int, int)> func3 = chain(add, division); result2_t result3 = func3(5, 2); assert( fusion::at_c<0>(result3) == add(5, 2) ); assert( fusion::at_c<1>(result3) == division(5, 2) ); // 一応、3つ以上もつなげることが出来るが・・・ typedef fusion::vector<int, fusion::vector<int, float> > result4_t; boost::function<result4_t(int, int)> func4 = chain(add, chain(sub, division) ); result4_t result4 = func4(7, 2); assert( fusion::at_c<0>(result4) == add(7, 2) ); assert( fusion::at_c<0>(fusion::at_c<1>(result4)) == sub(7, 2) ); assert( fusion::at_c<1>(fusion::at_c<1>(result4)) == division(7, 2) ); // メソッド hoge h; boost::function<void()> func5 = chain( boost::bind(&hoge::call, h), boost::bind(&hoge::call, h) ); func5(); return 0; }
素直に std::vector にでも突っ込んだほうがいいと思う。
2つの関数を保持する関数オブジェクトを作って、それの内部で呼び出しているだけです。
ロジック自体は、Loki の Chain そのまま。
Loki とは違い、関数の戻り値を fusion で返してるのでウマーな気がする。
今は引数なしと引数2つにしか対応してないから、BOOST_PP でなんとかしてみたい。
しかし、boost::result_of で boost::bind() の戻り値型が取得できないけどソースを読む気がしないな…。
(正確に言うと、読んでも分からない)
[boost]
ver 1.44.0
[参照]
http://www.kmonos.net/alang/boost/classes/result_of.html