From 0cad2dacc0d823827b65e31b0c8022715fac0bf3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 17 Feb 2025 18:36:23 +0100 Subject: [PATCH] Invocation: build a solution to adapt `std::array` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ...which should ''basically work,'' since `std::array` is ''»tuple-like«'' — BUT unfortunately it has a quite distinct template signature which does not fit into the generic scheme of a product type. Obviously we'd need a partial specialisation, but even re-implementing this turns out to be damn hard, because there is no way to generate a builder method with a suitable explicit type signature directly, because such a builder would need to accept precisely N arguments of same type. This leads to a different solution approach: we can introduce an ''adapter type'', which will be layered on top of `std::array` and just expose the proper type signature so that the existing Implementation can handle the array, relying on the tuple-protocol. __Note__: this changeset adds a convenient pretty-printer for `std::array`, based on the same forward-declaration trick employed recently for `lib::Several`. You need to include 'lib/format-util.hpp' to actually use it. --- src/lib/format-obj.hpp | 8 +- src/lib/format-util.hpp | 13 +++ src/lib/meta/tuple-closure.hpp | 89 ++++++++++++++- tests/library/meta/tuple-closure-test.cpp | 20 ++++ wiki/thinkPad.ichthyo.mm | 127 ++++++++++++++++++++-- 5 files changed, 245 insertions(+), 12 deletions(-) diff --git a/src/lib/format-obj.hpp b/src/lib/format-obj.hpp index b59342c63..dc5f98aa4 100644 --- a/src/lib/format-obj.hpp +++ b/src/lib/format-obj.hpp @@ -15,12 +15,12 @@ /** @file format-obj.hpp ** Simple functions to represent objects, for debugging and diagnostics. ** The helpers provided here are rather commonplace, but written in a way - ** as to incur only modest header inclusion load. It should be OK to use - ** these even on widely used interface headers. + ** to incur only modest header inclusion load. It should be OK to use them + ** even on interface headers in widespread use. ** - util::toString() performs a failsafe to-String conversion, thereby preferring a ** built-in conversion operator, falling back to a lexical conversion (boost) - ** or just a unmangled and simplified type string as default. - ** - util::typedString() combines this with a always visible type display + ** or just an unmangled and simplified type string as default. + ** - util::typedString() combines this with an always visible type display ** - lib::meta::demangleCxx() uses the built-in compiler support to translate a mangled ** type-ID (as given by `typeid(TY).name()`) into a readable, fully qualified ** C++ type name. This is only supported for GNU compatible compilers. diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index 1f0fd8bf8..f3b0a5cca 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -35,6 +35,7 @@ #include "lib/symbol.hpp" #include "lib/util.hpp" +#include #include #include #include @@ -257,6 +258,18 @@ namespace util { { return "["+join (forward (coll))+"]"; } + + + /** convenient pretty-printer for std::array instances */ + template + struct StringConv> + { + static std::string + invoke (std::array const& arr) noexcept + { + return util::toStringBracket (arr); + } + }; } // namespace util diff --git a/src/lib/meta/tuple-closure.hpp b/src/lib/meta/tuple-closure.hpp index b77d5fa02..a0fba6cb1 100644 --- a/src/lib/meta/tuple-closure.hpp +++ b/src/lib/meta/tuple-closure.hpp @@ -42,6 +42,7 @@ #include #include +#include @@ -96,5 +97,91 @@ namespace meta{ }; + /* ===== adapt array for tuple-like signature ===== */ + + template + struct ArrayAdapt; + + namespace { + + template + struct AllSame + : std::true_type + { }; + + template + struct AllSame + : __and_ + ,AllSame + > + { }; + + + template + struct Repeat + { + using Rem = typename Repeat::Seq; + using Seq = typename Prepend::Seq; + }; + + template + struct Repeat + { + using Seq = TySeq<>; + }; + + template + struct _Adapt + { + using NFold = typename Repeat::Seq; + using Array = typename RebindVariadic::Type; + }; + + template + using _AdaptArray_t = typename _Adapt::Array; + } + + template + struct ArrayAdapt + : std::array + { + static_assert (AllSame() + ,"Array can only hold elements of uniform type"); + using Array = std::array; + + ArrayAdapt (Array const& o) : Array{o} { } + ArrayAdapt (Array && r) : Array{move(r)} { } + + template + ArrayAdapt (XS&& ...inits) + : Array{std::forward (inits)...} + { } + }; + + template + struct TupleClosureBuilder> + : TupleClosureBuilder<_AdaptArray_t> + { + }; + + }} // namespace lib::meta -#endif + + +namespace std { // Specialisation to support C++ »Tuple Protocol« and structured bindings. + + /** determine compile-time fixed size of the adapted std::array */ + template + struct tuple_size > + : integral_constant + { }; + + /** expose the element type of the adapted std::array */ + template + struct tuple_element > + : tuple_element::Array> + { }; + + // Note: the std::get function will pick the subclass +} +#endif /*LIB_META_TUPLE_CLOSURE_H*/ diff --git a/tests/library/meta/tuple-closure-test.cpp b/tests/library/meta/tuple-closure-test.cpp index 8afc7327b..2db0d019c 100644 --- a/tests/library/meta/tuple-closure-test.cpp +++ b/tests/library/meta/tuple-closure-test.cpp @@ -24,6 +24,7 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "lib/meta/tuple-closure.hpp" +#include "lib/format-util.hpp" #include "lib/test/diagnostic-output.hpp"////////////////TODO @@ -35,6 +36,7 @@ namespace test { using std::string; using std::tuple; + using std::array; using std::make_tuple; using lib::meta::_Fun; using lib::test::showType; @@ -53,6 +55,7 @@ namespace test { { tuple_bindFront(); tuple_bindBack(); + array_bindFront(); } @@ -94,6 +97,23 @@ SHOW_EXPR(showType()) Tup t2 = c2(make_tuple (-1)); CHECK (t2 == "«tuple»──(-1,3.1415927,pi)"_expect); } + + + /** @test use a std::array and handle it like a tuple to pre-fix some elements + */ + void + array_bindFront() + { + using Arr = array; + using Builder = TupleClosureBuilder; + + auto cons = Builder::closeFront (1u,2.3); + using FunType = _Fun; + CHECK (showType() == "ArrayAdapt (ArrayAdapt)"_expect); + + Arr arr = cons({3,4,5}); + CHECK (arr == "[1, 2, 3, 4, 5]"_expect); + } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index f95f556da..d6a231fb0 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -91202,11 +91202,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + + - - - @@ -106029,6 +106027,39 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + +

+ BuildFunSig +

+ +
+
+ + + + + + + + + + + + + + + +
@@ -154188,7 +154219,7 @@ std::cout << tmpl.render({"what", "World"}) << s Einschränkungen aufrufbar, aber diesen Umstand kann man nur ausnützen, solange das Lambda keine captures hat; denn andernfalls wird es ein wirkliches Objekt mit Storage und Lebenszyklus.

- Gefährlich wird es aber, wenn man auf den Umkehrschluß aufbaut, mit der Annahme, eine bool-konvertierbare Funktion sei deaktivierbar. Also wenn man dieser Flag eine zusätzliche Bedeutung zuweist + Gefährlich wird es aber, wenn man auf den Umkehrschluß aufbaut, mit der Annahme, eine bool-konvertierbare Funktion sei deaktivierbar. Also wenn man dieser Flag eine zusätzliche Bedeutung zuweist

@@ -154196,7 +154227,7 @@ std::cout << tmpl.render({"what", "World"}) << s - + @@ -154576,8 +154607,90 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + + + +

+ Auf den ersten Blick könnte man meinen, hier sollte die »is-a«-Relation wirksam werden. Allerdings kann die C++-Sprache das nicht leisten. Die funktionale Template-Subsprache ist rein statisch und komplett explizit: würde eine Template-Spezialisierung für einen Subtyp aktiviert, dann würde die Implementierung statisch auf der Basis-Klasse operieren, Value-Objekte würden geSLICEd und virtueller Dispatch würde aus dem Objekt heraus nicht stattfinden (weil es den this-Typ der Basisklasse verwenden muß). Wohingegen ein nach außen geleiteter Aufruf über eine Rück-Referenz dann doch wieder eine eventuell vorhandene VTable verwenden würde. Es gäbe somit keine zuverlässig wirksamen Erweiterungspunkte.... +

+ +
+ +
+ + + + +

+ Angenommen, wir haben eine Klasse, die aus std::tuple ableitet. Man würde sich wünschen, diese würde automatisch am Tuple-Protocol partizipieren. Tut sie aber nicht, weil schon die Spezialisierung von std::tuple_size nicht greift. Das ist aber auch gut so, denn sonst wäre man auf die Implementierung von std::tuple festgelegt, und könnte z.B. keine Anpassungen am Storage-Management machen. Wenn man so etwas möchte, dann sollte man die klassische-OO verwenden. +

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

+ Jede mögliche Template-Instantiierung wird als konkret gegebene Implementierung dem Overload-Resolution-Set hinzugefügt. Eine genau passende konkrete Implementierung wird gegenüber einer nur inferierten / synthetisierten Implementierung vorgezogen, und auch gegenüber einer konkreten Implementierung, für die eine Typ-Konvertierung notwendig ist. +

+ +
+
+ + + + + + + +

+ lib::meta::disable_if_self +

+ +
+
+
+
+
+
- + + + + + + + +

+ ABER nur wenn der Template-Parameter für diesen konkreten Aufruf  instantiiert wird; ein Typ-Parameter einer umschließenden Klasse gilt bereits als fest gebunden, also funktioniert mit damit kein »perfect forwarding«. Im Besonderen bei Konstruktoren ist das eine häufig anzutreffende Falle +

+ + +
+
+ + + +