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 @@
-
-
-
+
-
-
-
+
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
-
+