diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index 59348fd8e..4c354f751 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -362,6 +362,43 @@ namespace typelist{ } }; + + /* ===== Helpers for partial function application ===== */ + + using std::tr1::_Placeholder; // what is the "official" way to import them? + + template + class PlaceholderTuple + { +// typedef typename Tuple::HeadType HeadType; +// typedef typename Tuple::ArgList TypeList; +// typedef typename Tuple::Tail::ArgList TailList; + typedef typename Tuple::Type TypeSeq; + typedef typename Tuple::TailType TailSeq; + + typedef typename PlaceholderTuple::PlaceholderSeq TailPlaceholderSeq; + + public: + typedef typename Prepend<_Placeholder, TailPlaceholderSeq>::Tuple PlaceholderSeq; + typedef Tuple Type; + + }; + + template + struct PlaceholderTuple, i> : PlaceholderTuple {}; + + template + struct PlaceholderTuple + { + typedef Types<> PlaceholderSeq; + typedef Tuple > Type; + }; + + + + + + } // (END) impl-namespace (func) @@ -397,6 +434,103 @@ namespace typelist{ }; + /** + * Partial function application + * Takes a function and a value tuple, + * using the latter to close function arguments + * either from the front (left) or aligned to the end + * of the function argument list. Result is a "reduced" function, + * expecting only the remaining "un-closed" arguments at invocation. + */ + template + class PApply + { + typedef typename func::_Fun::Args Args; + typedef typename func::_Fun::Ret Ret; + + enum { ARG_CNT = count::value + , VAL_CNT = count::value + , ROFFSET = (VAL_CNT < ARG_CNT)? ARG_CNT-VAL_CNT : 0 + }; + + typedef typename func::PlaceholderTuple::PlaceholderSeq::List Placeholders; + typedef typename Args::List ArgsList; + typedef typename VAL::List ValList; + + typedef typename Splice::List LeftReplaced; + typedef typename Splice::List RightReplaced; + + typedef typename Splice::Back LeftReduced; + typedef typename Splice::Front RightReduced; + + typedef Tuple TupleL; + typedef Tuple TupleR; + + typedef typename Tuple::Type ArgsL; + typedef typename Tuple::Type ArgsR; + + typedef tuple::BuildTuple BuildL; + typedef tuple::BuildTuple BuildR; + + + public: + typedef function::Sig> LeftReducedFunc; + typedef function::Sig> RightReducedFunc; + + + /** Contains the argument values, starting from left. + * Any remaining positions are occupied by binding placeholders */ + struct LeftReplacedArgs + : TupleL + { + LeftReplacedArgs (Tuple const& arg) + : TupleL ( BuildL::create(arg)) + { } + }; + + /** Contains the argument values, aligned to the end of the function argument list. + * Any remaining positions before are occupied by binding placeholders */ + struct RightReplacedArgs + : TupleR + { + RightReplacedArgs (Tuple const& arg) + : TupleR ( BuildR::create(arg)) + { } + }; + + /** do a partial function application, closing the first arguments + * f(a,b,c)->res + (a,b) yields f(c)->res + * + * @param f function, function pointer or functor + * @param arg value tuple, used to close function arguments starting from left + * @return new function object, holding copies of the values and using them at the + * closed arguments; on invocation, only the remaining arguments need to be supplied. + */ + static LeftReducedFunc + bindFront (SIG& f, Tuple const& arg) + { + LeftReplacedArgs params (arg); + return func::Apply::template bind (f, params.tupleCast()); + } + + /** do a partial function application, closing the last arguments + * f(a,b,c)->res + (b,c) yields f(a)->res + * + * @param f function, function pointer or functor + * @param arg value tuple, used to close function arguments starting from right + * @return new function object, holding copies of the values and using them at the + * closed arguments; on invocation, only the remaining arguments need to be supplied. + */ + static RightReducedFunc + bindBack (SIG& f, Tuple const& arg) + { + RightReplacedArgs params (arg); + return func::Apply::template bind (f, params.tupleCast()); + } + }; + + + /** * Closing a function over its arguments. * This is a small usage example or spin-off, @@ -430,6 +564,9 @@ namespace typelist{ namespace func { // ...some convenience shortcuts + + // helpers for specifying types in function declarations.... + template struct _Sig { @@ -445,6 +582,32 @@ namespace typelist{ typedef FunctionClosure Type; }; + template + struct _PapS + { + typedef typename _Fun::Ret Ret; + typedef typename _Fun::Args Args; + typedef typename Split::Head Arg; + typedef typename Split::Tail Rest; + typedef typename _Sig::Type Signature; + typedef function Function; + }; + + template + struct _PapE + { + typedef typename _Fun::Ret Ret; + typedef typename _Fun::Args Args; + typedef typename Split::End Arg; + typedef typename Split::Prefix Rest; + typedef typename _Sig::Type Signature; + typedef function Function; + }; + + + + + /* ========== function-style interface ============= */ /** build a TupleApplicator, which embodies the given * argument tuple and can be used to apply various @@ -483,7 +646,33 @@ namespace typelist{ typedef typename _Clo::Type Closure; return Closure (f,args); } - } + + + /** close the given function over the first argument */ + template + typename _PapS::Function + applyFirst (SIG& f, ARG arg) + { + typedef typename _PapS::Arg ArgType; + typedef Types ArgTypeSeq; + typedef Tuple ArgTuple; + ArgTuple val(arg); + return PApply::bindFront (f, val); + } + + /** close the given function over the last argument */ + template + typename _PapE::Function + applyLast (SIG& f, ARG arg) + { + typedef typename _PapE::Arg ArgType; + typedef Types ArgTypeSeq; + typedef Tuple ArgTuple; + ArgTuple val(arg); + return PApply::bindBack (f, val); + } + + } // (END) namespace func diff --git a/tests/lib/meta/function-composition-test.cpp b/tests/lib/meta/function-composition-test.cpp index 85701bace..aa3617744 100644 --- a/tests/lib/meta/function-composition-test.cpp +++ b/tests/lib/meta/function-composition-test.cpp @@ -163,223 +163,129 @@ namespace test { { DISPLAY (List1); DISPLAY (List2); - ; - ASSERT (6 == (getNumberz<1,2,3> (Num<1>(), Num<2>(), Num<3>()))); - ASSERT (6 == (getNumberz<1,1,1> (Num<1>(), Num<1>(2), Num<1>(3)))); + + ASSERT (6 == (fun1<1,2,3> (_1_, _2_, _3_)).o_ ); + ASSERT (6 == (fun1<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ ); + + ASSERT ( 1 == fun2 (fun1<1> (_1_)) ); + ASSERT ( 3 == fun2 (fun1<1,2> (_1_, _2_)) ); + ASSERT ( 6 == fun2 (fun1<1,2,3> (_1_, _2_, _3_)) ); + ASSERT (10 == fun2 (fun1<1,2,3,4> (_1_, _2_, _3_, _4_)) ); + ASSERT (15 == fun2 (fun1<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) ); + + ASSERT ( 9 == fun2 (fun1<2,3,4> (_2_, _3_, _4_)) ); + ASSERT (18 == fun2 (fun1<5,6,7> (_5_, _6_, _7_)) ); + ASSERT (24 == fun2 (fun1<9,8,7> (_9_, _8_, _7_)) ); } void - check_signatureTypeManip () + check_partialApplication () { - typedef int someFunc(Num<5>,Num<9>); - typedef FunctionSignature >::Ret RetType; // should be int - typedef FunctionSignature >::Args Args; - DISPLAY (Args); + // Because the code of the partial function application is very technical, + // the following might serve as explanation what actually happens.... + // (and actually it's a leftover from initial debugging) - typedef Prepend, Args>::Tuple NewArgs; // manipulate the argument type(s) - DISPLAY (NewArgs); + typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function - typedef FunctionTypedef::Sig NewSig; // re-build a new function signature + typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument + typedef function F23; // and a tr1::function object to hold such a function - NewSig& fun = getNumberz<1,5,9>; //...which is compatible to an existing real function signature! + Sig123& f =fun1<1,2,3>; // the actual input: a reference to the bare function - ASSERT (1+5+9 == fun(Num<1>(), Num<5>(), Num<9>())); + + // Version1: do a direct argument binding----------------- // + + typedef std::tr1::_Placeholder<1> PH1; // tr1::function argument placeholders + typedef std::tr1::_Placeholder<2> PH2; + + PH1 ph1; // these empty structs are used to mark the arguments to be kept "open" + PH2 ph2; + Num<1> num18 (18); // ...and this value is for closing the first function argument + + F23 fun_23 = std::tr1::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value) + , ph1 + , ph2 + ); + + int res = 0; + res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments + ASSERT (23 == res); + + + + // Version2: extract the binding arguments from a tuple--- // + + typedef Tuple, PH1, PH2> > PartialArg; // Tuple type to hold the binding values. Note the placeholder types + PartialArg arg(num18); // Value for partial application (the placeholders are default constructed) + + fun_23 = std::tr1::bind (f, tuple::element<0>(arg) // now extract the values to bind from this tuple + , tuple::element<1>(arg) + , tuple::element<2>(arg) + ); + res = 0; + res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor.... + ASSERT (23 == res); + + + + + // Version3: let the PApply-template do the work for us--- // + + typedef Types > ArgTypes; // now package just the argument(s) to be applied into a tuple + Tuple args_to_bind (Num<1>(18)); + + fun_23 = PApply::bindFront (f , args_to_bind); + // "bindFront" will close the parameters starting from left.... + res = 0; + res = fun_23 (_2_,_3_).o_; // invoke the resulting functor... + ASSERT (23 == res); + + + + + + + // Version4: as you'd typically do it in real life-------- // + + fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value + + res = 0; + res = fun_23 (_2_,_3_).o_; // invoke the resulting functor... + ASSERT (23 == res); + + + + // what follows is the real unit test... + function func123 (f); + fun_23 = func::applyFirst (func123, Num<1>(19)); + res = fun_23 (_2_,_3_).o_; + ASSERT (24 == res); +#if false //////////////TODO silly bug in the Split template! + typedef function(Num<1>, Num<2>)> F12; + F12 fun_12 = func::applyLast(f, Num<3>(20)); + res = fun_12 (_1_,_2_).o_; + ASSERT (23 == res); + + fun_12 = func::applyLast(func123, Num<3>(21)); + res = fun_12 (_1_,_2_).o_; + ASSERT (24 == res); +#endif } + void - check_applyFree () + check_functionalComposition () { - cout << "\t:\n\t: ---Apply---\n"; - - Tuple > tup0 ; - Tuple > tup1 (11); - Tuple > tup2 (11,12); - Tuple > tup3 (11,12,13); - DUMPVAL (tup0); - DUMPVAL (tup1); - DUMPVAL (tup2); - DUMPVAL (tup3); - - ASSERT (-1 == func::Apply<0>::invoke (fun0, tup0) ); - ASSERT (11 == func::Apply<1>::invoke (fun1, tup1) ); - ASSERT (11+12 == func::Apply<2>::invoke (fun2, tup2) ); - ASSERT (11+12+13 == func::Apply<3>::invoke (fun3, tup3) ); - - ASSERT (-1 == TupleApplicator (tup0) (fun0) ); - ASSERT (11 == TupleApplicator (tup1) (fun1) ); - ASSERT (11+12 == TupleApplicator (tup2) (fun2) ); - ASSERT (11+12+13 == TupleApplicator (tup3) (fun3) ); - - ASSERT (-1 == func::apply(fun0, tup0) ); - ASSERT (11 == func::apply(fun1, tup1) ); - ASSERT (11+12 == func::apply(fun2, tup2) ); - ASSERT (11+12+13 == func::apply(fun3, tup3) ); - +// ASSERT (1+5+9 == fun(Num<1>(), Num<5>(), Num<9>())); } - - void - check_applyFunc () - { - Tuple > tup0 ; - Tuple > tup1 (11); - Tuple > tup2 (11,12); - Tuple > tup3 (11,12,13); - function functor0 (fun0); - function functor1 (fun1); - function functor2 (fun2); - function functor3 (fun3); - - ASSERT (-1 == func::Apply<0>::invoke (functor0, tup0) ); - ASSERT (11 == func::Apply<1>::invoke (functor1, tup1) ); - ASSERT (11+12 == func::Apply<2>::invoke (functor2, tup2) ); - ASSERT (11+12+13 == func::Apply<3>::invoke (functor3, tup3) ); - - ASSERT (-1 == TupleApplicator (tup0) (functor0) ); - ASSERT (11 == TupleApplicator (tup1) (functor1) ); - ASSERT (11+12 == TupleApplicator (tup2) (functor2) ); - ASSERT (11+12+13 == TupleApplicator (tup3) (functor3) ); - - ASSERT (-1 == func::apply(functor0, tup0) ); - ASSERT (11 == func::apply(functor1, tup1) ); - ASSERT (11+12 == func::apply(functor2, tup2) ); - ASSERT (11+12+13 == func::apply(functor3, tup3) ); - - } - - - void - check_bindFree () - { - cout << "\t:\n\t: ---Bind----\n"; - - Tuple > tup0 ; - Tuple > tup1 (11); - Tuple > tup2 (11,12); - Tuple > tup3 (11,12,13); - - typedef function BoundFun; - - BoundFun functor0 = func::Apply<0>::bind (fun0, tup0); - BoundFun functor1 = func::Apply<1>::bind (fun1, tup1); - BoundFun functor2 = func::Apply<2>::bind (fun2, tup3); - BoundFun functor3 = func::Apply<3>::bind (fun3, tup3); - - ASSERT (-1 == functor0() ); - ASSERT (11 == functor1() ); - ASSERT (11+12 == functor2() ); - ASSERT (11+12+13 == functor3() ); - - functor0 = TupleApplicator (tup0).bind (fun0); - functor1 = TupleApplicator (tup1).bind (fun1); - functor2 = TupleApplicator (tup2).bind (fun2); - functor3 = TupleApplicator (tup3).bind (fun3); - - ASSERT (-1 == functor0() ); - ASSERT (11 == functor1() ); - ASSERT (11+12 == functor2() ); - ASSERT (11+12+13 == functor3() ); - - } - - - void - check_bindFunc () - { - Tuple > tup0 ; - Tuple > tup1 (11); - Tuple > tup2 (11,12); - Tuple > tup3 (11,12,13); - function unbound_functor0 (fun0); - function unbound_functor1 (fun1); - function unbound_functor2 (fun2); - function unbound_functor3 (fun3); - - typedef function BoundFun; - - BoundFun functor0 = func::Apply<0>::bind (unbound_functor0, tup0); - BoundFun functor1 = func::Apply<1>::bind (unbound_functor1, tup1); - BoundFun functor2 = func::Apply<2>::bind (unbound_functor2, tup3); - BoundFun functor3 = func::Apply<3>::bind (unbound_functor3, tup3); - - ASSERT (-1 == functor0() ); - ASSERT (11 == functor1() ); - ASSERT (11+12 == functor2() ); - ASSERT (11+12+13 == functor3() ); - - functor0 = TupleApplicator (tup0).bind (unbound_functor0); - functor1 = TupleApplicator (tup1).bind (unbound_functor1); - functor2 = TupleApplicator (tup2).bind (unbound_functor2); - functor3 = TupleApplicator (tup3).bind (unbound_functor3); - - ASSERT (-1 == functor0() ); - ASSERT (11 == functor1() ); - ASSERT (11+12 == functor2() ); - ASSERT (11+12+13 == functor3() ); - - } - - - void - build_closure () - { - Tuple > tup0 ; - Tuple > tup1 (11); - Tuple > tup2 (11,12); - Tuple > tup3 (11,12,13); - - FunctionClosure clo0 (fun0,tup0); - FunctionClosure clo1 (fun1,tup1); - FunctionClosure clo2 (fun2,tup2); - FunctionClosure clo3 (fun3,tup3); - - ASSERT (-1 == clo0() ); - ASSERT (11 == clo1() ); - ASSERT (11+12 == clo2() ); - ASSERT (11+12+13 == clo3() ); - - function unbound_functor0 (fun0); - function unbound_functor1 (fun1); - function unbound_functor2 (fun2); - function unbound_functor3 (fun3); - - clo0 = FunctionClosure (unbound_functor0,tup0); - clo1 = FunctionClosure (unbound_functor1,tup1); - clo2 = FunctionClosure (unbound_functor2,tup2); - clo3 = FunctionClosure (unbound_functor3,tup3); - - ASSERT (-1 == clo0() ); - ASSERT (11 == clo1() ); - ASSERT (11+12 == clo2() ); - ASSERT (11+12+13 == clo3() ); - - ASSERT (-1 == func::closure(fun0,tup0) () ); - ASSERT (11 == func::closure(fun1,tup1) () ); - ASSERT (11+12 == func::closure(fun2,tup2) () ); - ASSERT (11+12+13 == func::closure(fun3,tup3) () ); - - ASSERT (-1 == func::closure(unbound_functor0,tup0) () ); - ASSERT (11 == func::closure(unbound_functor1,tup1) () ); - ASSERT (11+12 == func::closure(unbound_functor2,tup2) () ); - ASSERT (11+12+13 == func::closure(unbound_functor3,tup3) () ); - - - // finally combine all techniques.... - typedef Tuple::Type NumberzArg; - typedef FunctionTypedef::Sig NumberzSig; - Tuple numberzTup (Num<5>(22), Num<6>(33), Num<7>(44)); - - FunctionClosure numClo (getNumberz<5,6,7>, numberzTup ); - - ASSERT (22+33+44 == numClo() ); - } }; /** Register this test class... */ - LAUNCHER (FunctionClosure_test, "unit common"); + LAUNCHER (FunctionComposition_test, "unit common");