From 4e26d655a89b8f759fc3d3f7bce54ede3ea77146 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 25 Mar 2024 23:41:14 +0100 Subject: [PATCH] Library: workaround for tricky problems with Template-Argument-Deduction We use a DataSrc template to access the actual data to be substituted. However, when applying the Text-Template, we need to pick the right specialisation, based on the type of the actual data provided. Here we face several challenges: * Class-Template-Argument-Deduction starts from the *primary* template's constructors. Without that, the compiler will only try the copy constructor and will never see the constructors of partial specialisations. This can be fixed by providing a ''dummy constructor''. * The specifics of how to provide a custom CTAD deduction guide for a **nested template** are not well documented. I have found several bug reports, and seemingly one of these bugs failed my my various attempts. Moreover it is ''not clear if such a deduction guide can even be given outside of the class definition scope.'' For the intended usage pattern this would be crucial, since users are expected to provide further specialisations of the DataSrc-template * Thus I resorted to the ''old school solution,'' which is to use a ''free builder function'' as an extension point. Thus users could provide further overloads for the `buildDataSrc()` function. * Unfortunately, SFINAE-Tricks are way more limited for function overload. Thus it seems impossible to have a generic and more specialised cases, unless all special cases are disjoint. Thus the solution is far from perfect, ''yet for the current situation it seems sufficient'' (and C++20 Concepts will greatly help to resolve this kind of problems) --- src/lib/text-template.hpp | 35 ++++++-- wiki/thinkPad.ichthyo.mm | 168 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 191 insertions(+), 12 deletions(-) diff --git a/src/lib/text-template.hpp b/src/lib/text-template.hpp index 4a16bee04..b101debcd 100644 --- a/src/lib/text-template.hpp +++ b/src/lib/text-template.hpp @@ -317,8 +317,6 @@ namespace lib { bool loopFurther(); }; - template - using InstanceIter = ExploreIter>>; ActionSeq actions_; @@ -328,7 +326,7 @@ namespace lib { { } template - InstanceIter + auto submit (DAT const& data) const; template @@ -536,6 +534,8 @@ namespace lib { static_assert (not sizeof(DAT), "unable to bind this data source " "for TextTemplate instantiation"); + + DataSource (DAT const&); }; using MapS = std::map; @@ -561,7 +561,7 @@ namespace lib { template<> struct TextTemplate::DataSource { - MapS const * data_; + MapS const * data_{nullptr}; string keyPrefix_{}; bool isNested() { return not isnil (keyPrefix_); } @@ -642,6 +642,29 @@ namespace lib { } }; + namespace {// help the compiler with picking the proper specialisation for the data binding + + template> > + inline auto + bindDataSource(STR const& spec) + { + return TextTemplate::DataSource{spec}; + } + + inline auto + bindDataSource(MapS const& map) + { + return TextTemplate::DataSource{map}; + } + + /* Why this approach? couldn't we use CTAD? + * - for one, there are various compiler bugs related to nested templates and CTAD + * - moreover I am unable to figure out how to write a deduction guide for an + * user provided specialisation, given possibly within another header. + */ + } + + /* ======= implementation of the instantiation state ======= */ @@ -821,10 +844,10 @@ namespace lib { * thereby producing a sequence of `std::string_view&` */ template - inline TextTemplate::InstanceIter + inline auto TextTemplate::submit (DAT const& data) const { - return explore (InstanceCore{actions_, DataSource{data}}); + return explore (InstanceCore{actions_, bindDataSource(data)}); } /** submit data and materialise rendered results into a single string */ diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 0ead2d102..8219d67db 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -79558,7 +79558,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -113501,14 +113501,93 @@ std::cout << tmpl.render({"what", "World"}) << s - + - + + + + + + + +

+ zunächst einmal: Deklaration kann auto verwenden +

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

+ Grund: die expliziten Spezialisierungen kommen überhaupt erst ins's Spiel, wenn der Compiler die konkreten Template-Argumente bereits erschlossen hat. Vorher schaut er nur in das primäre Template... und wenn das keinen Konstruktor hat, dann kennt der Compiler nur den Copy-Konstruktor. Die resultierende Fehlermeldung ist dann unglaublich hilfreich... +

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

+ Das sind die Limitierungen bei Function-Overloads. Mit Template-Deduction-Guides hätten wir dieses Problem vermutlich nicht (wenn wir sie denn schreiben könnten). +

+

+ +

+

+ Im Detail: Bei der Function Overload-Resolution werden die verschiedenen Kandidaten geordnet. Dabei werden zunächst alle überschüssigen und Default-Argumente weggestrichen. Konsequenz: ein enable-If kann zwar einen einzelnen Overload entfernen — wenn er aber nicht entfernt wurde, stehen zwei äquivalente Overloads da, und es gibt einen Compilation-Fehler. Rückgabewerte helfen hier auch nichts (die tragen nur zur const-ness-Auswahl bei). Das heißt, bei Function-Overload-Resolution muß das enable-If auf einem weiteren  Parameter stehen, der auch tatsächlich verwendet wird. Und an der Stelle wird's dann wirklich so trickreich, das es mir fragil erscheint. +

+ + +
+ +
+ + + + + + + + + + +
+
+
+ +
@@ -120849,7 +120928,8 @@ std::cout << tmpl.render({"what", "World"}) << s
- + + @@ -121044,6 +121124,80 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + + + + + + + +
+ explicit-specifier(optional) template-name (  parameter-decl-clause ) -> simple-template-id  ; + + +
+ +
+
+ + + + + + + + + + + + + + +

+ Automatic Deduction setzt ctor im primären  Template vorraus +

+ +
+ + + + + + + + +

+ ⟹ dann findet der Compiler ohne Hilfe keinen Construktor +

+ +
+ + + + + +

+ bzw. er findet nur den Copy-Konstruktur +

+ +
+ +
+
+
+ + + + + + + @@ -122442,7 +122596,9 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - + + + @@ -123194,7 +123350,7 @@ unsigned int ThreadIdAsInt = *static_cast<unsigned int*>(static_cast<vo - +