/* FUNCTION-CLOSURE.hpp - metaprogramming tools for closing a function over given arguments Copyright (C) Lumiera.org 2009, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /** @file function-closure.hpp ** Partial function application and building a complete function closure. ** This is a addendum to (and thin wrapper for) ``, supporting ** the case when a function should be _closed_ over (partially or all) arguments, ** where especially the parameter values to close on are provided as a tuple. ** Additionally, we allow for composing (chaining) of two functions. ** ** Because we have to deal with arbitrary functions and arbitrary parameter types, ** we need a lot of repetitive code to "catch" functions from zero to nine arguments. ** At the bottom of this header, you'll find a function-style interface, which ** wraps up all these technicalities. ** ** @todo the implementation is able to handle partial application with N arguments, ** but currently we need just one argument, thus only this case was wrapped ** up into a convenient functions func::applyFirst and func::applyLast ** ** @see control::CommandDef usage example ** @see function.hpp ** */ #ifndef LIB_META_FUNCTION_CLOSURE_H #define LIB_META_FUNCTION_CLOSURE_H #include "lib/meta/function.hpp" #include "lib/meta/tuple-helper.hpp" #include #include namespace lib { namespace meta{ namespace func{ using std::function; using std::tuple; namespace { // helpers for binding and applying a function to an argument tuple using std::get; /** * this Helper with repetitive specialisations for up to nine arguments * is used either to apply a function to arguments given as a tuple, or * to create the actual closure (functor) over all function arguments. */ template struct Apply; template<> //__________________________________ struct Apply<0> ///< Apply function without Arguments { template static RET invoke (FUN& f, TUP&) { return f (); } template static RET bind (FUN& f, TUP&) { return std::bind (f); } }; template<> //_________________________________ struct Apply<1> ///< Apply function with 1 Argument { template static RET invoke (FUN& f, TUP & arg) { return f (get<0>(arg)); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg)); } }; template<> //_________________________________ struct Apply<2> ///< Apply function with 2 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) ); } }; template<> //_________________________________ struct Apply<3> ///< Apply function with 3 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) ); } }; template<> //_________________________________ struct Apply<4> ///< Apply function with 4 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) ); } }; template<> //_________________________________ struct Apply<5> ///< Apply function with 5 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) ); } }; template<> //_________________________________ struct Apply<6> ///< Apply function with 6 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) ); } }; template<> //_________________________________ struct Apply<7> ///< Apply function with 7 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) , get<6>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) , get<6>(arg) ); } }; template<> //_________________________________ struct Apply<8> ///< Apply function with 8 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) , get<6>(arg) , get<7>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) , get<6>(arg) , get<7>(arg) ); } }; template<> //_________________________________ struct Apply<9> ///< Apply function with 9 Arguments { template static RET invoke (FUN& f, TUP & arg) { return f ( get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) , get<6>(arg) , get<7>(arg) , get<8>(arg) ); } template static RET bind (FUN& f, TUP & arg) { return std::bind (f, get<0>(arg) , get<1>(arg) , get<2>(arg) , get<3>(arg) , get<4>(arg) , get<5>(arg) , get<6>(arg) , get<7>(arg) , get<8>(arg) ); } }; /* ===== Helpers for partial function application ===== */ /** @note relying on the implementation type * since we need to _build_ placeholders */ using std::_Placeholder; /** * Build a list of standard function argument placeholder types. * For each of the elements of the provided reference list, * a Placeholder is added, numbers counting up starting with 1 (!) */ template struct PlaceholderTuple : PlaceholderTuple { }; template struct PlaceholderTuple, i> { using TailPlaceholders = typename PlaceholderTuple::List; using List = Node<_Placeholder, TailPlaceholders>; }; template struct PlaceholderTuple { using List = NullType; }; using std::tuple_element; using std::tuple_size; using std::get; /** * Builder for a tuple instance, where only some ctor parameters are supplied, * while the remaining arguments will be default constructed. The use case is * creating of a function binder, where some arguments shall be passed through * (and thus be stored in the resulting closure), while other arguments are just * marked as "Placeholder" with `std::_Placeholder`. * These placeholder marker terms just need to be default constructed, and will * then be stored into the desired positions. Later on, when actually invoking * such a partially closed function, only the arguments marked with placeholders * need to be supplied, while the other arguments will use the values hereby * "baked" into the closure. * @tparam TAR full target tuple type. Some oft the elements within this tuple will * be default constructed, some will be initialised from the SRC tuple * @tparam SRC argument tuple type, for the values _actually to be initialised_ here. * @tparam start position within TYPES, at which the sequence of init-arguments starts; * all other positions will just be default initialised * @see lib::meta::TupleConstructor */ template struct PartiallyInitTuple { template using DestType = typename std::tuple_element::type; /** * define those index positions in the target tuple, * where init arguments shall be used on construction. * All other arguments will just be default initialised. */ static constexpr bool useArg (size_t idx) { return (start <= idx) and (idx < start + std::tuple_size()); } template struct IndexMapper { SRC const& initArgs; operator DestType() { return std::get (initArgs); } }; template struct IndexMapper { SRC const& initArgs; operator DestType() { return DestType(); } }; }; } // (END) impl-namespace /* ======= core operations: closures and partial application ========= */ /** * Closure-creating template. * The instance is linked (reference) to a given concrete argument tuple. * A functor with a matching signature may then either be _closed_ over * this argument values, or even be invoked right away with theses arguments. * @warning we take functor objects _and parameters_ by reference */ template class TupleApplicator { using Args = typename _Fun::Args; using Ret = typename _Fun::Ret; using BoundFunc = function; enum { ARG_CNT = count::value }; /** storing a ref to the parameter tuple */ Tuple& params_; public: TupleApplicator (Tuple& args) : params_(args) { } BoundFunc bind (SIG& f) { return Apply::template bind (f, params_); } BoundFunc bind (function const& f) { return Apply::template bind (f, params_); } Ret operator() (SIG& f) { return Apply::template invoke (f, params_); } Ret operator() (function& f) { return Apply::template invoke (f, params_); } }; /** * Closing a function over its arguments. * This is a small usage example or spin-off, * having almost the same effect than invoking `std::bind()`. * The notable difference is that the function arguments for * creating the closure are passed in as one tuple compound. */ template class FunctionClosure { typedef typename _Fun::Args Args; typedef typename _Fun::Ret Ret; function closure_; public: FunctionClosure (SIG& f, Tuple& arg) : closure_(TupleApplicator(arg).bind(f)) { } FunctionClosure (function const& f, Tuple& arg) : closure_(TupleApplicator(arg).bind(f)) { } Ret operator() () { return closure_(); } typedef Ret result_type; ///< for STL use typedef void argument_type; }; /** * 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. * @tparam SIG signature of the function to be closed, either as * function reference type or `std::function` object * @tparam VAL type sequence describing the tuple of values * used for closing arguments * @note the construction of this helper template does not verify or * match types to the signature. In case of mismatch, you'll get * a compilation failure from `std::bind` (which can be confusing) */ template class PApply { typedef typename _Fun::Args Args; typedef typename _Fun::Ret Ret; typedef typename Args::List ArgsList; typedef typename VAL::List ValList; typedef typename Types::Seq ValTypes; enum { ARG_CNT = count::value , VAL_CNT = count ::value , ROFFSET = (VAL_CNT < ARG_CNT)? ARG_CNT-VAL_CNT : 0 }; // create list of the *remaining* arguments, after applying the ValList typedef typename Splice::Back LeftReduced; typedef typename Splice::Front RightReduced; typedef typename Types::Seq ArgsL; typedef typename Types::Seq ArgsR; // build a list, where each of the *remaining* arguments is replaced by a placeholder marker typedef typename func::PlaceholderTuple::List TrailingPlaceholders; typedef typename func::PlaceholderTuple::List LeadingPlaceholders; // ... and splice these placeholders on top of the original argument type list, // thus retaining the types to be closed, but setting a placeholder for each remaining argument typedef typename Splice::List LeftReplaced; typedef typename Splice::List RightReplaced; typedef typename Types::Seq LeftReplacedTypes; typedef typename Types::Seq RightReplacedTypes; // create a "builder" helper, which accepts exactly the value tuple elements // and puts them at the right location, while default-constructing the remaining // (=placeholder)-arguments. Using this builder helper, we can finally set up // the argument tuples (Left/RightReplacedArgs) used for the std::bind call template using IdxSelectorL = typename PartiallyInitTuple::template IndexMapper; template using IdxSelectorR = typename PartiallyInitTuple::template IndexMapper; using BuildL = TupleConstructor; using BuildR = TupleConstructor; /** Tuple to hold all argument values, starting from left. * Any remaining positions behind the substitute values are occupied by binding placeholders */ using LeftReplacedArgs = Tuple; /** Tuple to hold all argument values, aligned to the end of the function argument list. * Any remaining positions before the substitute values are occupied by binding placeholders */ using RightReplacedArgs = Tuple; public: typedef function::Sig> LeftReducedFunc; typedef function::Sig> RightReducedFunc; /** 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 const& f, Tuple const& arg) { LeftReplacedArgs params {BuildL(arg)}; return func::Apply::template bind (f, params); } /** 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 const& f, Tuple const& arg) { RightReplacedArgs params {BuildR(arg)}; return func::Apply::template bind (f, params); } }; namespace _composed { // repetitive impl.code for function composition using std::bind; using std::function; using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; using std::placeholders::_4; using std::placeholders::_5; using std::placeholders::_6; using std::placeholders::_7; using std::placeholders::_8; using std::placeholders::_9; template struct Build; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6,_7)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6,_7,_8)); } }; template struct Build { static function func(F1& f1, F2& f2) { return bind (f2, bind (f1,_1,_2,_3,_4,_5,_6,_7,_8,_9)); } }; } // (End) impl namespace (_composed) /** * Functional composition. Create a functor, which * on invocation will execute two functions chained, * i.e. fed the result of invoking the first function * as argument into the second function. */ template class FunctionComposition { typedef typename _Fun::Args Args; typedef typename _Fun::Ret Ret1; typedef Types ArgsF2; typedef typename FunctionTypedef::Sig SigF2; typedef typename FunctionTypedef::Sig ChainedSig; enum { ARG_CNT = count::value }; public: static function chain (F1& f1, SigF2& f2) { return _composed::Build::func (f1,f2); } static function chain (F1& f1, function& f2) { return _composed::Build, ARG_CNT>::func (f1,f2); } }; /** * Bind a specific argument to an arbitrary value. * Especially, this "value" might be another binder. */ template class BindToArgument { typedef typename _Fun::Args Args; typedef typename _Fun::Ret Ret; typedef typename Args::List ArgsList; typedef typename Types::List ValList; enum { ARG_CNT = count::value }; typedef typename Splice::Front RemainingFront; typedef typename Splice::Back RemainingBack; typedef typename func::PlaceholderTuple::List PlaceholdersBefore; typedef typename func::PlaceholderTuple::List PlaceholdersBehind; typedef typename Append< typename Append< PlaceholdersBefore , ValList >::List , PlaceholdersBehind >::List PreparedArgs; typedef typename Append::List ReducedArgs; using PreparedArgTypes = typename Types::Seq; using RemainingArgs = typename Types::Seq; using ReducedSig = typename FunctionTypedef::Sig; template using IdxSelector = typename PartiallyInitTuple::template IndexMapper; using BuildPreparedArgs = TupleConstructor; public: typedef function ReducedFunc; static ReducedFunc reduced (SIG& f, Tuple> const& val) { Tuple params {BuildPreparedArgs(val)}; return func::Apply::template bind (f, params); } }; namespace { // ...helpers for specifying types in function declarations.... template struct _Sig { typedef typename FunctionTypedef::Sig Type; typedef TupleApplicator Applicator; }; template struct _Clo { typedef typename _Fun::Ret Ret; typedef typename _Sig::Type Signature; typedef FunctionClosure Type; }; template struct _Chain { typedef typename _Fun::Args Args; typedef typename _Fun::Ret Ret; typedef typename FunctionTypedef::Sig Chained; typedef function Function; }; 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; }; } // (End) argument type shortcuts /* ========== function-style interface ============= */ /** build a TupleApplicator, which embodies the given * argument tuple and can be used to apply them * to various functions repeatedly. */ template inline typename _Sig>::Applicator tupleApplicator (std::tuple& args) { typedef typename _Sig>::Type Signature; return TupleApplicator (args); } /** apply the given function to the argument tuple */ template inline typename _Fun::Ret apply (SIG& f, std::tuple& args) { typedef typename _Fun::Ret Ret; // typedef typename _Sig>::Type Signature; // Note: deliberately re-building the Signature Type return TupleApplicator (args) (f); // in order to get better error messages here } /** close the given function over all arguments, * using the values from the argument tuple. * @return a closure object, which can be * invoked later to yield the * function result. */ template inline typename _Clo>::Type closure (SIG& f, std::tuple& args) { typedef typename _Clo>::Type Closure; return Closure (f,args); } /** close the given function over the first argument */ template inline 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 inline 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); } /** bind the last function argument to an arbitrary term, * which especially might be a (nested) binder... */ template inline typename _PapE::Function bindLast (SIG& f, TERM const& arg) { typedef Types ArgTypeSeq; typedef Tuple ArgTuple; ArgTuple argT(arg); enum { LAST_POS = -1 + count::Args::List>::value }; return BindToArgument::reduced (f, argT); } /** build a functor chaining the given functions */ template inline typename _Chain::Function chained (SIG1& f1, SIG2& f2) { typedef typename _Chain::Ret Ret; return FunctionComposition::chain (f1, f2); } }}} // namespace lib::meta::func #endif