diff --git a/research/try.cpp b/research/try.cpp index ff94160c6..8a3bfbd2c 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -1,6 +1,7 @@ /* try.cpp - to try out and experiment with new features.... * scons will create the binary bin/try */ +// 06/25 - investigate function type detection of std::bind Binders // 12/24 - investigate problem when perfect-forwarding into a binder // 12/24 - investigate overload resolution on a templated function similar to std::get // 11/24 - how to define a bare object location comparison predicate @@ -8,77 +9,130 @@ /** @file try.cpp - * Partially binding / closing arguments of a function with _perfect forwarding_ can be problematic. - * The problem was encountered in the steam::engine::TypeHandler::create() - function with additional - * constructor arguments. Obviously, we want these to be _perfect forwarded_ into the actual constructor, - * but the binding must store a captured copy of these values, because the handler can be used repeatedly. - * - * The actual problem is caused by the instantiation of the target function, because the arguments are - * also passed into the binding mechanism by _perfect forwarding._ The target function template will thus - * be instantiated to expect RValues, but the binder can only pass a copy by-reference. At this point then - * the problem materialises (with a rather confusing error message). - * - * The Problem was already discussed on [Stackoverflow] - * - * A simple workaround is to change the types in the instantiation into references; - * obviously this can not work for some argument types; if a more elaborate handling is necessary, - * the [handling of bound arguments] should be considered in detail. - * - * [Stackoverflow]: https://stackoverflow.com/q/30968573/444796 - * [handling of bound arguments]: http://en.cppreference.com/w/cpp/utility/functional/bind#Member_function_operator.28.29 + * Investigate ambiguities regarding the function type of standard binders. + * The Binder objects returned from `std::bind` provide a set of overloaded + * function call `operator()` (with variants for c/v). Unfortunately this defeats + * the common techniques to detect a function signature from a callable, when + * only a concrete instance of such a binder is given. Furthermore, looking + * at the definition of `class _Bind_result<_Result, _Functor(_Bound_args...)>` + * in my implementation of the C++ Stdlib, it seems we are pretty much out + * of luck, and even `std::function` fails with the template argument detection. + * A possible workaround could be to wrap the Binder object immediately into + * a lambda, but only if the actual types for the argument list can be + * provided directly to a template to generate this λ-wrapper. + * @note there is a nasty twist regarding const-correctness, which almost made + * this workaround fail altogether. The overloaded operator() from std::bind + * serves the same purpose (to deal with const/volatile), and this is the + * very reason that defeats the detection of the function signature. + * The workaround attempts to expose precisely one function call operator, + * and this becomes problematic as soon as the resulting object is processed + * further, and maybe bound into another lambda capture. Thus we define the + * wrapper class explicitly, so that any const-ness can be cast away. + * This turns out to be necessary in tuple-closure.hpp. */ -typedef unsigned int uint; - #include "lib/format-cout.hpp" #include "lib/test/test-helper.hpp" #include "lib/test/diagnostic-output.hpp" +#include "lib/meta/function.hpp" +#include "lib/meta/variadic-rebind.hpp" #include "lib/util.hpp" #include -using std::cout; -using std::endl; using std::forward; using std::placeholders::_1; +using lib::meta::_Fun; -template -inline void -dummy (int extra, ARGS&& ...args) - { - cout << extra <<"▷"; - ((cout << forward(args) << "•"), ...) - << endl; - } - -template -auto -bound (ARGS&& ...args) - { - return std::bind (dummy, _1, forward(args) ...); - } void -fun (int&& a) +fun (int& a) { std::cout << a << std::endl; } +short +fup (long l, long long ll) + { + return short(l - ll); + } + + + +/** WORKAROUND: wrap a binder to yield clear function signature */ +template +struct AdaptInvokable + { + template + static auto + buildWrapper (FUN&& fun) + { + + struct Wrap + { + FUN fun_; + + Wrap(FUN&& f) : fun_{forward(f)} { } + + auto + operator() (ARGS... args) + { + return fun_(forward(args)...); + } + }; + + return Wrap{forward(fun)}; +///////////////////////////////////////////////////////////// NOTE +///////////////////////////////////////////////////////////// can not use a Lambda, since we're then trapped +///////////////////////////////////////////////////////////// in an unsurmountable mixture of const and non-const +// return [functor = forward(fun)] +// (ARGS... args) mutable +// { +// return functor (forward (args)...); +// }; + } + }; + +template +auto +buildInvokableWrapper (FUN&& fun) + { + using ArgTypes = typename TYPES::Seq; + using Builder = typename lib::meta::RebindVariadic::Type; + + return Builder::buildWrapper (forward (fun)); + } + int main (int, char**) { - dummy (55,2,3,5,8); + SHOW_EXPR (fup(2,3)) + auto bup = std::bind (fup, _1, 5); + SHOW_EXPR (bup) + using Bup = decltype(bup); + using Fub = _Fun; + SHOW_TYPE (Bup) + SHOW_TYPE (Fub) +// using Sub = Fub::Sig; ////////////////Problem: does not compile - auto bun = bound (2,3,5); - using Bun = decltype(fun); -SHOW_TYPE(Bun) - bun (55); - - auto bi = std::bind (fun, 55); -// bi(); /////////// this invocation does not compile, because the Binder passes a copy to the RValue-Ref + using Fut = decltype(fun); + SHOW_TYPE (_Fun::Sig) + + auto wup = buildInvokableWrapper>(bup); + + using Wup = decltype(wup); + using WupSig = _Fun::Sig; + SHOW_TYPE (WupSig); + SHOW_EXPR (wup(3)) + SHOW_EXPR (sizeof(bup)) + SHOW_EXPR (sizeof(wup)) + + auto waua = buildInvokableWrapper> (std::bind (fun, 55)); + waua (); + SHOW_TYPE (_Fun::Sig) cout << "\n.gulp." < + struct AdaptInvokable + { + template + static auto + buildWrapper (FUN&& fun) + { + struct Wrap + { + FUN fun_; + + auto + operator() (ARGS... args) + { + return fun_(forward(args)...); + } + }; + + return Wrap{forward(fun)}; + } + }; + } // (END) impl-namespace @@ -211,7 +243,26 @@ namespace func{ ,std::forward (tuple)); } - + /** + * Workaround to yield std::bind functors + * with a clearly defined function signature. + * @tparam TYPES type-sequence or type-list of the function arguments to take + * @remark the result of `std::bind` exposes several overloaded `operator()`, + * which unfortunately defeats detection of the resulting function signature + * by downstream code, which needs this information to guide further processing. + * @see TupleClosureBuilder::wrapBuilder(closureFun) + */ + template + auto + buildInvokableWrapper (FUN&& fun) + { + static_assert (is_Typelist::value); + using ArgTypes = typename TYPES::Seq; + using Builder = typename lib::meta::RebindVariadic::Type; + + return Builder::buildWrapper (forward (fun)); + } + /** @@ -234,7 +285,7 @@ namespace func{ * over time several variations were added, so that now the main functionality * is now _implemented twice_, in a very similar way in BindToArgument. * Actually, the latter seems much clearer, and possibly PApply could be - * rewritten into a front-end and delegate to BindToArgument.. ////////////////////////////////////TICKET #1394 + * rewritten into a front-end and delegate to BindToArgument... ////////////////////////////////////TICKET #1394 */ template class PApply @@ -375,7 +426,6 @@ namespace func{ using PreparedArgTypes = typename TySeq::Seq; using RemainingArgs = typename TySeq::Seq; - using ReducedSig = typename BuildFunType::Sig; template using IdxSelector = typename PartiallyInitTuple::template IndexMapper; @@ -385,18 +435,18 @@ namespace func{ public: - using ReducedFunc = function; ///////////////////////////////////////////////////////////TICKET #1394 : get rid of std::function ////OOO problem with tuple-closure - template - static ReducedFunc + static auto reduced (FUN&& f, VAL&& val) { Tuple bindingTuple { BuildPreparedArgs{ std::forward_as_tuple ( forward(val))}}; - return bindArgTuple (forward(f) - , move(bindingTuple)); + + return buildInvokableWrapper( + bindArgTuple (forward(f) + , move(bindingTuple))); } }; diff --git a/src/lib/meta/tuple-closure.hpp b/src/lib/meta/tuple-closure.hpp index bd13588ec..40254fe52 100644 --- a/src/lib/meta/tuple-closure.hpp +++ b/src/lib/meta/tuple-closure.hpp @@ -32,6 +32,11 @@ ** @see weaving-pattern-builder.hpp ** @see NodeBuilder_test::build_Node_closedParam() ** + ** @remark const correctness creates an unfortunate twist here, so that the Wrapper + ** in #wrapBuilder must be declared explicitly (no λ). There must not be + ** several overloaded `operator()` — otherwise the metaprogramming for + ** detection of the function signature would be defeated, since it + ** relies on `decltype(operator())` */ @@ -39,6 +44,7 @@ #define LIB_META_TUPLE_CLOSURE_H #include "lib/meta/function-closure.hpp" +#include "lib/util.hpp" #include #include @@ -49,6 +55,9 @@ namespace lib { namespace meta{ + using std::move; + using util::unConst; + /** * Metaprogramming helper to build a constructor-function * for »tuple-like« records, where some of the initialisation @@ -110,19 +119,34 @@ namespace meta{ return wrapBuilder (func::BindToArgument::reduced (buildRecord, std::forward(val))); } + template + static auto + closett (VAL&& val) + { + using BoundVal = std::decay_t; + return wrapBuilder (func::BindToArgument::refused (buildRecord, std::forward(val))); + } + private: template static auto - wrapBuilder (CLO closureFun) + wrapBuilder (CLO closureFun) ///< need to provide the remaining arguments as a tuple { using RemainingArgs = typename _Fun::Args; using RemainingParams = typename lib::meta::RebindVariadic::Type; - return [partialClosure = move(closureFun) - ] - (RemainingParams remPar) - { - return std::apply (partialClosure, remPar); - }; + + struct Wrap + { + auto + operator() (RemainingParams remPar) const + { + return std::apply (unConst(this)->partialClosure_, remPar); + }; + + CLO partialClosure_; + }; // Note: need a single operator() which works also for const + // can not do this with a λ + return Wrap{move(closureFun)}; } }; diff --git a/tests/library/meta/function-closure-test.cpp b/tests/library/meta/function-closure-test.cpp index c46f20ba0..8a633071f 100644 --- a/tests/library/meta/function-closure-test.cpp +++ b/tests/library/meta/function-closure-test.cpp @@ -35,12 +35,8 @@ #include "meta/typelist-diagnostics.hpp" #include "meta/tuple-diagnostics.hpp" -#include -using ::test::Test; using std::string; -using std::cout; -using std::endl; namespace lib { diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 92b887077..b20242566 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -58951,7 +58951,23 @@ - + + + + + + + +

+ ist aber im Rückblick nicht mehr so ganz klar, was das Problem war. Die Zielsetzung erscheint etwas widersprüchlich... ging es um perfect-forwrading, um move-only-Funktoren, wollte ich Referenzen binden können? +

+ +
+
+ + + + @@ -59014,6 +59030,93 @@ + + + + + + + + +

+ commit 03b17c78da67b8cdba014773fa99736f2f9ed8b5 +

+

+ Author: Ichthyostega <prg@ichthyostega.de> +

+

+ Date:   Mon Dec 16 23:01:57 2024 +0100 +

+

+ +

+

+     Buffer-Provider: investigate Problem with embedded type-constructor-arguments +

+

+     +

+

+     This is a possible extension which frequently comes up again during the design of the Engine. +

+

+     Basically, the `TypeHandler` in the metadata-descriptor used by the `BufferProvder` could capture +

+

+     additional context-arguments, which are then later passed to an object instance embedded into the buffer. +

+

+     +

+

+     Yesterday I attempted to use this feature for a simple demonstration in `NodeBasic_test`, +

+

+     just to find out that passing additional constructor arguments to the capture fails with +

+

+     a confusing compilation error message. This failure could be traced down to the function binder; +

+

+     and what at first sight seemed to be a compiler error, turned out to be a quite logical limitation: +

+

+     When we »close« some objects of the constructor, but delay the construction itself, we'll have to +

+

+     store a copy in the constructor-λ. And this implies, that we'll have to change the types +

+

+     used for instantiation of the compiler, so that the construction-function can be invoked +

+

+     by passing references from the captured copy of the additional arguments. +

+

+     +

+

+     When naively passing those forwarded arguments into the std::bind()-call, +

+

+     the resulting functor will fail at instantiation, when the compiler attempts +

+

+     to generate the function-call `operator()` +

+

+     +

+

+     see: https://stackoverflow.com/q/30968573/444796 +

+ +
+
+ + + +
@@ -59079,6 +59182,7 @@ + @@ -133458,7 +133562,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension)

- + @@ -164672,8 +164776,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -164684,8 +164787,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -164746,6 +164848,9 @@ Since then others have made contributions, see the log for the history. + + + @@ -164762,7 +164867,7 @@ Since then others have made contributions, see the log for the history. - + @@ -165014,7 +165119,8 @@ Since then others have made contributions, see the log for the history. - + + @@ -165060,7 +165166,7 @@ Since then others have made contributions, see the log for the history. - + @@ -165114,13 +165220,140 @@ Since then others have made contributions, see the log for the history. - + + + + + + + + + + + + + + + + +

+ hat nur eine public Typedef return_type +

+ +
+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +

+ no matching function for call to ' +

+

+ __invoke( +

+

+ AdaptInvokable<int, double>::buildWrapper<BINDER >(BINDER&&)::<lambda(int, double)> const& +

+

+ , __tuple_element_t<0, tuple<int, double> >& +

+

+ , __tuple_element_t<1, tuple<int, double> >& +

+

+ )' +

+ +
+
+ + + +
+ + + + +

+ das const& erscheint verdächtig +

+ +
+ + + + + +

+ damit die reguläre Variante des Funktions-Operators genommen wird +

+ +
+
+ + + + +

+ weil man nämlich damit versucht, die Probleme mit const / volatile wegzubügeln, so daß sich der Binder in jedem Fall aufrufen läßt +

+ +
+
+
+ + + + + + + + + + + + + - + + + + + + + + + + @@ -165162,6 +165395,7 @@ Since then others have made contributions, see the log for the history. +
@@ -165249,8 +165483,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -165286,7 +165519,7 @@ Since then others have made contributions, see the log for the history. - + @@ -165336,6 +165569,24 @@ Since then others have made contributions, see the log for the history. + + + + + + +

+ hier droht das Function-Closure-Monster +

+ +
+ + +
+ + + +