ファンクタとラムダ双方を兼ねるようなファンクタって作れるのかにゃ?つまり一つでもプレースホルダを引数に取ったらラムダで,全部の引数が普通の引数ならファンクタとして普通に値を評価するという・・・.
Boost.Lambdaの実装読めば簡単にできそうな気がするけれど,あの実装読むのまんどくせ.
#できた
意外と簡単だったというか単にboost::lambda::bindでやっていることをまねするだけだった.後はぐちゃぐちゃしてる部分をCRTPで汎用ライブラリ化すれば「お手軽ラムダっ!アナタが定義したファンクタも今日からラムダっ!!bind不要っ!!」みたいな.
#include <iostream> #include <boost/mpl/or.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/void.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/utility/enable_if.hpp> #include <boost/lambda/bind.hpp> template<class T> struct is_boost_lambda_placeholder : public boost::mpl::bool_<false> { }; template<int I> struct is_boost_lambda_placeholder< boost::lambda::lambda_functor< boost::lambda::placeholder<I> > > : public boost::mpl::bool_<true> { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct is_lambda : public boost::mpl::or_< is_boost_lambda_placeholder<A0> , is_boost_lambda_placeholder<A1> , is_boost_lambda_placeholder<A2> , is_boost_lambda_placeholder<A3> , is_boost_lambda_placeholder<A4> > { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct enable_if_lambda : public boost::enable_if< is_lambda<A0, A1, A2, A3, A4> > { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct disable_if_lambda : public boost::disable_if< is_lambda<A0, A1, A2, A3, A4> > { }; struct plus_ { // 戻り値型の計算が適当なので良い子は真似しないように. template<class FArgs> struct result; template<class LHS, class RHS> struct result< plus_(LHS, RHS) > { typedef typename boost::remove_cv< typename boost::remove_reference<LHS>::type >::type type; }; template<class FArgs> struct sig { typedef typename boost::tuples::element<1, FArgs>::type type; }; template<class LHS, class RHS> typename result< plus_(LHS const &, RHS const &) >::type operator()( LHS const &lhs, RHS const &rhs , typename disable_if_lambda<LHS, RHS>::type * = 0 ) const { return lhs + rhs; } template<class Arg1, class Arg2> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<3, boost::lambda::function_action<3> > , typename boost::lambda::detail::bind_tuple_mapper< plus_ const, Arg1 const, Arg2 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2 , typename enable_if_lambda<Arg1, Arg2>::type * = 0 ) const { return boost::lambda::bind(*this, a1, a2); } }; plus_ const plus = plus_(); int main() { using namespace boost::lambda; int a = 2; int b = 3; std::cout << plus(a, b) << std::endl; // 普通のファンクタとして機能 std::cout << plus(_1, b)(a) << std::endl; // ラムダとして機能 std::cout << plus(a, _1)(b) << std::endl; // ラムダとして機能 std::cout << plus(_1, _2)(a, b) << std::endl; // ラムダとして機能 return 0; }
アイデア10分,実装90分にしてはなかなかうまーな予感.
#CRTP化してみた.
#include <boost/mpl/or.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/void.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/utility/enable_if.hpp> #include <boost/utility/result_of.hpp> #include <boost/lambda/bind.hpp> template<class T> struct is_boost_lambda_placeholder : public boost::mpl::bool_<false> { }; template<int I> struct is_boost_lambda_placeholder< boost::lambda::lambda_functor< boost::lambda::placeholder<I> > > : public boost::mpl::bool_<true> { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct is_lambda : public boost::mpl::or_< is_boost_lambda_placeholder<A0> , is_boost_lambda_placeholder<A1> , is_boost_lambda_placeholder<A2> , is_boost_lambda_placeholder<A3> , is_boost_lambda_placeholder<A4> > { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct enable_if_lambda : public boost::enable_if< is_lambda<A0, A1, A2, A3, A4> > { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct disable_if_lambda : public boost::disable_if< is_lambda<A0, A1, A2, A3, A4> > { }; template<class Derived> struct enable_lambda { private: template<class FArgs, int arity> struct sig_impl; template<class FArgs> struct sig_impl<FArgs, 1> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type() > { }; template<class FArgs> struct sig_impl<FArgs, 2> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 3> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 4> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & , typename boost::tuples::element<3, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 5> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & , typename boost::tuples::element<3, FArgs>::type & , typename boost::tuples::element<4, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 6> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & , typename boost::tuples::element<3, FArgs>::type & , typename boost::tuples::element<4, FArgs>::type & , typename boost::tuples::element<5, FArgs>::type & ) > { }; public: template<class FArgs> struct sig : sig_impl<FArgs, boost::tuples::length<FArgs>::value> { }; template<class Arg1> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<2, boost::lambda::function_action<2> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const >::type > > const operator()( Arg1 const &a1 , typename enable_if_lambda<Arg1>::type * = 0 ) const { return boost::lambda::bind(*static_cast<Derived const *>(this), a1); } template<class Arg1, class Arg2> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<3, boost::lambda::function_action<3> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const, Arg2 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2 , typename enable_if_lambda<Arg1, Arg2>::type * = 0 ) const { return boost::lambda::bind(*static_cast<Derived const *>(this), a1, a2); } template<class Arg1, class Arg2, class Arg3> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<4, boost::lambda::function_action<4> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const, Arg2 const, Arg3 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2, Arg3 const &a3 , typename enable_if_lambda<Arg1, Arg2, Arg3>::type * = 0 ) const { return boost::lambda::bind(*static_cast<Derived const *>(this), a1, a2, a3); } template<class Arg1, class Arg2, class Arg3, class Arg4> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<5, boost::lambda::function_action<5> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const, Arg2 const, Arg3 const, Arg4 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2, Arg3 const &a3, Arg4 const &a4 , typename enable_if_lambda<Arg1, Arg2, Arg3, Arg4>::type * = 0 ) const { return boost::lambda::bind( *static_cast<Derived const *>(this), a1, a2, a3, a4 ); } template<class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<5, boost::lambda::function_action<6> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const , Arg1 const, Arg2 const, Arg3 const, Arg4 const, Arg5 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2 , Arg3 const &a3, Arg4 const &a4, Arg5 const &a5 , typename enable_if_lambda<Arg1, Arg2, Arg3, Arg4, Arg5>::type * = 0 ) const { return boost::lambda::bind( *static_cast<Derived const *>(this), a1, a2, a3, a4, a5 ); } }; // struct enable_lambda ////////// 以上ライブラリコード ////////// ////////// 以下ユーザコード ////////// #include <iostream> struct plus_ : public enable_lambda<plus_> // CRTPでラムダ化 { using enable_lambda<plus_>::operator(); // これがどうしても必要.む〜. // 戻り値型計算部(ちょ〜適当につきマネしないように) template<class FArgs> struct result; template<class LHS, class RHS> struct result< plus_(LHS, RHS) > { typedef typename boost::remove_cv< typename boost::remove_reference<LHS>::type >::type type; }; template<class LHS, class RHS> struct result< plus_ const(LHS, RHS) > { typedef typename boost::remove_cv< typename boost::remove_reference<LHS>::type >::type type; }; // operator() 本体 template<class LHS, class RHS> typename result< plus_(LHS const &, RHS const &) >::type operator()( LHS const &lhs, RHS const &rhs , typename disable_if_lambda<LHS, RHS>::type * = 0 // これも必要.む〜. ) const { return lhs + rhs; } }; plus_ const plus = plus_(); int main() { using namespace boost::lambda; int a = 2; int b = 3; std::cout << plus(a, b) << std::endl; std::cout << plus(_1, b)(a) << std::endl; std::cout << plus(a, _1)(b) << std::endl; std::cout << plus(_1, _2)(a, b) << std::endl; return 0; }
細かいところだとBoost.Lambdaのsigが実引数が左辺値か右辺値かを識別する能力がないのに対して,boost::result_ofはその識別ができるため極まれにマズイ場合がある.(ファンクタが,実引数が左辺値か右辺値かに基づくオーバーロードを行っていてそれぞれ戻り値型が異なる場合.ただし,こんな状況は普通は極希少)
#lambdaだと転送関数内で一旦変数に保持されるから,必ず左辺値で呼ばれて問題にならない気がしてきた.
あと,ファンクタのconstnessの取り扱いも微妙な予感.
#む〜?というか直接プレースホルダが与えられたときだけじゃなくて,間接的にラムダな場合があるじゃん.ぶ〜ぶ〜ぶ〜.引数がlambda_functorかどうかで判断しないといけにゃいにゃ〜.
#ということで修正.多分決定稿〜.
#include <boost/mpl/or.hpp> #include <boost/mpl/bool.hpp> #include <boost/mpl/void.hpp> #include <boost/type_traits/remove_cv.hpp> #include <boost/utility/enable_if.hpp> #include <boost/utility/result_of.hpp> #include <boost/lambda/bind.hpp> template<class T> struct is_boost_lambda_lambda_functor : public boost::mpl::bool_<false> { }; template<class Base> struct is_boost_lambda_lambda_functor< boost::lambda::lambda_functor<Base> > : public boost::mpl::bool_<true> { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct is_lambda : public boost::mpl::or_< is_boost_lambda_lambda_functor<A0> , is_boost_lambda_lambda_functor<A1> , is_boost_lambda_lambda_functor<A2> , is_boost_lambda_lambda_functor<A3> , is_boost_lambda_lambda_functor<A4> > { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct enable_if_lambda : public boost::enable_if< is_lambda<A0, A1, A2, A3, A4> > { }; template< class A0 , class A1 = boost::mpl::void_ , class A2 = boost::mpl::void_ , class A3 = boost::mpl::void_ , class A4 = boost::mpl::void_ > struct disable_if_lambda : public boost::disable_if< is_lambda<A0, A1, A2, A3, A4> > { }; template<class Derived> struct enable_lambda { private: template<class FArgs, int arity> struct sig_impl; template<class FArgs> struct sig_impl<FArgs, 1> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type() > { }; template<class FArgs> struct sig_impl<FArgs, 2> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 3> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 4> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & , typename boost::tuples::element<3, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 5> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & , typename boost::tuples::element<3, FArgs>::type & , typename boost::tuples::element<4, FArgs>::type & ) > { }; template<class FArgs> struct sig_impl<FArgs, 6> : public boost::result_of< typename boost::tuples::element<0, FArgs>::type( typename boost::tuples::element<1, FArgs>::type & , typename boost::tuples::element<2, FArgs>::type & , typename boost::tuples::element<3, FArgs>::type & , typename boost::tuples::element<4, FArgs>::type & , typename boost::tuples::element<5, FArgs>::type & ) > { }; public: template<class FArgs> struct sig : sig_impl<FArgs, boost::tuples::length<FArgs>::value> { }; template<class Arg1> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<2, boost::lambda::function_action<2> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const >::type > > const operator()( Arg1 const &a1 , typename enable_if_lambda<Arg1>::type * = 0 ) const { return boost::lambda::bind(*static_cast<Derived const *>(this), a1); } template<class Arg1, class Arg2> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<3, boost::lambda::function_action<3> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const, Arg2 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2 , typename enable_if_lambda<Arg1, Arg2>::type * = 0 ) const { return boost::lambda::bind(*static_cast<Derived const *>(this), a1, a2); } template<class Arg1, class Arg2, class Arg3> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<4, boost::lambda::function_action<4> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const, Arg2 const, Arg3 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2, Arg3 const &a3 , typename enable_if_lambda<Arg1, Arg2, Arg3>::type * = 0 ) const { return boost::lambda::bind(*static_cast<Derived const *>(this), a1, a2, a3); } template<class Arg1, class Arg2, class Arg3, class Arg4> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<5, boost::lambda::function_action<5> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const, Arg1 const, Arg2 const, Arg3 const, Arg4 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2, Arg3 const &a3, Arg4 const &a4 , typename enable_if_lambda<Arg1, Arg2, Arg3, Arg4>::type * = 0 ) const { return boost::lambda::bind( *static_cast<Derived const *>(this), a1, a2, a3, a4 ); } template<class Arg1, class Arg2, class Arg3, class Arg4, class Arg5> boost::lambda::lambda_functor< boost::lambda::lambda_functor_base< boost::lambda::action<5, boost::lambda::function_action<6> > , typename boost::lambda::detail::bind_tuple_mapper< Derived const , Arg1 const, Arg2 const, Arg3 const, Arg4 const, Arg5 const >::type > > const operator()( Arg1 const &a1, Arg2 const &a2 , Arg3 const &a3, Arg4 const &a4, Arg5 const &a5 , typename enable_if_lambda<Arg1, Arg2, Arg3, Arg4, Arg5>::type * = 0 ) const { return boost::lambda::bind( *static_cast<Derived const *>(this), a1, a2, a3, a4, a5 ); } }; // struct enable_lambda ////////// 以上ライブラリコード ////////// ////////// 以下ユーザコード ////////// #include <iostream> #include <cassert> struct plus_ : public enable_lambda<plus_> // CRTPでラムダ化 { using enable_lambda<plus_>::operator(); // これがどうしても必要.む〜. // 戻り値型計算部(ちょ〜適当につきマネしないように) template<class FArgs> struct result; template<class LHS, class RHS> struct result< plus_(LHS, RHS) > { typedef typename boost::remove_cv< typename boost::remove_reference<LHS>::type >::type type; }; template<class LHS, class RHS> struct result< plus_ const(LHS, RHS) > { typedef typename boost::remove_cv< typename boost::remove_reference<LHS>::type >::type type; }; // operator() 本体 template<class LHS, class RHS> typename result< plus_(LHS const &, RHS const &) >::type operator()( LHS const &lhs, RHS const &rhs , typename disable_if_lambda<LHS, RHS>::type * = 0 // これも必要.む〜. ) const { return lhs + rhs; } }; plus_ const plus = plus_(); int main() { using namespace boost::lambda; int a = 2; int b = 3; int c = 4; int d = 5; assert(plus(a, b) == 5); assert(plus(a, _1)(b) == 5); assert(plus(_1, b)(a) == 5); assert(plus(_1, _2)(a, b) == 5); assert(plus(a, plus(b, c)) == 9); assert(plus(a, plus(b, _1))(c) == 9); assert(plus(a, plus(_1, c))(b) == 9); assert(plus(_1, plus(b, c))(a) == 9); assert(plus(a, plus(_1, _2))(b, c) == 9); assert(plus(_1, plus(b, _2))(a, c) == 9); assert(plus(_1, plus(_2, c))(a, b) == 9); assert(plus(_1, plus(_2, _3))(a, b, c) == 9); assert(plus(plus(a, b), c) == 9); assert(plus(plus(a, b), _1)(c) == 9); assert(plus(plus(a, _1), c)(b) == 9); assert(plus(plus(_1, b), c)(a) == 9); assert(plus(plus(a, _1), _2)(b, c) == 9); assert(plus(plus(_1, b), _2)(a, c) == 9); assert(plus(plus(_1, _2), c)(a, b) == 9); assert(plus(plus(_1, _2), _3)(a, b, c) == 9); assert(plus(plus(a, b), plus(c, d)) == 14); assert(plus(plus(a, b), plus(c, _1))(d) == 14); assert(plus(plus(a, b), plus(_1, d))(c) == 14); assert(plus(plus(a, _1), plus(c, d))(b) == 14); assert(plus(plus(_1, b), plus(c, d))(a) == 14); assert(plus(plus(a, b), plus(_1, _2))(c, d) == 14); assert(plus(plus(a, _1), plus(c, _2))(b, d) == 14); assert(plus(plus(a, _1), plus(_2, d))(b, c) == 14); assert(plus(plus(_1, b), plus(c, _2))(a, d) == 14); assert(plus(plus(_1, b), plus(_2, d))(a, c) == 14); assert(plus(plus(_1, _2), plus(c, d))(a, b) == 14); assert(plus(plus(a, _1), plus(_2, _3))(b, c, d) == 14); assert(plus(plus(_1, b), plus(_2, _3))(a, c, d) == 14); assert(plus(plus(_1, _2), plus(c, _3))(a, b, d) == 14); assert(plus(plus(_1, _2), plus(_3, d))(a, b, c) == 14); return 0; }