Invocation: problems with function template overload resolution

Why is our specialisation of `std::get` not picked up by the compiler?

 * it must somehow be related to the fact that `std::tuple` itself is a base class of lib::HeteroData
 * if we remove this inheritance relation, our specialisation is used by the compiler and works as intended
 * however, this strange behaviour can not be reproduced in a simple synthetic setup

It must be some further subtlety which marks the tuple case as preferrable
This commit is contained in:
Fischlurch 2024-12-12 04:38:55 +01:00
parent f2f321a3b8
commit 4a7e1eeb36
4 changed files with 136 additions and 153 deletions

View file

@ -1,18 +1,15 @@
/* try.cpp - to try out and experiment with new features....
* scons will create the binary bin/try
*/
// 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
* Attempt to generalise the util::isSameObject, to support a mix of pointers and references.
* This is a long standing requirement, yet I made several failed attempts in the past,
* due to difficulties detecting a pointer after perfect-forwarding the argument. However,
* it is not even necessary to perfect-forward, if all we want is to extract the raw address.
*
* To my defence, I should add that I never took the time to conduct a proper experiment
* in a stand-alone setup; investigating directly in "lib/util.hpp" is a waste of time.
* 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.
*/
typedef unsigned int uint;
@ -25,109 +22,56 @@ typedef unsigned int uint;
#include <utility>
#include <string>
#include <tuple>
using lib::rani;
using std::move;
using std::string;
using lib::test::showType;
template<class X>
inline const void*
getAd (X& x)
{
return static_cast<const void*> (std::addressof(x));
}
template<class X>
inline const void*
getAd (X* x)
{
return static_cast<const void*> (x);
}
template<typename...TS>
string
showTypes()
{
return "<" + ((showType<TS>()+",") + ... + ">");
}
template<class A, class B>
inline bool
isSameOb (A const& a, B const& b)
{
return getAd(a)
== getAd(b);
}
using std::tuple;
struct Boo
{
string moo;
short goo;
Boo(short uh)
: moo{util::toString(uh-1)}
, goo(uh+1)
{ }
operator string() const
{
return moo+util::toString(goo);
}
};
struct SuBoo : Boo
{
long poo = rani(goo);
using Boo::Boo;
};
struct B { };
inline Boo*
asBoo (void* mem)
{// type tag to mark memory address as Buffer
return static_cast<Boo*> (mem);
}
struct D1 : B { };
struct D2 : D1 { };
string getty (B&) { return "getty-B&"; }
string getty (D1&&){ return "getty-D1&&"; }
string getty (D1&) { return "getty-D1&"; }
template<class...TS>
string getty (tuple<TS...>&) { return "getty-tuple& "+showTypes<TS...>(); }
template<class...TS>
struct F : tuple<TS...> { };
template<class...TS>
struct FD1 : F<TS...> {};
template<class...TS>
struct FD2 : FD1<TS...> {};
template<class...TS>
string getty (FD1<TS...>&) { return "getty-FD1& "+showTypes<TS...>(); }
int
main (int, char**)
{
Boo boo(23);
Boo booo(23);
Boo* boop = &boo;
Boo const* const beep = boop;
cout << boo << endl;
SHOW_EXPR(util::showAdr(getAd(boo )))
SHOW_EXPR(util::showAdr(getAd(&boo)))
SHOW_EXPR(util::showAdr(getAd(boop)))
SHOW_EXPR(util::showAdr(getAd(beep)))
SHOW_EXPR(isSameOb(boop, beep))
SHOW_EXPR(isSameOb(&boop, &beep))
SHOW_EXPR(isSameOb(boo, beep))
SHOW_EXPR(isSameOb(*beep, booo))
SHOW_EXPR(isSameOb(boo, boo.moo))
SHOW_EXPR(isSameOb(boo, &boo.moo))
SHOW_EXPR(isSameOb(boo.moo, booo))
SHOW_EXPR(isSameOb(booo, asBoo(&booo.moo)))
SHOW_EXPR(isSameOb(booo, asBoo(&booo.goo)))
const void* voo = boop;
SHOW_EXPR(isSameOb(voo, boo))
SHOW_EXPR(isSameOb(voo, boop))
SHOW_EXPR(isSameOb(voo, asBoo(&boo.moo)))
SHOW_EXPR(isSameOb(voo, asBoo(&booo.moo)))
SHOW_EXPR(isSameOb(voo, asBoo(&boo.goo)))
D2 d2;
SHOW_EXPR(getty(d2));
Boo&& roo = move(boo);
SHOW_EXPR(isSameOb(roo, boo))
SHOW_EXPR(isSameOb(voo, roo))
SHOW_EXPR(isSameOb(voo, Boo{roo}))
SuBoo* suBoo = static_cast<SuBoo*>(&boo);
SHOW_EXPR(isSameOb(boo, suBoo))
SHOW_EXPR(isSameOb(boo, suBoo->moo))
SHOW_EXPR(isSameOb(voo, suBoo->moo))
SHOW_EXPR(isSameOb(voo, suBoo->poo))
SuBoo sudo{*suBoo};
SHOW_EXPR(isSameOb(sudo, suBoo))
suBoo = &sudo;
SHOW_EXPR(isSameOb(sudo.poo, suBoo->poo))
FD2<int, char**> fd2;
SHOW_EXPR(getty(fd2));
cout << "\n.gulp.\n";
return 0;

View file

@ -74,7 +74,7 @@ namespace lib {
template<size_t seg, typename...DATA>
struct StorageFrame
: protected StorageLoc
: StorageLoc
, std::tuple<DATA...>
{
using Tuple = std::tuple<DATA...>;
@ -88,13 +88,16 @@ namespace lib {
template<size_t seg, typename...DATA, class TAIL>
class HeteroData<meta::Node<StorageFrame<seg, DATA...>,TAIL>>
: StorageFrame<seg, DATA...>
// : StorageFrame<seg, DATA...>
// : util::NonCopyable
{
using _Self = HeteroData;
using _Tail = HeteroData<TAIL>;
using Tuple = std::tuple<DATA...>;
using Frame = StorageFrame<seg, DATA...>;
Frame frame_;
static constexpr size_t localSiz = sizeof...(DATA);
template<size_t slot>
@ -107,14 +110,17 @@ namespace lib {
_Tail&
accessTail()
{
REQUIRE (Frame::next, "HeteroData storage logic broken: follow-up extent not yet allocated");
return * reinterpret_cast<_Tail*> (Frame::next);
REQUIRE (frame_.next, "HeteroData storage logic broken: follow-up extent not yet allocated");
return * reinterpret_cast<_Tail*> (frame_.next);
}
template<typename...XX>
friend class HeteroData; ///< allow chained types to use recursive type definitions
using Frame::Frame;
template<typename...INIT>
HeteroData (INIT&& ...initArgs)
: frame_{std::forward<INIT> (initArgs)...}
{ }
public:
HeteroData() = default;
@ -135,7 +141,7 @@ namespace lib {
{
static_assert (slot < size(), "HeteroData access index beyond defined data");
if constexpr (slot < localSiz)
return std::get<slot> (*this);
return std::get<slot> (frame_);
else
return accessTail().template get<slot-localSiz>();
}

View file

@ -131,8 +131,8 @@ namespace test{
CHECK ((showType<std::tuple_element_t<1, Block2>>() == "string"_expect));
CHECK (std::get<0> (chain2) == 42);
// CHECK (std::get<1> (chain2) == "1.618034"_expect); ////////////////////////////TODO somehow the overload for std::tuple takes precedence here
// CHECK (std::get<2> (chain2) == "Φ"_expect);
CHECK (std::get<1> (chain2) == "1.618034"_expect); ////////////////////////////TODO somehow the overload for std::tuple takes precedence here
CHECK (std::get<2> (chain2) == "Φ"_expect);
CHECK (std::get<0> (b2) == "1.618034"_expect);
CHECK (std::get<1> (b2) == "Φ"_expect);

View file

@ -22649,9 +22649,7 @@
<node CREATED="1540639934819" ID="ID_1733133201" MODIFIED="1557498707225" TEXT="...da es sich um ein Attribut-Binding handelt"/>
<node CREATED="1540639945978" ID="ID_1136500098" MODIFIED="1576282358082" TEXT="(sollte aber auch niemals auftreten)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...da wir unterstellen, da&#223; das Gegenst&#252;ck im Session-Modell,
@ -22986,9 +22984,7 @@
<node CREATED="1480780253397" ID="ID_1966427410" MODIFIED="1518487921082" TEXT="zum Preis von deutlich Mehraufwand"/>
<node CREATED="1480780260508" ID="ID_1565963038" MODIFIED="1539382659215" TEXT="plus einer korrodierenden Wirkung">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...welche darin besteht,
@ -23396,9 +23392,7 @@
</node>
<node CREATED="1666476675737" ID="ID_489715422" MODIFIED="1678406261536">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<u>Dienst</u>: aktueller Skalenfaktor + Offset
@ -24200,9 +24194,7 @@
</node>
<node CREATED="1582992315675" ID="ID_1937379462" MODIFIED="1582992414632">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Ausl&#246;sen durch <b><font color="#7a264c">signalStructureChange</font></b>
@ -25342,9 +25334,7 @@
<node CREATED="1674603535772" ID="ID_1899820582" MODIFIED="1674603556532" TEXT="manchmal erfolgt dann kein Update / re-Draw beim Dragging"/>
<node CREATED="1674603557497" ID="ID_1941260000" MODIFIED="1674603679778" TEXT="die Position wird aber korrekt berechnet und gesetzt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
nachweislich...
@ -27410,9 +27400,7 @@
<node CREATED="1677365093931" ID="ID_1368458373" MODIFIED="1677365099512" TEXT="Bogen">
<node CREATED="1677368264383" ID="ID_1877634925" MODIFIED="1677368499104" TEXT="jeweils in Radians">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
&#8630; freeCAD : rechtsdrehend(mathematisch)
@ -30944,9 +30932,7 @@
<node CREATED="1555020163776" ID="ID_1470546162" MODIFIED="1557498707229" TEXT="collapsed == Scope durch Ruler ersetzt"/>
<node CREATED="1555020175102" ID="ID_1798602" MODIFIED="1557498707229">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<i>das </i>ist ein reiner (pesistent) presentation state
@ -31769,9 +31755,7 @@
</node>
<node CREATED="1563033292565" ID="ID_1632267547" MODIFIED="1563033473194" TEXT="aktuell ist der Pfad schon sinnvoll (aber noch nicht ideal)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
window:backdrop:dir-ltr.background box:backdrop:dir-ltr.vertical box:backdrop:dir-ltr[2/3].horizontal widget:backdrop:dir-ltr[2/2] widget:backdrop:dir-ltr paned:backdrop:dir-ltr.vertical widget:backdrop:dir-ltr box:backdrop:dir-ltr.vertical notebook:backdrop:dir-ltr[1/1].frame paned:backdrop:dir-ltr.horizontal box:backdrop:dir-ltr.vertical fork.timeline
@ -35471,9 +35455,7 @@
<node CREATED="1666474895435" ID="ID_1743952496" MODIFIED="1666474895435" TEXT="per Zeit an den Canvas geheftet"/>
<node CREATED="1612473206764" ID="ID_383710389" MODIFIED="1666475206066" TEXT="Widget kann sich selbst um seine eigene Ausdehnung k&#xfc;mmern">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
das mag &#252;berraschend sein &#8212; erst dachte ich, es sein ein Nachteil, aber tats&#228;chlich f&#252;gt es sich nat&#252;rlich in die Layout-Steuerung von GTK ein; denn GTK fragt ja das Widget nach seiner ben&#246;tigten Ausdehnung, und das ist auch genau der Mechanismus, &#252;ber den wir eine Beschr&#228;nkung auf eine vorgegebene zeitliche Ausdehnung realisieren. Zudem hat das Widget die Information &#252;ber seine eigene Ausdehnung als Zeitangabe vorliegen, und das pa&#223;t dann auch gut in dieses Aufrufschema
@ -37735,9 +37717,7 @@
<node CREATED="1616709888782" ID="ID_1557466321" MODIFIED="1616709896625" TEXT="mit einer Interaktionsform"/>
<node CREATED="1616709937487" ID="ID_1221710731" MODIFIED="1616709984652" TEXT="idealerweise abstrakt und &#xfc;bergreifend">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Das Ideal w&#228;re, da&#223; man das nicht speziell f&#252;r Maus, Tastatur, Stift und Hardware einrichten mu&#223;, sondern lediglich qualifizieren
@ -38967,9 +38947,7 @@
</node>
<node CREATED="1620406074960" ID="ID_1455403417" MODIFIED="1620406114628">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
wenn wir trotzdem einen Overrun-Check haben wollen,
@ -39378,9 +39356,7 @@
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1616684916746" ID="ID_571239713" MODIFIED="1616684947088">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
2.Schritt: <i>rektifizieren</i>
@ -39647,9 +39623,7 @@
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1672672729523" ID="ID_204825395" MODIFIED="1672672853253" TEXT="vorerst durch generische Regel im Code festgelegt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...sp&#228;ter m&#246;chte ich diesen als eigenen zus&#228;tzlichen Freiheitsgrad einf&#252;hren; dazu mu&#223; aber bereits mehr &#252;ber den Verwendungszusammenhang bekannt sein; insofern habe ich vorerst eine generische Regel implementiert, die den Zoom-Anchor anhand der Position zum Gesamt-Canvas festlegt (ganz am Anfgang liegt er ganz links, am Ende ganz rechts)
@ -40050,9 +40024,7 @@
</node>
<node CREATED="1668176234367" ID="ID_762072729" MODIFIED="1668176354941" TEXT="...sondern an allen potentiell gef&#xe4;hrlichen Rechenschritten explizit auf Wert-Erhalt abgestellt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
das hei&#223;t...
@ -88268,8 +88240,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
sollte <i>im Prinzip ja</i>&#160;einfach sein...
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<arrowlink COLOR="#3362d9" DESTINATION="ID_609631801" ENDARROW="Default" ENDINCLINATION="-1197;56;" ID="Arrow_ID_145396423" STARTARROW="None" STARTINCLINATION="-581;47;"/>
<icon BUILTIN="ksmiletris"/>
</node>
<node COLOR="#338800" CREATED="1733946491575" ID="ID_1678748014" MODIFIED="1733946694559" TEXT="tuple_len">
@ -88296,6 +88268,57 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="broken-line"/>
<node CREATED="1733946765784" ID="ID_1087783493" MODIFIED="1733946780713" TEXT="das bekannte Problem mit Funktion Overloads"/>
<node CREATED="1733946781559" ID="ID_1787763968" MODIFIED="1733946793699" TEXT="es ist dann eben keine partielle Template-Spezialisierung"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1733960761558" ID="ID_1977939692" MODIFIED="1733960781184" TEXT="trotzdem verstehe ich das Problem nicht.....">
<icon BUILTIN="forward"/>
<node CREATED="1733960786954" ID="ID_1320100813" MODIFIED="1733960810467" TEXT="er sollte dann doch den gegebenen Overload verwenden"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1733960871534" ID="ID_1458313610" MODIFIED="1733960901968" TEXT="versuche mal den std::tuple-Basistyp herauszunehmen">
<icon BUILTIN="idea"/>
<node CREATED="1733964059183" ID="ID_39714121" MODIFIED="1733964345755" TEXT="vorsicht Empty-Baseclass">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
HeteroData darf dann <i>gar keine Basisklasse haben.</i>&#160;Denn die empty-baseclass-optimisation ist optional; letztlich l&#228;&#223;t der Standard dem konkreten Compiler einigen Spielraum bez&#252;glich des Speicher-Layouts
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1733964072098" ID="ID_374917909" MODIFIED="1733964090307" TEXT="Frame mu&#xdf; als erstes Member in HeteroData liegen"/>
<node CREATED="1733964092091" ID="ID_715175218" MODIFIED="1733964257280" TEXT="nat&#xfc;rlich w&#xe4;re diese L&#xf6;sung bedeutent unklarer">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Die Definition mit dem Frame als Basisklasse erscheint mir sauberer, denn sie legt zweifelsfrei fest, wo der Frame sein soll und da&#223; er mit dem Anfang von HeteroData zusammenfallen soll. Au&#223;erdem dokumentiert das eine is-a Beziehung (wenngleich auch die Sichtbarkeit eingeschr&#228;nkt ist).
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1733964348236" ID="ID_1028912337" MODIFIED="1733970622428" TEXT="damit funktioniert der Test &#x27f9; meine Spezialisierung als Solche war korrekt"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1733964371585" ID="ID_440070050" MODIFIED="1733964386475" TEXT="warum wird dann trotzdem der Tuple-Overload gew&#xe4;hlt?">
<icon BUILTIN="help"/>
<node CREATED="1733970706145" ID="ID_1987504943" MODIFIED="1733970763130" TEXT="Overload-Resolution">
<node CREATED="1733970768481" ID="ID_1455689693" MODIFIED="1733970773841" TEXT="drei Phasen">
<node CREATED="1733970775984" ID="ID_1426251192" MODIFIED="1733970781923" TEXT="Phase-1 : Namen sammeln"/>
<node CREATED="1733970783492" ID="ID_1265095943" MODIFIED="1733970791970" TEXT="Phase-2 : Kandidaten ausscheiden"/>
<node CREATED="1733970793182" ID="ID_1827220461" MODIFIED="1733970804439" TEXT="Phase-3 : Rangordnung der verbleibenden Kandidaten"/>
</node>
<node CREATED="1733970712987" ID="ID_1572539784" LINK="https://en.cppreference.com/w/cpp/language/overload_resolution" MODIFIED="1733970751100" TEXT="Basis-Regeln"/>
<node CREATED="1733970720145" ID="ID_1764534136" LINK="https://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading" MODIFIED="1733970760198" TEXT="Regeln f&#xfc;r Funktions-Templates"/>
</node>
<node CREATED="1733970625236" ID="ID_1475799809" MODIFIED="1733970641577" TEXT="beide Kandidaten sind Funktions-Templates"/>
<node CREATED="1733970647177" ID="ID_305236337" MODIFIED="1733970684664" TEXT="Parameter &#x27fc; Tuple(Base) gilt als Conversion"/>
<node CREATED="1733970685828" ID="ID_442077561" MODIFIED="1733970703973" TEXT="deshalb sollte m.E. dieser Fall einen schlechteren Rang bekommen"/>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1733970909696" ID="ID_1168280520" MODIFIED="1733970922306" TEXT="Untersuchung">
<icon BUILTIN="forward"/>
<node CREATED="1733972793050" ID="ID_1370589924" MODIFIED="1733972813148" TEXT="im einfachen Beispiel verh&#xe4;lt sich alles wie erwartet...">
<icon BUILTIN="smily_bad"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
@ -142743,7 +142766,17 @@ std::cout &lt;&lt; tmpl.render({&quot;what&quot;, &quot;World&quot;}) &lt;&lt; s
</node>
</node>
</node>
<node CREATED="1715693174737" ID="ID_152113212" MODIFIED="1715725332461" TEXT="(custom)Allocator">
<node CREATED="1733950360795" ID="ID_28461925" MODIFIED="1733950367973" TEXT="Tupel und structured binding">
<node CREATED="1733950416539" ID="ID_1732623981" MODIFIED="1733950431054" TEXT="man kann damit ein Tupel in lokale Variable auspacken">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1733950377664" ID="ID_242038986" LINK="https://en.cppreference.com/w/cpp/utility/tuple/tuple-like" MODIFIED="1733950483299" TEXT="verwendbar f&#xfc;r alles was das &#xbb;tuple protocoll&#xab; erf&#xfc;llt"/>
<node COLOR="#435e98" CREATED="1733950233731" ID="ID_609631801" LINK="https://devblogs.microsoft.com/oldnewthing/20201015-00/?p=104369" MODIFIED="1733950498051" TEXT="How to add structured binding support to your own types">
<linktarget COLOR="#3362d9" DESTINATION="ID_609631801" ENDARROW="Default" ENDINCLINATION="-1197;56;" ID="Arrow_ID_145396423" SOURCE="ID_1814813251" STARTARROW="None" STARTINCLINATION="-581;47;"/>
<icon BUILTIN="info"/>
</node>
</node>
<node CREATED="1715693174737" FOLDED="true" ID="ID_152113212" MODIFIED="1715725332461" TEXT="(custom)Allocator">
<linktarget COLOR="#4961d5" DESTINATION="ID_152113212" ENDARROW="Default" ENDINCLINATION="-2030;323;" ID="Arrow_ID_717223261" SOURCE="ID_868305899" STARTARROW="None" STARTINCLINATION="-1527;135;"/>
<node CREATED="1715693298760" ID="ID_1472333314" MODIFIED="1715693300284" TEXT="Quellen">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1715696253175" ID="ID_267534641" MODIFIED="1715696324289" TEXT="CPP-Referenz">