Invocation: build a solution to adapt std::array

...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.
This commit is contained in:
Fischlurch 2025-02-17 18:36:23 +01:00
parent 8bb332cc5e
commit 0cad2dacc0
5 changed files with 245 additions and 12 deletions

View file

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

View file

@ -35,6 +35,7 @@
#include "lib/symbol.hpp"
#include "lib/util.hpp"
#include <array>
#include <string>
#include <vector>
#include <sstream>
@ -259,5 +260,17 @@ namespace util {
}
/** convenient pretty-printer for std::array instances */
template<typename T, std::size_t N>
struct StringConv<std::array<T,N>>
{
static std::string
invoke (std::array<T,N> const& arr) noexcept
{
return util::toStringBracket (arr);
}
};
} // namespace util
#endif /*LIB_FORMAT_UTIL_H*/

View file

@ -42,6 +42,7 @@
#include <utility>
#include <tuple>
#include <array>
@ -96,5 +97,91 @@ namespace meta{
};
/* ===== adapt array for tuple-like signature ===== */
template<typename...TTT>
struct ArrayAdapt;
namespace {
template<typename...TTT>
struct AllSame
: std::true_type
{ };
template<typename T1, typename T2, typename...TS>
struct AllSame<T1,T2,TS...>
: __and_<is_same<T1,T2>
,AllSame<T2,TS...>
>
{ };
template<typename T, size_t N>
struct Repeat
{
using Rem = typename Repeat<T, N-1>::Seq;
using Seq = typename Prepend<T,Rem>::Seq;
};
template<typename T>
struct Repeat<T,0>
{
using Seq = TySeq<>;
};
template<typename T, size_t N>
struct _Adapt
{
using NFold = typename Repeat<T,N>::Seq;
using Array = typename RebindVariadic<ArrayAdapt, NFold>::Type;
};
template<typename T, size_t N>
using _AdaptArray_t = typename _Adapt<T,N>::Array;
}
template<typename T, typename...TT>
struct ArrayAdapt<T,TT...>
: std::array<T, 1+sizeof...(TT)>
{
static_assert (AllSame<T,TT...>()
,"Array can only hold elements of uniform type");
using Array = std::array<T, 1+sizeof...(TT)>;
ArrayAdapt (Array const& o) : Array{o} { }
ArrayAdapt (Array && r) : Array{move(r)} { }
template<typename...XS>
ArrayAdapt (XS&& ...inits)
: Array{std::forward<XS> (inits)...}
{ }
};
template<typename T, size_t N>
struct TupleClosureBuilder<std::array<T,N>>
: TupleClosureBuilder<_AdaptArray_t<T,N>>
{
};
}} // 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<typename...TTT>
struct tuple_size<lib::meta::ArrayAdapt<TTT...> >
: integral_constant<size_t, sizeof...(TTT)>
{ };
/** expose the element type of the adapted std::array */
template<size_t I, typename...TTT>
struct tuple_element<I, lib::meta::ArrayAdapt<TTT...> >
: tuple_element<I, typename lib::meta::ArrayAdapt<TTT...>::Array>
{ };
// Note: the std::get<i> function will pick the subclass
}
#endif /*LIB_META_TUPLE_CLOSURE_H*/

View file

@ -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<FunType::Sig>())
Tup t2 = c2(make_tuple (-1));
CHECK (t2 == "«tuple<int, double, string>»──(-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<int,5>;
using Builder = TupleClosureBuilder<Arr>;
auto cons = Builder::closeFront (1u,2.3);
using FunType = _Fun<decltype(cons)>;
CHECK (showType<FunType::Sig>() == "ArrayAdapt<int, int, int, int, int> (ArrayAdapt<int, int, int>)"_expect);
Arr arr = cons({3,4,5});
CHECK (arr == "[1, 2, 3, 4, 5]"_expect);
}
};

View file

@ -91202,11 +91202,9 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1733946498997" ID="ID_1699016974" MODIFIED="1733946531556" TEXT="l&#xe4;&#xdf;t sich direkt auf std::integral_constant zur&#xfc;ckf&#xfc;hren"/>
<node CREATED="1733946533117" ID="ID_1111331922" MODIFIED="1733946542098" TEXT="implementiert f&#xfc;r beliebige HeteroData"/>
<node CREATED="1733946542902" ID="ID_139021842" MODIFIED="1733946550153" TEXT="auch gleich implementiert f&#xfc;r StorageFrame"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1733946551317" FOLDED="true" ID="ID_555431810" MODIFIED="1733946586881" TEXT="Beachte: Template-Spezialisierungen greifen nicht f&#xfc;r abgeleitete Klassen">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1733946551317" ID="ID_555431810" MODIFIED="1739806634258" TEXT="Beachte: Template-Spezialisierungen greifen nicht f&#xfc;r abgeleitete Klassen">
<arrowlink COLOR="#833d47" DESTINATION="ID_872833596" ENDARROW="Default" ENDINCLINATION="-889;37;" ID="Arrow_ID_395284049" STARTARROW="None" STARTINCLINATION="-945;38;"/>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1733946590316" ID="ID_626664530" MODIFIED="1733946600694" TEXT="nach zwemal Nachdenken: ist auch sinnvoll"/>
<node CREATED="1733946621803" ID="ID_333291639" MODIFIED="1733946638813" TEXT="man w&#xfc;rde dann statisch auf der Basis-Klasse operieren"/>
<node CREATED="1733946640233" ID="ID_1111919688" MODIFIED="1733946648532" TEXT="es g&#xe4;be keine Erweiterungspunkte"/>
</node>
</node>
<node COLOR="#338800" CREATED="1733946654687" ID="ID_1461671576" MODIFIED="1733946693210" TEXT="tuple_element">
@ -106029,6 +106027,39 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node COLOR="#338800" CREATED="1739752776321" ID="ID_376565948" MODIFIED="1739752785816" TEXT="TupleClosure_test zur Dokumentation">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739756728914" ID="ID_1671606956" MODIFIED="1739813490672" TEXT="Auf std::array anwendbar machen">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739756741667" ID="ID_20824263" MODIFIED="1739813448142" TEXT="Problem: spezielle Typsignatur">
<icon BUILTIN="pencil"/>
<node CREATED="1739756760373" ID="ID_753184722" MODIFIED="1739756775186" TEXT="deshalb ist in jedem Fall eine partielle Spezialisierung notwendig"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1739756922215" ID="ID_1294911921" MODIFIED="1739798944791" TEXT="Problem: eine Builder-Funktion mit expliziter Argument-Anzahl">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1739756977184" ID="ID_1621203501" MODIFIED="1739757006679" TEXT="den Signatur-Typ k&#xf6;nnte man noch leicht synthetisieren">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
BuildFunSig
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1739757025236" ID="ID_416963925" MODIFIED="1739757054617" TEXT="aber woher bekomme ich die zu verarbeitende Funktion??"/>
<node COLOR="#5b280f" CREATED="1739798954692" ID="ID_381914136" MODIFIED="1739798965636" TEXT="als std::function aufbauen...">
<icon BUILTIN="button_cancel"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1739798967457" ID="ID_1274106457" MODIFIED="1739813473419" TEXT="anderer Ansatz: Tupel-Typsignatur emulieren">
<icon BUILTIN="idea"/>
<node CREATED="1739798985823" ID="ID_9547449" MODIFIED="1739799013054" TEXT="...dann k&#xf6;nnte die Standard-Impl verwendet werden"/>
<node CREATED="1739799014639" ID="ID_205042889" MODIFIED="1739799031581" TEXT="brauche daf&#xfc;r einen automatisch konvertierbaren Adapter"/>
<node CREATED="1739807307000" ID="ID_382558613" MODIFIED="1739812304265" TEXT="mu&#xdf; dann wohl Tuple-Protocol reimplementieren">
<arrowlink COLOR="#846867" DESTINATION="ID_872833596" ENDARROW="Default" ENDINCLINATION="-1112;-50;" ID="Arrow_ID_577510339" STARTARROW="None" STARTINCLINATION="-1320;72;"/>
<icon BUILTIN="smiley-oh"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
@ -154188,7 +154219,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
Einschr&#228;nkungen aufrufbar, aber diesen Umstand kann man nur ausn&#252;tzen, solange das Lambda keine captures hat; denn andernfalls wird es ein wirkliches Objekt mit Storage und Lebenszyklus.
</p>
<p>
<i>Gef&#228;hrlich wird es</i>&#160;aber, wenn man auf den Umkehrschlu&#223; aufbaut, mit der Annahme, eine <b>bool-konvertierbare Funktion sei deaktivierbar</b>. Also wenn man dieser Flag eine zus&#228;tzliche Bedeutung zuweist
<i>Gef&#228;hrlich wird es </i>aber, wenn man auf den Umkehrschlu&#223; aufbaut, mit der Annahme, eine <b>bool-konvertierbare Funktion sei deaktivierbar</b>. Also wenn man dieser Flag eine zus&#228;tzliche Bedeutung zuweist
</p>
</body>
</html></richcontent>
@ -154196,7 +154227,7 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
<node COLOR="#435e98" CREATED="1734712435722" ID="ID_1295746147" MODIFIED="1734712695180" TEXT="beide F&#xe4;lle sind mit Type-Traits unterscheidbar">
<icon BUILTIN="idea"/>
<node CREATED="1734712469719" ID="ID_1645609663" MODIFIED="1734712480849" TEXT="std::function ist nur explizit bool-Konvertierbar"/>
<node CREATED="1734712482172" ID="ID_261058818" MODIFIED="1734712533377" TEXT="die Konvertierbarkeit das triviale &#x3bb; in den Signatur-Pointer kann man detektierren"/>
<node CREATED="1734712482172" ID="ID_261058818" MODIFIED="1734712482172" TEXT="die Konvertierbarkeit eines trivialen &#x3bb; in den Signatur-Pointer kann man detektieren"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" CREATED="1734724075728" ID="ID_1330421689" LINK="#ID_475098889" MODIFIED="1734724182332" STYLE="bubble" TEXT="...siehe L&#xf6;sung in _ParamFun (feed-manifold.hpp)">
<icon BUILTIN="list"/>
@ -154576,8 +154607,90 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
<node CREATED="1703176027969" ID="ID_434248727" MODIFIED="1703176047234" TEXT="Conversions happen at asignment or force-conversion and can alter the value"/>
</node>
</node>
<node CREATED="1739806612780" ID="ID_1137910648" MODIFIED="1739806618430" TEXT="Spezialisierungen">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1733946551317" ID="ID_872833596" MODIFIED="1739812304266" TEXT="Template-Spezialisierungen greifen nicht f&#xfc;r abgeleitete Klassen">
<linktarget COLOR="#833d47" DESTINATION="ID_872833596" ENDARROW="Default" ENDINCLINATION="-889;37;" ID="Arrow_ID_395284049" SOURCE="ID_555431810" STARTARROW="None" STARTINCLINATION="-945;38;"/>
<linktarget COLOR="#846867" DESTINATION="ID_872833596" ENDARROW="Default" ENDINCLINATION="-1112;-50;" ID="Arrow_ID_577510339" SOURCE="ID_382558613" STARTARROW="None" STARTINCLINATION="-1320;72;"/>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1733946590316" ID="ID_346822453" MODIFIED="1739807049760" TEXT="nach zwemal Nachdenken: ist auch sinnvoll">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Auf den ersten Blick k&#246;nnte man meinen, hier sollte die &#187;is-a&#171;-Relation wirksam werden. Allerdings kann die C++-Sprache das nicht leisten. Die funktionale Template-Subsprache ist rein statisch und komplett explizit: w&#252;rde eine Template-Spezialisierung f&#252;r einen Subtyp aktiviert, dann w&#252;rde die Implementierung statisch auf der Basis-Klasse operieren, Value-Objekte w&#252;rden geSLICEd und virtueller Dispatch w&#252;rde aus dem Objekt heraus nicht stattfinden (weil es den this-Typ der Basisklasse verwenden mu&#223;). Wohingegen ein nach au&#223;en geleiteter Aufruf &#252;ber eine R&#252;ck-Referenz dann doch wieder eine eventuell vorhandene VTable verwenden w&#252;rde. Es g&#228;be somit keine zuverl&#228;ssig wirksamen Erweiterungspunkte....
</p>
</body>
</html></richcontent>
<icon BUILTIN="smiley-neutral"/>
</node>
<node CREATED="1739807051288" ID="ID_1504352780" MODIFIED="1739807211620" TEXT="relevantes Beispiel: &#xbb;Tuple-Protocol&#xab;">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Angenommen, wir haben eine Klasse, die aus std::tuple ableitet. Man w&#252;rde sich w&#252;nschen, diese w&#252;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&#228;re man auf die Implementierung von std::tuple festgelegt, und k&#246;nnte z.B. keine Anpassungen am Storage-Management machen. Wenn man <i>so etwas m&#246;chte, dann sollte man die klassische-OO verwenden.</i>
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1739807542287" ID="ID_1950397403" MODIFIED="1739807563117" TEXT="Vorsicht Falle: Funktion-Overloads beachten die Basisklasse">
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1739806725444" ID="ID_117708999" MODIFIED="1739806732689" TEXT="Concepts?">
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="help"/>
</node>
</node>
<node CREATED="1739804758227" ID="ID_1591357203" MODIFIED="1739804766069" TEXT="overload resolution">
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1620405462300" ID="ID_1712821553" LINK="https://issues.lumiera.org/ticket/963" MODIFIED="1739804484292" TEXT="Probleme mit Variadic-Template-Konstruktor (#963)">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1739804086379" ID="ID_1261892764" MODIFIED="1739804259340" TEXT="der gilt als ein besserer match gg&#xfc; generiertem Copy-Konstruktor">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Jede m&#246;gliche Template-Instantiierung wird als konkret gegebene Implementierung dem Overload-Resolution-Set hinzugef&#252;gt. Eine genau passende konkrete Implementierung wird gegen&#252;ber einer nur inferierten / synthetisierten Implementierung vorgezogen, und auch gegen&#252;ber einer konkreten Implementierung, f&#252;r die eine Typ-Konvertierung notwendig ist.
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1739804265419" ID="ID_1015040603" MODIFIED="1739804282815" TEXT="man mu&#xdf; dann die Copy-Konstrukturen explizit dazu definieren"/>
<node CREATED="1739804283513" ID="ID_1923467578" MODIFIED="1739804366546" TEXT="oder den Template-Konstruktor im Spezialfall deaktivieren">
<icon BUILTIN="idea"/>
<node CREATED="1739804303374" ID="ID_1920192438" MODIFIED="1739804353277">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
<font color="#827474" face="Monospaced">lib::meta::</font><font color="#392f56" face="Monospaced"><b>disable_if_self</b></font>
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
</node>
</node>
<node CREATED="1620405445951" ID="ID_1127258714" MODIFIED="1620405460977" TEXT="&#xbb;perfect forwarding&#xab;">
<node CREATED="1620405462300" ID="ID_1712821553" MODIFIED="1620405478341" TEXT="hatte nun mehrfach Probleme in Kombination mit Variadic-Templates"/>
<node BACKGROUND_COLOR="#d5bfb3" CREATED="1739804777416" ID="ID_1413281090" MODIFIED="1739804885525" TEXT="Vorsicht Falle: setzt Match auf freien Template-Parameter voraus">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1739804887609" ID="ID_357922580" MODIFIED="1739804899364" TEXT="Type&amp;&amp; ist stets eine RValue-Referenz"/>
<node CREATED="1739804907078" ID="ID_413386124" MODIFIED="1739805133069" TEXT="aber template&lt;typename X&gt; X&amp;&amp; &#x27f9; X kann als Type const&amp; deduziert werden">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
ABER nur wenn der Template-Parameter <b>f&#252;r diesen konkreten Aufruf</b>&#160; instantiiert wird; ein Typ-Parameter einer umschlie&#223;enden Klasse gilt bereits als fest gebunden, also <i>funktioniert mit damit kein &#187;perfect forwarding&#171;.</i>&#160;Im Besonderen bei Konstruktoren ist das eine h&#228;ufig anzutreffende Falle
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1739804952757" ID="ID_996711039" MODIFIED="1739804967987" TEXT="es ist und bleibt ein Compilerbau-Trick">
<icon BUILTIN="smiley-angry"/>
</node>
</node>
<node CREATED="1620405479689" ID="ID_53418276" MODIFIED="1620405514581" TEXT="std::forward&lt;ARGS&gt;(args) ... ">
<node CREATED="1620405515532" ID="ID_89296387" MODIFIED="1620405524655" TEXT="wenn man damit einen ctor aufruft..."/>
<node CREATED="1620405525238" ID="ID_728173351" MODIFIED="1620405542709" TEXT="dann w&#xe4;hlt das den copy-ctor, nicht den move-ctor"/>