Ich habe hier eine Software die ich ein bisschen "verbessern" darf -
alles ist dynamic_cast basiert (nicht von mir) und ich will
häppchenweise die Software vereinfachen um später das ganze komplett zu
ersetzen - jetzt gerade geht es mir aber nur darum mal ohne große
Umbauten den Code aufräumen zu können - um weniger Fehler zu machen
also habe ich mir ein Helper Template geschrieben das prüft ob
ein ein Base-Class Pointer auf einen gegeben Typ gecastet werden kann
und falls ja eine entsprechende Empfänger-Methode aufruft - klappt auch
alles so wie es soll.
Es gibt nur eine Unschönheit das ich in dem eigentlich generischem
Helper
relativ früh den Target-Scope-Type in diesem Beispiel Test_t angeben
muss obwohl ich denke das ich das irgendwie auch automatisch aus dem
this Pointer den ich eh mitgeben dedukten könnte, komme aber nicht drauf
wie ich das lösen kann - ich will aber vermeiden einen weiteren
Template-Parameter zu haben - falls das möglich ist
Hat jemand eine Idee
Dein eigentliches Problem scheint ja zu sein, Member-Funktionen per
std::function aufzurufen ohne den this-Pointer explizit mitgeben zu
müssen. Das geht z.B. per std::bind:
Alternativ kannst du auch direkt mit Member-Funktion-Pointern arbeiten,
das hat aber den Nachteil dass TypeBasedDispatch dann nur noch mit
Member-Funktionen funktioniert:
Ein netter Nebeneffekt ist dass dank Inferierung der template-Parameter
das TypeA_t gar nicht mehr übergeben werden muss.
Schau dir auch mal std::variant und std::visit an, das ist so ähnlich...
Wilhelm M. schrieb:> Vielleicht so?
Ich habe es so verstanden, dass OnType immer mit einem BaseType_t*
aufgerufen wird, und man eine Laufzeit-Unterscheidung braucht - daher
das dynamic_cast. Sonst könnte man ja einfach mehrere Overloads benutzen
- OnType (const TypeA_t*), OnType(const TypeB_t*), ... - ist auch etwas
übersichtlicher als per if-constexpr und is_same ...
Niklas G. schrieb:> Wilhelm M. schrieb:>> Vielleicht so?>> Ich habe es so verstanden, dass OnType immer mit einem BaseType_t*> aufgerufen wird, und man eine Laufzeit-Unterscheidung braucht - daher> das dynamic_cast. Sonst könnte man ja einfach mehrere Overloads benutzen> - OnType (const TypeA_t*), OnType(const TypeB_t*), ... - ist auch etwas> übersichtlicher als per if-constexpr und is_same ...
Ich habe gar nicht verstanden, was er will ...
Natürlich kann man ein overload-set benutzen, oder auch double-dispatch.
Allerdings dachte ich mir, das er mindestens ggf. ein type-mapping per
meta-function dort einbauen will als customizatoin-point.
Ich glaube er will einfach nur per dynamic_cast den Laufzeit-Typ eines
Objekts unterscheiden und dann dementsprechend unterschiedliche
Funktionen aufrufen. Eigentlich nur Single-Dispatch, aber nicht über
virtuelle Funktionen des Objekts selbst. Er will das dynamic_cast aber
kapseln (in OnType) und das Problem besteht lediglich darin, die
verschiedenen Funktionen aufzurufen. Das eigentliche Problem hat also
nichts mit Typ-Unterscheidung oder Dispatch zu tun, sondern bloß damit
wie man Member-Funktionen indirekt (std::function oder Member Function
Pointer) aufruft.
>Ich glaube er will einfach nur per dynamic_cast den Laufzeit-Typ eines>Objekts unterscheiden
die jetzige Software macht es so - ich will nicht unbedingt
>Das eigentliche Problem hat also>nichts mit Typ-Unterscheidung oder Dispatch zu tun, sondern bloß damit>wie man Member-Funktionen indirekt (std::function oder Member Function>Pointer) aufruft.
Ja genau - die Software an der ich arbeite ist riesig (>1Mio Zeile Code)
und ich möchte in vielen kleinen (Testsicheren) Stufen das jetziges
Konzept (dynamic_cast) immer mehr in eine Richtung bringen das ich es
schlussendlich einfacher ersetzen kann ohne zu viel kaputt zu machen -
diese Helper-Templates sind nur Refactoring-Begünstiger die eine Weile
lang im Code verbleiben dürfen
in sowas umwandeln - könnte das mit Variadic Templates gehen?
Dispatcher<
// die runtime Anteile wie der base_type/this
// muessen dann von aussen kommen
TypeBasedDispatch(&Test_t::OnTypeA),
TypeBasedDispatch(&Test_t::OnTypeB)
> dispatcher;
void OnType (BaseType_t * base_type)
{
dispatcher.dispatch(base_type, this);
}
es muss denk eich per Variadic Template sein weil die TypeBasedDispatch
einzeln spezalisiert sind - eine Idee wie man das erreichen könnte?
Info: der base_type Pointer kann nicht zur Kompilezeit ausgewertet
werden - kommt ganz wo anders her (und ich kann das Prinzip auch nicht
total verändern - hängt viel zu viel Code drann u.a. nicht von mir ist)
Man hat einfach mehrere Überladungen von OnType für die verschiedenen
Typen. TypeBasedDispatch ruft die passende Überladung auf, je nachdem
was es ist. Die letzte Überladung ist für BaseType_t * und ruft
TypeBasedDispatch auf. Hat den kleinen netten Vorteil, dass man von
außen einfach nur OnType(obj) aufrufen muss, und es wird immer direkt
das richtige aufgerufen - wenn obj schon zur Kompile-Zeit ein TypeA_t
ist, wird direkt der richtige Overload aufgerufen, ohne den Overhead per
dynamic_cast.
Ein weitere Vorteil an den Overloads ist, dass man auch templates nehmen
kann:
Perfekt läuft sofort
Wie kann man jetzt noch direkt beim 1. Treffer returnen (also nicht
weiter die OnTypes aufrufen) und wenn gar kein cast funktioniert hat ein
assert(false) verursachen?
Das ist beides in C++11 etwas lästig. Die gezeigte C++17-Version erfüllt
den ersten Wunsch bereits (Short-Circuit Evaluation in der
fold-expression), der zweite lässt sich leicht ergänzen:
Frage 1: Ich bräuchte beides für C++11 - was von deinem Beispiel kann
ich dann so einsetzen?
Frage 2: gibt es einen Möglichkeit die OnType-Routine auch per
Template-Parameter aus zu tauschen
so in der Art:
CppBert1 schrieb:> Frage 1: Ich bräuchte beides für C++11 - was von deinem Beispiel kann> ich dann so einsetzen?
Welcher aktuelle Compiler kann denn nur C++11, aber nichtmal C++14? Wenn
es schon vorgedrungen ist dass die C++-Entwicklung wieder weiter geht,
kann man auch am Ball bleiben und nicht C++11 das neue C++98 werden
lassen... Aber okay, so geht es in C++11:
Durch die Auslagerung ist man nebenbei nicht mehr auf Test_t festgelegt.
CppBert1 schrieb:> Frage 2: gibt es einen Möglichkeit die OnType-Routine auch per> Template-Parameter aus zu tauschen
Genau das geht bei den Overloads nicht, weil man ja alle Overloads
übergeben müsste - nicht sehr schön. Man kann aber wie gesagt einfach
die Test_t -Klasse austauschen und eben für jedes Verhalten eine andere
Klasse nehmen.
Danke wieder mal - jetzt muss ich mich erst mal da durch schlumpfen
>Welcher aktuelle Compiler kann denn nur C++11
Wer sagt denn das ich einen aktuellen Kompiler verwenden darf :)
bei mir ist es der VS2015 - und ein Wechsel verursacht gerade zu viel
Testaufwand - angedacht ist es aber - genau wegen solchen Problemen
>Genau das geht bei den Overloads nicht, weil man ja alle Overloads>übergeben müsste - nicht sehr schön. Man kann aber wie gesagt einfach>die Test_t -Klasse austauschen und eben für jedes Verhalten eine andere>Klasse nehmen.
Ich habe jetzt 2-3 Varianten von deinem Dispatcher für unterschiedliche
Basis-Types aber werden in einer Klasse verarbeitet
also so:
BaseType1_t
Type_1_A
Type_1_B
Type_1_C
die sollen an OnType1-overloads dispatcht werden
BaseType2_t
Type_2_A
Type_2_B
Type_2_C
die sollen an OnType2-overloads dispatcht werden
also nicht für jeden Typ eingenes Ziel sondern für jede
typeBasedDispatch-Instanzen ein Ziel
void OnType1 (BaseType1_t * base_type)
{
typeBasedDispatch <Type_1_A_t, Type_1_B_t> (*this, base_type);
// geht an OnType1 overloads
}
void OnType2 (BaseType2_t * base_type)
{
typeBasedDispatch <Type_2_A_t, Type_2_B_t> (*this, base_type);
// geht an OnType2 overloads
}
da werden unterschiedliche Sachen Dispatcht aber das Prinzip ist das
gleiche
Sorry ich bin total blind - den
template <typename Base, typename Obj, typename... Types>
struct TypeBasedDispatch;
fehlt ja nur noch ein typename Function dann sollte das gehen was ich
will
CppBert1 schrieb:> fehlt ja nur noch ein typename Function dann sollte das gehen was ich> will
Typename ist für Typen... Man kann zwar (Member-)Funktionspointer
übergeben, aber diese beziehen sich immer auf eine konkrete Funktion,
also einen spezifischen Overload. Du kannst aber einfach alles in 1
Klasse packen:
Hab es jetzt doch geschafft verschiedene Ziel-Methoden rein zu bringen
ich habe einen weiteren Template-Parameter CallType eingefügt mit dem
ich an der ehemaligen OnType()-Call Stelle ein schon partiell
spezialisiertes Template OverloadCaller<CallType> mit Obj und T1
ausspezialisiere - damit klappt es (aber jetzt habe ich das Problem das
bei der eine Variante noch ein Parameter mehr durchgeschleift werden
muss...)
Was ist eigentlich der Grund dafür das es in C++ Methoden-Alias oder
sowas gibt (um solche Overloads zu erreichen) - gibt das einfach nicht
oder geht es aus einem bestimmten Grund nicht?
CppBert schrieb:> gibt das einfach nicht> oder geht es aus einem bestimmten Grund nicht?
Passt halt wenig zur Sprache. Das wäre ja ein Alias für mehrere
Funktionen gleichen Namens; also weder ein Funktionszeiger (der zeigt
immer auf eine bestimmte Funktion) noch ein Typ-Alias, sondern ein
komplett neues Sprachelement. Das wird vermutlich einfach zu wenig
nachgefragt, als dass es sinnvoll wäre, die Sprache derart umzukrempeln.
Kennst du eine Sprache, die das kann?
In C++11 kann man alternativ ja auch einfach eine Klasse definieren,
welche mehrere Overloads für oder einen templatisierten "operator ()"
hat - diese Klasse entspricht dann einer "Operation" (=mehrere
Funktionen), und dafür gibt es Typ-Aliase. Wenn ich dich richtig
verstehe hast du ja auch genau das gemacht...
In C++14 kann man stattdessen auch template-Lambdas benutzen - das
Lambda ist dann ein template und quasi ein alias für mehrere Funktionen:
Man beachte dass innerhalb von "TypeBasedDispatch" weder Test_t noch
OnType auftauchen, und man das damit für andere Klassen und Funktionen
wiederverwenden kann. Man könnte das auch für mehrere Lambdas ausbauen -
man übergibt ein Lambda pro TypeX_t, und es wird jeweils das eine
passende aufgerufen, somit gäbe es keine einzelnen OnType-Funktionen
mehr. Man muss sich bloß was clevers überlegen, wie man gleichzeitig ein
Parameter-Pack für die abgeleiteten Klassen und eines für die Lambdas
übergibt...
Niklas G. schrieb:> Man muss sich bloß was clevers überlegen, wie man gleichzeitig ein> Parameter-Pack für die abgeleiteten Klassen und eines für die Lambdas> übergibt...
Mache aus einem nackten Parameter-Pack der abgel. Klassen eine Typ-Liste
(Typ-Container), dann ist der einzige Parameter-Pack der für die Lambdas
...
Wilhelm M. schrieb:> Mache aus einem nackten Parameter-Pack der abgel. Klassen eine Typ-Liste> (Typ-Container)
Die obligatorische Util::TypeList<...> Klasse die jedes fortgeschrittene
C++- Projekt braucht :-) ... Leider sieht der Aufruf dann relativ
hässlich aus. Ohne C++17 und Fold Expressions würde die Implementation
dank doppelter Rekursion auch unschön.
Das Lambda-Template Beispiel von dir wäre perfekt - und glücklicherweise
darf ich doch C++14 verwenden - aber dein Beispiel kompiliert erst mit
C++17 :(
wenn den den Online Kompiler auf C++14 stelle geht es nicht mehr,
meintest du C++17?
https://onlinegdb.com/Bk536q607
CppBert1 schrieb:> könnt ihr ein> Buch/Online-Tutorial zu Variadic Templates (C++11..C++17) empfehlen?
Weiß grad keins.
CppBert1 schrieb:> Das Lambda-Template Beispiel von dir wäre perfekt - und glücklicherweise> darf ich doch C++14 verwenden - aber dein Beispiel kompiliert erst mit> C++17 :(
Das von heut morgen? Kompiliert bei mit sowohl mit GCC als auch Clang
wenn auf C++14 gestellt. Auf Godbolt's Compiler Explorer funktioniert es
erst ab GCC 7.1, ältere Versionen haben da vermutlich einen Bug; ab
Clang 3.5 (von 2014) geht's:
https://godbolt.org/z/sCZY8n
Anscheinend erkennt der alte GCC nicht, dass er "this" capturen muss.
Mit einem kleinen Workaround kann man das explizit machen, dann
funktioniert es ab GCC 4.9.0:
Bei solchen Fragen kann ich Dir eigentlich nur raten, diese auf
stackoverflow zu stellen.
Dieses Forum hat ja den Titel Mikrocontroller.net, es sind hier
demzufolge viele Leute unterwegs, die (unverständlicherweise) mit C++
nichts am Hut haben und schon gar nicht mit generischem Code, und eher
weniger die kompetenten C++-Experten.
Niklas G. ist sicher eine große Ausnahme, er hat sich viel mehr Mühe
gegeben, Dein Problem zu verstehen als ich, und er hat Dir sehr gut
weitergeholfen. Das ist auf diesem Niveau hier aber sehr selten ...
>Bei solchen Fragen kann ich Dir eigentlich nur raten, diese auf>stackoverflow zu stellen.
oder reddit/cpp_question, cppforum, ... ich bin auf ein paar Platformen
Unterwegs - wollte man ausprobieren wie die Mikrocontroller.net
Community reagiert :)
>Dieses Forum hat ja den Titel Mikrocontroller.net, es sind hier>demzufolge viele Leute unterwegs, die (unverständlicherweise) mit C++>nichts am Hut haben und schon gar nicht mit generischem Code, und eher>weniger die kompetenten C++-Experten.
Das ist hier gar nicht so das Problem - nach meiner Erfahrung - eher das
sich hier viel zu viele "Profis" mit Halbwissen dauerhaft Unterhaltungen
einnehmen - einer schreibt eine etwas schlecht formulierte Frage und 10
Mann schlagen sich dann darum wer die blöderen 08/15 Kommentare abgibt -
meist in vollkommener Abwesenheit der Threadstarters...
>Niklas G. ist sicher eine große Ausnahme, er hat sich viel mehr Mühe>gegeben, Dein Problem zu verstehen als ich, und er hat Dir sehr gut>weitergeholfen. Das ist auf diesem Niveau hier aber sehr selten ...
wenn man sich keine Mühe gibt das Problem zu verstehen sollte man nicht
Antworten - wenn man nichts sinnvolles Beitragen kann sollte man nicht
Antworten - das sind Regeln die leider zu viele im Internet nicht
verstehen
Danke noch mal an Niklas G. der mit seiner Erfahrung recht schnell mein
Problem verstanden hat und auch lösen konnte - Danke auch den anderen
Beteiligten welche die Posts hier warm gehalten haben :)
CppBert schrieb:> oder reddit/cpp_question, cppforum, ... ich bin auf ein paar Platformen> Unterwegs - wollte man ausprobieren wie die Mikrocontroller.net> Community reagiert :)
Anders formuliert, du wolltest mal ein bisschen mit erweiterten
C++-Kenntnissen angeben.
Damit exponierst du dich auf eine Stufe wie jemand der im
Modelleisenbahnforum seine Landschaftsgärtnerkenntnisse zur Schau
stellen will.
Abradolf L. schrieb:> Anders formuliert, du wolltest mal ein bisschen mit erweiterten> C++-Kenntnissen angeben.> Damit exponierst du dich auf eine Stufe wie jemand der im> Modelleisenbahnforum seine Landschaftsgärtnerkenntnisse zur Schau> stellen will.
Ja richtig - genau so wie das kleine Kinder eben machen :)
Falls es dir aufgefallen ist: mein Anfangs Post hier ist 08/15 C++ - die
"erweiterten C++-Kenntnissen" kamen von "Niklas G."
und @Wilhelm M.
Es gibt hier recht viele gute, und auch schnell reagierende C++
Entwickler - es gibt also keine Notwendigkeit die C++ "Hilfe-Performanz"
in dieser Community klein zu reden :)