2009-05-23 8 views
10

Ich habe über Typlisten in 'Modern C++ Design' gelesen und ich habe es als eine Art von Union für Typen verstanden. Wenn Sie verschiedene, nicht verwandte Typen in eine Typenliste einfügen, können Sie damit mehr als einen Typ auf einmal ohne Vererbung darstellen. Ich habe die Typenliste in einigen einfachen Funktionen mit primitiven Typen getestet, aber ich konnte keine von ihnen zum Arbeiten bekommen.Wie man Typlisten verwendet

Kann mir jemand sagen, ob mein Verständnis von Typlisten richtig ist und geben Sie ein einfaches Beispiel aus der realen Welt, wie man Typelisten im täglichen Durchschnittscode verwendet? Danke im Voraus.

Btw, ich benutze Windows und Visual Studio 2005 und seinen Compiler.

EDIT: Meine Beispiele sind weg, ich benutze ein Sandbox-Projekt in vs, um diese Dinge zu testen. Aber es war ruhig ähnlich in Dobbs Tutorial-Code:

void SomeOperation(DocumentItem* p) 
{ 
    if (TextArea* pTextArea = dynamic_cast<TextArea*>(p)) 
    { 
     ... operate on a TextArea object ... 
    } 
    else if (VectorGraphics* pVectorGraphics = 
     dynamic_cast<VectorGraphics*>(p)) 
    { 
     ... operate on a VectorGraphics object ... 
    } 
    else if (Bitmap* pBitmap = dynamic_cast<Bitmap*>(p)) 
    { 
     ... operate on a Bitmap object ... 
    } 
    else 
    { 
     throw "Unknown type passed"; 
    } 
} 

Das funktioniert aber ich habe nicht den Vorteil der Vererbung sehen, die das gleiche tun kann. Und dynamischer Cast funktioniert nicht für primitive Typen. Ist es möglich, sie als Rückgabewert zu verwenden wie:

typedef Typelist<int, string> mylist 
mylist myfunction() { 
    if(foo == bar) 
     return 5; 

    return "five"; 
} 
+0

Fügen Sie ein Beispiel für den Code hinzu, der für Ihre Frage nicht funktioniert hat. –

Antwort

18

Die Typlisten sind generische Kompilierzeit-Sammlungen von Typen. Wenn Sie dynamic_cast verwenden, fehlt der Punkt, da er nicht benötigt wird, da es sich um ein statisches Kompilierzeitkonzept handelt.

Dies funktioniert, aber ich sehe nicht den Vorteil gegenüber der Vererbung, die in der Lage ist, das Gleiche zu tun.

Sie können keinen vorhandenen Typ von irgend etwas erben lassen. Dies ist einfach nicht durchführbar, da dieser vorhandene Typ ein eingebauter Typ oder ein Typ aus einer Bibliothek sein kann. Denken Sie an die Typlisten als Erweiterungen von Listen von Typen (z. B. in std :: pair) für jede vernünftige Anzahl von Typen (statt nur 2).

Die Typlisten können zum Erstellen einer Funktion verwendet werden, um eine Reihe von Argumenten an eine Funktion zu übergeben.Dies ist ein Codeabschnitt, der verallgemeinerte Funktoren von 5 Parametern (ein anderes Konzept aus dem modernen C++ - Design) mit den Argumenten in einem Tupel (noch einem anderen) mit der Typliste aufruft, die im Tupel enthaltene Objekttypen definiert:

//functor is just a holder of a pointer to method and a pointer to object to call this 
//method on; (in case you are unfamiliar with a concept) 
template<class R, class t0, class t1, class t2, class t3, class t4> 
R call(Loki::Functor<R,LOKI_TYPELIST_5(t0, t1, t2, t3, t4 
    )> func, 
    Loki::Tuple<LOKI_TYPELIST_5(t0, t1, t2, t3, t4)> tuple) 
{ 
    ///note how you access fields 
    return func(Loki::Field<0>(tuple), Loki::Field<1>(tuple), 
     Loki::Field<2>(tuple), Loki::Field<3>(tuple), 
     Loki::Field<4>(tuple)); 
} 

//this uses the example code 
#include<iostream> 
using namespace std; 

int foo(ostream* c,int h,float z, string s,int g) 
{ 
    (*c)<<h<<z<<s<<g<<endl; 
    return h+1 
} 

int main(int argc,char**argv) 
{ 
    Loki::Functor<int,LOKI_TYPELIST_5(ostream*, int, float, string, int)> f=foo; 
    //(...) 
    //pass functor f around 
    //(...) 
    //create a set of arguments 
    Loki::Tuple<LOKI_TYPELIST_5(ostream*, int, float, string, int)> tu; 
    Field<0>(tu)=&cout; 
    Field<1>(tu)=5; 
    Field<2>(tu)=0.9; 
    Field<3>(tu)=string("blahblah"); 
    Field<4>(tu)=77; 
    //(...) 
    //pass tuple tu around, possibly save it in a data structure or make many 
    //specialized copies of it, or just create a memento of a call, such that 
    //you can make "undo" in your application; note that without the typelist 
    //you would need to create a struct type to store any set of arguments; 
    //(...) 
    //call functor f with the tuple tu 
    call(f,tu); 
} 

Beachten Sie, dass nur mit anderen Konzepten wie Tupel oder Funktoren die Typlisten nützlich werden. Außerdem habe ich Loki seit ungefähr 2 Jahren in einem Projekt erlebt und wegen des Template-Codes (eine Menge davon) sind die ausführbaren Dateien in DEBUG-Versionen meistens GROSS (mein Rekord war 35 MB oder so). Auch die Geschwindigkeit der Kompilation wurde etwas beeinflusst. Erinnern Sie sich auch, dass C++ 0x wahrscheinlich einen äquivalenten Mechanismus enthält. Fazit: Versuchen Sie, keine Typlisten zu verwenden, wenn Sie nicht müssen.

+0

Vielen Dank, du hast einiges für mich erledigt. Ich denke, ich sollte das Kapitel noch einmal lesen. – DaClown

5

Does this Artikel von Dr. Dobbs Journal Hilfe jeder?

4

Typlisten sind eine Möglichkeit, "Listen mit Parametern" an Vorlagen-Meta-Programme zu übergeben, die als Teil des Kompilierungsprozesses "ausgeführt" werden.

Als solche können sie verwendet werden, um eine Art von "Union" Typ zu generieren, aber das ist nur eine mögliche Verwendung.

Für ein "reales" Beispiel: Wir haben Typlisten verwendet, um die Methode "QueryInterface" automatisch zu generieren, wenn COM-Objekte in der Bibliothek Comet implementiert werden.

Es Sie Code wie diesen zu schreiben erlaubt:

class Dog : public implement_qi<make_list<IAnimal, INoisy, IPersistStream> > 
{ 
    // The implement_qi template has provided 
    // an implementation of COM's QueryInterface method for us without 
    // having to write an ugly ATL "message map" or use any Macros. 
    ... 
} 

In diesem Beispiel „make_list“ war eine Vorlage verwendet, um eine „Typenliste“ zu erzeugen, die die implement_qi Vorlage könnte dann „aufzählen über“ zu generate the appropriate QueryInterface code.