From 03b17c78da67b8cdba014773fa99736f2f9ed8b5 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 16 Dec 2024 23:01:57 +0100 Subject: [PATCH] Buffer-Provider: investigate Problem with embedded type-constructor-arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- research/try.cpp | 136 ++++------ src/steam/engine/type-handler.hpp | 13 +- tests/core/steam/engine/node-base-test.cpp | 3 +- wiki/thinkPad.ichthyo.mm | 295 +++++++++++++++++---- 4 files changed, 301 insertions(+), 146 deletions(-) diff --git a/research/try.cpp b/research/try.cpp index 4eac80673..ff94160c6 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -1,23 +1,31 @@ /* try.cpp - to try out and experiment with new features.... * scons will create the binary bin/try */ +// 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 // 11/23 - prototype for grouping from iterator /** @file try.cpp - * Find out about the conditions when an overload of a function template is picked. - * This is an investigation regarding the proper way to overload `std::get` - * especially when the base class of the custom type itself is a tuple. + * 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. * - * As it turns out, overload resolution works as expected; rather the implementation - * of `std::get` causes the problems, as it triggers an assertion immediately when - * instantiated with out-of-bounds parameters, which prevents the overload resolution - * to commence and directly terminates the compilation. The reason is that this - * standard implementation relies on std::tuple_element to do the actual - * bounds checking. This can be demonstrated by extracting the standard - * implementation and our custom implementation under a different name. + * 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 */ typedef unsigned int uint; @@ -26,94 +34,52 @@ typedef unsigned int uint; #include "lib/format-cout.hpp" #include "lib/test/test-helper.hpp" #include "lib/test/diagnostic-output.hpp" -#include "lib/hetero-data.hpp" #include "lib/util.hpp" -#include -#include -#include +#include -using lib::test::showTypes; -using std::tuple; +using std::cout; +using std::endl; +using std::forward; +using std::placeholders::_1; -struct B { }; +template +inline void +dummy (int extra, ARGS&& ...args) + { + cout << extra <<"▷"; + ((cout << forward(args) << "•"), ...) + << endl; + } -struct D1 : B { }; +template +auto +bound (ARGS&& ...args) + { + return std::bind (dummy, _1, forward(args) ...); + } -struct D2 : D1 { }; +void +fun (int&& a) + { + std::cout << a << std::endl; + } -string getty (B&) { return "getty-B&"; } -string getty (D1&&){ return "getty-D1&&"; } -string getty (D1&) { return "getty-D1&"; } - - - -template -string getty (tuple&) { return "getty-tuple& "+showTypes(); } - - -template -struct F : tuple { }; - -template -struct FD1 : F {}; - -template -struct FD2 : FD1 {}; - - -template -string getty (FD1&) { return "getty-FD1& "+showTypes(); } - - -template -string getty (lib::HeteroData&) { return "getty-Hetero& "+showTypes(); } - - - template -// constexpr std::__tuple_element_t<__i, tuple<_Elements...>>& - decltype(auto) - gritty(tuple<_Elements...>& __t) noexcept - { return std::__get_helper<__i>(__t); } - -template -constexpr std::tuple_element_t>& -gritty (lib::HeteroData & heDa) noexcept -{ - return heDa.template get(); -} int main (int, char**) { - D2 d2; - SHOW_EXPR(getty(d2)); + dummy (55,2,3,5,8); - FD2 fd2; - SHOW_EXPR(getty(fd2)); + 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 Het = lib::HeteroData; - Het h1; - SHOW_EXPR(getty(h1)); -// SHOW_EXPR(std::get<1>(h1) = 5.5) - SHOW_EXPR(h1.get<1>() = 5.5) - - using Constructor = Het::Chain; - auto h2 = Constructor::build (true, "Ψ"); - h2.linkInto(h1); - - using Het2 = Constructor::ChainType; - Het2& chain2 = Constructor::recast (h1); - SHOW_TYPE(Het2) - SHOW_EXPR(getty(chain2)); -// SHOW_EXPR(std::get<1>(chain2)) -// SHOW_EXPR(std::get<3>(chain2)) - SHOW_EXPR(chain2.get<1>()) - SHOW_EXPR(chain2.get<3>()) - SHOW_EXPR(gritty<1>(chain2)) - SHOW_EXPR(gritty<3>(chain2)) - - cout << "\n.gulp.\n"; + cout << "\n.gulp." <()} { } - /** builder function defining a TypeHandler - * to place an object into the buffer, - * possibly with given ctor arguments. */ + /** builder function for a pre-configured TypeHandler to place a + * new instance into the buffer, possibly with given ctor arguments. + * @warning additional ctor arguments will be materialised + * and stored as copy in the TypeHandler, for repeated use. + * @remark need to change the instantiation type to LValue-ref + * as pointed out on [Stackoverflow] + * [Stackoverflow]: https://stackoverflow.com/q/30968573/444796 + */ template static TypeHandler create (ARGS&& ...args) { - return TypeHandler ( bind (buildIntoBuffer, _1, forward (args)...) + return TypeHandler ( bind (buildIntoBuffer, _1, forward (args)...) , destroyInBuffer); } diff --git a/tests/core/steam/engine/node-base-test.cpp b/tests/core/steam/engine/node-base-test.cpp index c833db28b..3632955e5 100644 --- a/tests/core/steam/engine/node-base-test.cpp +++ b/tests/core/steam/engine/node-base-test.cpp @@ -89,8 +89,7 @@ namespace test { // CHECK (m1.param ); BufferProvider& provider = DiagnosticBufferProvider::build(); - BuffHandle buff = provider.lockBufferFor(); ////////////////////////////OOO can not pass ctor-args directly here -buff.accessAs() = -55; + BuffHandle buff = provider.lockBufferFor (-55); CHECK (buff.isValid()); CHECK (buff.accessAs() == -55); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index cfed671e4..40b8ae106 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -24275,9 +24275,7 @@ - - - +

es gibt keinen einfachen und performanten Mechanismus, über den ich mir irgend ein Widget merken und später noch sicher addressieren kann, selbst wenn eine Diff-Nachricht inzwischen die Anzeige umbaut und das bezeichnete Element inzwischen gelöscht ist. Und zwar deshalb, weil wir hier von echten einfachen GTK-Widgets reden, und nicht von unseren "Tangibles", die am UI-Bus hängen. Selbst wenn wir noch gesicherte »Mediatoren« dazwischenschalten, also z.B. diese Nachrichten über jeweils ein zuständiges model::Tangible zustellen, dann haben wiederum diese das identische Problem: sie bekommen nicht garantiert mit, wenn eines der von ihnen verwalteten Widgets inzwischen nicht mehr existiert. Wir bräuchten @@ -24827,9 +24825,7 @@ - - - +

noch nicht klar, welche Rolle der spielt; ich sehe ihn erst mal vor, weil er möglich ist. Denkbar wäre, daß er durch User-Interaktion entsteht, oder aber auch systematisch generiert wird, um bestimmte Arten von Clips optisch abzusetzen @@ -25537,9 +25533,7 @@ - - - +

...aber die gesamte Verankerungs-Aktion läßt sich rein auf Widget-Interface-Ebene machen. @@ -26300,9 +26294,7 @@ - - - +

weil der Trigger irgendwo unten passiert, und nicht auf dem top-Level ViewHook @@ -27450,9 +27442,7 @@ - - - +

weil ich diese Benennungen von Anfang an auch so in FreeCAD verwendet habe, und jetzt nicht alles umbenennen möchte @@ -29034,9 +29024,7 @@ - - - +

d.h. wenn zufällig das Interface auch eine Methode CloneInto() enthält, aber mit einer unpassenden Signatur @@ -29090,9 +29078,7 @@ - - - +

nämlich immer dann, wenn man tatsächlich den CopySupport oder CloneSupport als Basis des Interfaces verwendet... @@ -33199,9 +33185,7 @@ - - - +

macht ggfs ganz natürlich einen box-shadow sichtbar @@ -33277,9 +33261,7 @@ - - - +

ClipPresenter::determineRequiredVerticalExtension() @@ -35265,9 +35247,7 @@ - - - +

weil dies die einzige Info ist, @@ -36594,9 +36574,7 @@ - - - +

das ist ein akzidentelles Problem @@ -37335,9 +37313,7 @@ - - - +

gehört diese nun dem Layout-Manager, oder gehört er der Geste? @@ -37628,9 +37604,7 @@ - - - +

  • @@ -38431,9 +38405,7 @@ - - - +

    das Platzieren auf den Canvas ist keine high-Performance-Operation;
    vielmehr ist es sogar vernachlässigbar im Vergleich zum Aufwand der Zeichen-Operationen; und letztere werden eben genau aus Performance-Gründen gebatcht und gebündelt.... @@ -82697,7 +82669,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

    - +
    @@ -88526,7 +88498,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - + + @@ -88586,8 +88559,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
            }

    - - +
    @@ -88602,8 +88574,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
      std::function<void(void*)>::function(std::_Bind<void (*(std::_Placeholder<1>, int))(void*, int&&)>&)

    - - +
    @@ -88614,8 +88585,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    with CTOR = std::_Bind<void (*(std::_Placeholder<1>, int))(void*, int&&)>;

    - - +
    @@ -88623,6 +88593,203 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    + + + + + + + + + + + + + + + + + + +

    + Aufgerufen: (std::_Bind<void (*(std::_Placeholder<1>, int))(void*, int&&)>) (void*&) +

    + + +
    +
    + + + + +

    + Candidate: _Result std::_Bind<_Functor(_Bound_args ...)>::operator()(_Args&& ...) +

    + +
    +
    + + + + +

    +   _Functor = void (*)(void*, int&&) +

    + +
    +
    + + + + +

    +   _Bound_args = {std::_Placeholder<1>, int} +

    + +
    +
    + + + + +

    + Beobachtung: _Functor(_Bound_args ...) +

    + + +
    + + + + + + + + + + + + +
    +
    + + + + + + + + + + + + + + +
    +
    + #include <functional> +
    +
    + #include <iostream> +
    +
    +
    + +
    + inline void +
    +
    + fun (int&& a) +
    +
    + { +
    +
    + std::cout << a << std::endl; +
    +
    + } +
    +
    + +
    +
    + int +
    +
    + main (int, char**) +
    +
    + { +
    +
    + auto bi = std::bind (fun, 55); +
    +
    + bi(); +
    +
    + return 0; +
    +
    + } +
    +
    + +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + +

    + also: func<ARGS& ...> +

    + + +
    + +
    + + + + + + +
    +
    +
    + + + + + + + + +
    @@ -91887,6 +92054,20 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    + + + + + + + + + + + + + + @@ -91929,8 +92110,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    Für die BuffTable war früher jede Menge Funktionalität geplant, die inzwischen im BufferProvider realisiert wurde; der alte Test hängt seit >10 Jahren bei den Engine-Tests unimplementiert mit rum — es ist sinnlos, ihn nun umzuwidmen

    - - +
    @@ -92151,9 +92331,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - + + @@ -93182,6 +93363,10 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    + + + +
    @@ -94913,7 +95098,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - + @@ -95770,7 +95955,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - +