C++ - "Curiously Recurring Template Pattern"

Algorithmen, Sprachunabhängige Diskussionen zu Konzepten, Programmiersprachen-Design
Antworten
Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

C++ - "Curiously Recurring Template Pattern"

Beitrag von fat-lobyte » Mo Mär 26, 2012 10:24 am

Tag.

Also hier gehts um das sogenannte Curiously Recurring Template Pattern (CRTP), bei dem eine Basisklasse ein Template ist, das vom Typ der abgeleiteten Klasse ist, also auf folgende Weise:

Code: Alles auswählen

template <typename Derived> struct Base {
// ...
};

struct Derived : public Base<Derived> {
// ...
};
So weit, so gut. An mehreren Stellen wird darauf verwiesen, dass man damit Virtuelle Funktionen simulieren kann. Der Code auf Wikipedia sieht so aus:

Code: Alles auswählen

template <class Derived> 
struct Base
{
    void interface()
    {
        // ...
        static_cast<Derived*>(this)->implementation();
        // ...
    }
 
    static void static_func()
    {
        // ...
        Derived::static_sub_func();
        // ...
    }
};
 
struct Derived : Base<Derived>
{
    void implementation();
    static void static_sub_func();
};
Bei genauerer Überlegung frage ich mich aber eigentlich: was genau wird hier simuliert?? Was ist da noch Virtuell? Wenn einen Basiszeiger haben will, muss der Abgeleitete Typ immer mitgegeben werden:

Code: Alles auswählen

Base<Derived>* b_ptr = new Derived();
ok, ich kann mit dem "Basiszeiger" zwar die Abgeleiteten Funktionen aufrufen - aber überall wo ich diesen Zeiger verwende muss ich die Abgeleitete Klasse als Templateargument übergeben. Da könnte ich ja gleich statt dem Basiszeiger die abgeleitete Version verwenden, oder seh ich das falsch? Polymorphismus ist das nicht (auch kein "simulierter").

Sieht jemand einen Vorteil? Oder bin ich hier einem Anti-Pattern aufgesessen?
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: C++ - "Curiously Recurring Template Pattern"

Beitrag von Xin » Mo Mär 26, 2012 10:53 am

fat-lobyte hat geschrieben:Tag.

Also hier gehts um das sogenannte Curiously Recurring Template Pattern (CRTP), bei dem eine Basisklasse ein Template ist, das vom Typ der abgeleiteten Klasse ist, also auf folgende Weise:

Code: Alles auswählen

template <typename Derived> struct Base ...
struct Derived : public Base<Derived>
So weit, so gut. An mehreren Stellen wird darauf verwiesen, dass man damit Virtuelle Funktionen simulieren kann. Der Code auf Wikipedia sieht so aus:

Code: Alles auswählen

template <class Derived> 
struct Base
{
    void interface()
    {
        // ...
        static_cast<Derived*>(this)->implementation();
        // ...
    }
};
Bei genauerer Überlegung frage ich mich aber eigentlich: was genau wird hier simuliert?? Was ist da noch Virtuell? Wenn einen Basiszeiger haben will, muss der Abgeleitete Typ immer mitgegeben werden:

Sieht jemand einen Vorteil? Oder bin ich hier einem Anti-Pattern aufgesessen?
Ich glaube, dass die Beschreibung hier falsche Erwartungen weckt.

Du kannst aus einem Algorithmus heraus, der in der Base<T> definiert wird - und nirgendwo sonst! - , Funktionen rufen, die in T definiert werden. Da ist nichts "virtuelles". Das geht nur deswegen, weil Du weißt, wer von Base ableitet (nämlich T).
Wenn das wie von Dir beschrieben mit interface() gekapselt ist, ist das auch nett - so habe ich noch nie gemacht und es ehrlich gesagt auch nicht vor.

Aber:
Ich übergebe den abgeleiteten Typ sehr häufig an das Basis-Template. So sind bei mir die String-Funktionalitäten größtenteils in der Basisklasse "Array" untergebracht. Ich kann ein Array also genauso z.B. splitten oder zusammenführen. Die Funktionen des Arrays erzeugen dann ein neues Array und kopieren dann den gewünschten Teil da wieder rein. Hierfür rufe ich den Konstruktor des abgeleiteten Type, z.B. des Strings und gebe das zurück. Der Konstruktor ist letztendlich auch nur eine Methode des abgeleiteten Typs.
Jetzt weiß ich auch, dass es für mein Handeln einen Design-Pattern-Namen gibt - ist ja auch was feines. Hab ich bis heute abend wohl auch wieder vergessen. ;-D

Obwohl die Funktionalität in Array<> definiert ist, kommt ein vollständiger String zurück, weil ich quasi "virtuell" den richtigen Konstruktor gerufen habe. Aber ich habe auf dieser Tastatur gar nicht genug Anführungszeichen, um "quasi" und "virtuell" ausreichend mit Warnsymbolen, dass man das nicht wörtlich nehmen soll, auszustatten.

=> Sehr nützliches Pattern!
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: C++ - "Curiously Recurring Template Pattern"

Beitrag von fat-lobyte » Mo Mär 26, 2012 7:54 pm

Ok, also ich rekapituliere:
Das ganze bringt nur etwas, wenn ich in der Basisklasse Funktionalität implementiere, und dabei von dem Typ der Abgeleiteten Klasse abhängige methoden aufrufe. Richtig?

Funktioniert also sowieso alles nur statisch.

In meinem Programm habe ich in der Basisklasse keine Funktionalität, sondern wollte nur das interface definieren. Das funktioniert aber nicht, weil das "Interface" trotzdem Typabhängig ist und somit eigentlich nicht mehr das "interface" sondern die Implementierung ist. :| :?:

Oder so ^^

Naja, sieht so aus als würde das mein Problem nicht lösen.

Es ist so: mir fehlen in C++ zurzeit tierisch die "Concepts", also die Fähigkeit Ansprüche an einen Typ zu stellen, der als Template-Parameter übergeben wird.

Beispiel:
Eine Katze ist von einem Tier abgeleitet, und ein Hund ebenfalls. Allokiere ich ein Katzenobjekt, kann ich über virtuelle Tiermethoden darauf zugreifen, und dieses Objekt an alle Funktionen übergeben, die Tiere erwarten.
Eine Petunie ist kein Tier, also kann ich auch keinen Petunienzeiger an eine Tierfunktion übergeben - das "Tier" ist das Interface gegen das ich prüfe. Wenns nicht passt gibts einen Compilerfehler.

So, nun ist es bei mir so, dass ich schon zur kompilierzeit weiß, dass das Tier ne Katze ist. Ich werde mir also den virtuellen Funktionsaufruf sparen, und die Tierfunktionen als Templates implementieren, die vom Tiertyp abhängen. Gebe ich den Typ Katze in die Tierfunktion hinein, ist alles grün. Und JETZT kommt mein großes Problem, wieso ich den Zirkus veranstalten wollte:
Wenn ich ne Petunie in eine Tierfunktion übergebe, dann funktioniert das!!! Zumindest so lange, bis die Tierfunktion versucht den Blutdruck zu messen und feststellen muss, dass eine Petunie keine get()-Methode für den Blutdruck hat - und das ist viel zu spät.

Dieses Problem wäre mit Concepts gelöst worden, wurde aber (wegen grundlegender Überlegungen) vertagt. Das CRTP ding war mein kleiner Versuch etwas ähnliches nachzubauen, hat aber nicht funktioniert.
Hat jemand ne Idee, wie ich verhindere, dass sich Petunien als Tiere ausgeben?

Es geht mir übrigens nicht nur um Compilerfehler. Ich hätte gerne als "dokumentation" oder "garantie", dass ein Typ einem gewissen Konzept entspricht.
Das kann man zurzeit im Stil der STL in der Dokumentation machen, ist aber wenig befriedigend :-(
Haters gonna hate, potatoes gonna potate.

Benutzeravatar
Kerli
Beiträge: 1456
Registriert: So Jul 06, 2008 10:17 am
Wohnort: Österreich
Kontaktdaten:

Re: C++ - "Curiously Recurring Template Pattern"

Beitrag von Kerli » Mo Mär 26, 2012 9:00 pm

Concepts kann man mit enable_if/SFINAE recht leicht nachstellen. zb (ungetestet):

Code: Alles auswählen

template<class T>
typename std::enable_if<std::is_base_of<Animal,T>, bool>::type
isAnimalAlive(const T& animal)
{
  return animal.getBloodPressure() > 0.01;
}
Für alle von Animal abgeleiteten Klassen wird dieses Template instanziiert. Wenn man versucht die Funktion mit einem nicht kompatiblen Typ aufzurufen, dann existiert diese für den Compiler nicht.
"Make it idiot-proof and someone will invent an even better idiot." (programmers wisdom)

OpenGL Tutorials und vieles mehr rund ums Programmieren: http://www.tomprogs.at

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: C++ - "Curiously Recurring Template Pattern"

Beitrag von Xin » Mo Mär 26, 2012 9:08 pm

Habe auch keinen bessere Idee.
Das Problem habe ich selbst und in C++ bisher keine Lösung dafür gefunden, außer Disziplin zu bewahren.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
fat-lobyte
Beiträge: 1398
Registriert: Sa Jul 05, 2008 12:23 pm
Wohnort: ::1
Kontaktdaten:

Re: C++ - "Curiously Recurring Template Pattern"

Beitrag von fat-lobyte » Di Mär 27, 2012 11:42 am

Kerli hat geschrieben:Concepts kann man mit enable_if/SFINAE recht leicht nachstellen.
Stimmt eigentlich, nur die Syntax ist nicht gerade die Hübscheste. Und die Fehlermeldung "Methode nicht gefunden" von GCC ist dann auch wenig hilfreich. (Clang listet noch auf, warum die vorhandenen Kandidaten nicht passen).

Naja, im Notfall kann mans schon verwenden. Eine Repräsentierung in der Sprache wäre aber schon schön.
Haters gonna hate, potatoes gonna potate.

Antworten