diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index eedbc2358..51f04e1d2 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -33,12 +33,6 @@ ** @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 - ** @todo 11/23 these functor-utils were written at a time when support for handling - ** generic functions in C++ was woefully inadequate; at that time, we neither - ** had Lambda-support in the language, nor the ability to use variadic arguments. - ** Providing a one-shot function-style interface for this kind of manipulations - ** is still considered beneficial, and thus we should gradually modernise - ** the tools we want to retain... ** @todo 2/25 note that there is a bind_front in C++20 and C++23 will provide a bind_back ** helper, which would provide the implementation fully in accordance with current ** expectations (including move, constexpr); if we choose to retain a generic @@ -83,191 +77,6 @@ namespace func{ 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. - * @todo 2/2025 should be replaced by a single variadic template, and - * implemented using std::apply. Note also that std::bind would - * support perfect-forwarding, especially also for the functor; - * the latter would allow to use move-only functors. - */ - template - struct Apply; - - - template<> //__________________________________ - struct Apply<0> ///< Apply function without Arguments - { - template - static RET - bind (FUN& f, TUP&) - { - return std::bind (f); - } - }; - - - template<> //_________________________________ - struct Apply<1> ///< Apply function with 1 Argument - { - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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 - 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; @@ -371,47 +180,23 @@ namespace func{ + /* ======= 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 + * Base technique: build a binder with arguments from a tuple. + * @remark based on the apply-λ-trick by David Vandervoorde + * to unpack the tuple elements as argument pack + * @note both the functor and the arguments can be passed + * by _perfect forwarding_, yet both will be **copied** + * into the resulting binder + * @param fun anything »invocable« + * @param tuple a suitable set of arguments, or std binding placeholders + * @return a _binder functor_, as generated by `std::bind`; all arguments + * supplied with explicitly given values will be _closed_, while + * argument positions marked with `std::_Placeholder` instances + * will remain _open_ to accept arguments on the resulting function. */ - template - class TupleApplicator - { - using Args = typename _Fun::Args; - using Ret = typename _Fun::Ret; - - using BoundFunc = function; - - enum { ARG_CNT = count() }; - - - /** 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_); } - - template - Ret - operator() (FUN&& f) - { - ASSERT_VALID_SIGNATURE (FUN,SIG); - return std::apply (forward (f), params_); - } - }; - - template> auto bindArgTuple (FUN&& fun, TUP&& tuple) @@ -427,37 +212,6 @@ namespace func{ - /** - * 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& args) - : closure_{bindArgTuple (f, args)} - { } - FunctionClosure (function const& f, Tuple& args) - : closure_{bindArgTuple (f, args)} - { } - - Ret operator() () { return closure_(); } - - typedef Ret result_type; ///< for STL use - typedef void argument_type; - }; - - - /** * Partial function application @@ -638,14 +392,6 @@ namespace func{ using util::unConst; - template - struct _Clo - { - using Ret = typename _Fun::Ret; - using Signature = typename BuildFunType::Sig; - using Type = FunctionClosure; - }; - template struct _Chain { @@ -744,21 +490,6 @@ namespace func{ /* ========== function-style interface ============= */ - /** 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) - { - using Closure = typename _Clo>::Type; - return Closure (f,args); - } - - /** close the given function over the first argument. * @warning never tie an ownership-managing object by-value! */ template diff --git a/tests/12metaprogramming.tests b/tests/12metaprogramming.tests index f0a54891b..ee1f4fde0 100644 --- a/tests/12metaprogramming.tests +++ b/tests/12metaprogramming.tests @@ -109,13 +109,11 @@ out-lit: List2 :-<5>-<6>-<7>- out-lit: Args :-<5>-<9>- out-lit: NewArgs :-<1>-<5>-<9>- out-lit: : -out-lit: : ---Apply--- +out-lit: : ---Bind---- out-lit: tup0 :«tuple<>»──() out-lit: tup1 :«tuple»──(11) out-lit: tup2 :«tuple»──(11,12) out-lit: tup3 :«tuple»──(11,12,13) -out-lit: : -out-lit: : ---Bind---- return: 0 END diff --git a/tests/library/meta/function-closure-test.cpp b/tests/library/meta/function-closure-test.cpp index a7939df1b..c46f20ba0 100644 --- a/tests/library/meta/function-closure-test.cpp +++ b/tests/library/meta/function-closure-test.cpp @@ -50,16 +50,8 @@ namespace test { namespace { // test data - - - typedef TySeq< Num<1> - , Num<2> - , Num<3> - >::List List1; - typedef TySeq< Num<5> - , Num<6> - , Num<7> - >::List List2; + using List1 = TySeq, Num<2>, Num<3> >::List; + using List2 = TySeq, Num<6>, Num<7> >::List; /** special test fun @@ -82,24 +74,24 @@ namespace test { - using func::Apply; - using func::TupleApplicator; using func::bindArgTuple; - using func::FunctionClosure; - using func::closure; /*********************************************************************//** * @test building a function closure for a given function or functor, * while arguments are passed in as tuple * - accessing signatures as typelists - * - apply free function to tuple - * - apply functor to tuple * - bind free function to tuple * - bind functor to tuple - * - build a simple "tuple closure" * @remark this test is _rather low-level_ and documents the construction - * of the implementation + * of the implementation; furthermore, most of this construction + * was obsoleted by newer language features, notably std::apply + * and the technique to unpack variadic-λ arguments. + * What remains, is now largely a definition how to handle + * function argument list signatures, to build suitable + * argument tuple types by metaprogramming, and finally + * to pass them to construct a binder. + * @see function-composition-test.cpp (advanced features like partial application) */ class FunctionClosure_test : public Test { @@ -108,11 +100,8 @@ namespace test { { verify_setup(); check_signatureTypeManip(); - check_applyFree(); - check_applyFunc(); check_bindFree(); check_bindFunc(); - build_closure(); } @@ -150,56 +139,6 @@ namespace test { } - void - check_applyFree () - { - 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); - - CHECK (-1 == TupleApplicator (tup0) (fun0) ); - CHECK (11 == TupleApplicator (tup1) (fun1) ); - CHECK (11+12 == TupleApplicator (tup2) (fun2) ); - CHECK (11+12+13 == TupleApplicator (tup3) (fun3) ); - - CHECK (-1 == std::apply(fun0, tup0) ); - CHECK (11 == std::apply(fun1, tup1) ); - CHECK (11+12 == std::apply(fun2, tup2) ); - CHECK (11+12+13 == std::apply(fun3, tup3) ); - } - - - 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); - - CHECK (-1 == TupleApplicator (tup0) (functor0) ); - CHECK (11 == TupleApplicator (tup1) (functor1) ); - CHECK (11+12 == TupleApplicator (tup2) (functor2) ); - CHECK (11+12+13 == TupleApplicator (tup3) (functor3) ); - - CHECK (-1 == std::apply(functor0, tup0) ); - CHECK (11 == std::apply(functor1, tup1) ); - CHECK (11+12 == std::apply(functor2, tup2) ); - CHECK (11+12+13 == std::apply(functor3, tup3) ); - } - - void check_bindFree () { @@ -214,22 +153,12 @@ namespace test { DUMPVAL (tup2); DUMPVAL (tup3); - typedef function BoundFun; + using BoundFun = function; - BoundFun functor0 = Apply<0>::bind (fun0, tup0); - BoundFun functor1 = Apply<1>::bind (fun1, tup1); - BoundFun functor2 = Apply<2>::bind (fun2, tup3); - BoundFun functor3 = Apply<3>::bind (fun3, tup3); - - CHECK (-1 == functor0() ); - CHECK (11 == functor1() ); - CHECK (11+12 == functor2() ); - CHECK (11+12+13 == functor3() ); - - functor0 = bindArgTuple (fun0, tup0); - functor1 = bindArgTuple (fun1, tup1); - functor2 = bindArgTuple (fun2, tup2); - functor3 = bindArgTuple (fun3, tup3); + BoundFun functor0 = bindArgTuple (fun0, tup0); + BoundFun functor1 = bindArgTuple (fun1, tup1); + BoundFun functor2 = bindArgTuple (fun2, tup2); + BoundFun functor3 = bindArgTuple (fun3, tup3); CHECK (-1 == functor0() ); CHECK (11 == functor1() ); @@ -251,84 +180,18 @@ namespace test { function unbound_functor2 (fun2); function unbound_functor3 (fun3); - typedef function BoundFun; + using BoundFun = function; - BoundFun functor0 = Apply<0>::bind (unbound_functor0, tup0); - BoundFun functor1 = Apply<1>::bind (unbound_functor1, tup1); - BoundFun functor2 = Apply<2>::bind (unbound_functor2, tup3); - BoundFun functor3 = Apply<3>::bind (unbound_functor3, tup3); + BoundFun functor0 = bindArgTuple (unbound_functor0, tup0); + BoundFun functor1 = bindArgTuple (unbound_functor1, tup1); + BoundFun functor2 = bindArgTuple (unbound_functor2, tup2); + BoundFun functor3 = bindArgTuple (unbound_functor3, tup3); CHECK (-1 == functor0() ); CHECK (11 == functor1() ); CHECK (11+12 == functor2() ); CHECK (11+12+13 == functor3() ); - functor0 = bindArgTuple (unbound_functor0, tup0); - functor1 = bindArgTuple (unbound_functor1, tup1); - functor2 = bindArgTuple (unbound_functor2, tup2); - functor3 = bindArgTuple (unbound_functor3, tup3); - - CHECK (-1 == functor0() ); - CHECK (11 == functor1() ); - CHECK (11+12 == functor2() ); - CHECK (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); - - CHECK (-1 == clo0() ); - CHECK (11 == clo1() ); - CHECK (11+12 == clo2() ); - CHECK (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); - - CHECK (-1 == clo0() ); - CHECK (11 == clo1() ); - CHECK (11+12 == clo2() ); - CHECK (11+12+13 == clo3() ); - - CHECK (-1 == closure(fun0,tup0) () ); - CHECK (11 == closure(fun1,tup1) () ); - CHECK (11+12 == closure(fun2,tup2) () ); - CHECK (11+12+13 == closure(fun3,tup3) () ); - - CHECK (-1 == closure(unbound_functor0,tup0) () ); - CHECK (11 == closure(unbound_functor1,tup1) () ); - CHECK (11+12 == closure(unbound_functor2,tup2) () ); - CHECK (11+12+13 == closure(unbound_functor3,tup3) () ); - - - // finally combine all techniques.... - using NumberzArg = TySeq::Seq; - using NumberzSig = BuildFunType::Sig; - - Tuple numberzTup (Num<5>(22), Num<6>(33), Num<7>(44)); - - FunctionClosure numClo (getNumberz<5,6,7>, numberzTup ); - - CHECK (22+33+44 == numClo() ); } }; diff --git a/tests/library/meta/function-composition-test.cpp b/tests/library/meta/function-composition-test.cpp index 3fc0313b1..625df1d7a 100644 --- a/tests/library/meta/function-composition-test.cpp +++ b/tests/library/meta/function-composition-test.cpp @@ -30,7 +30,7 @@ namespace meta { namespace test { using ::test::Test; -// using lib::test::showType; + using lib::test::showType; using lib::meta::_Fun; using func::applyFirst; using func::applyLast; @@ -236,7 +236,6 @@ namespace test { // 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 int r5 = fun_23(_2_,_3_).o_; // invoke the resulting functor... @@ -276,7 +275,6 @@ namespace test { CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_); CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_); CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_); - */ @@ -403,9 +401,9 @@ namespace test { using SigC = _Fun::Sig; using SigP = _Fun::Sig; -// CHECK (showType() == "double (float&, int&, long)"_expect); -// CHECK (showType() == "long (float&, int&, long)"_expect); -// CHECK (showType() == "double (int&, long)"_expect); + CHECK (showType() == "double (float&, int&, long)"_expect); + CHECK (showType() == "long (float&, int&, long)"_expect); + CHECK (showType() == "double (int&, long)"_expect); CHECK (220 == f1 (ff,ii,33)); CHECK (220 == chain(ff,ii,33)); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index f176590d0..f3636870b 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -164414,7 +164414,8 @@ Since then others have made contributions, see the log for the history. - + + @@ -164675,9 +164676,21 @@ Since then others have made contributions, see the log for the history. + + + + + + + + + + + +