2016-07-01 11 views
0

Ich denke, dass diese Aufgabe sehr häufig ist, aber ich kann keine richtige Lösung für sie finden.Wie man eine Fabrik für Templates richtig implementiert

Ich habe eine Hierarchie von „Produkten“, die einige „Traits“ hat, so habe ich beschlossen, auf Vorlagen basierende Schnittstelle für Produkte zu verwenden, in dem der Template-Parameter ist der „Zug“:

Diese sind Merkmale:

struct Foo { 
    static std::string get_name() { return "Foo"; } 

    Foo(int a) : a_(a) {} 
    int operator()() const { return a_; } 

private: 
    int a_; 
}; 

struct Bar { 
    static std::string get_name() { return "Bar"; } 

    Bar(int a, int b) : a_(a), b_(b) {} 
    int operator()() const { return a_ + b_; } 

private: 
    int a_; 
    int b_; 
}; 

struct Spam { 
    Spam(int a, int b) : a_(a), b_(b), c_(0) {} 
    void operator()() { c_++; } 

private: 
    int a_; 
    int b_; 
    int c_; 
}; 

und diese sind Produkte Hierarchie:

template <class T> 
class Product { 

public: 
    typedef T T_type; 

    virtual T get() = 0; 

    virtual ~Product() {} 
}; 

template <class T> 
class ProductA : public Product<T> { 

    typedef Product<T> base_type; 

public: 
    ProductA(int a) : a_(a) {} 

    virtual ~ProductA() {} 

    virtual T get() { 
     return T(a_); 
    } 

private: 
    int a_; 
}; 

template <class T, class U> 
class ProductB : public Product<T> { 

    typedef Product<T>         base_type; 

public: 
    typedef U           U_type; 

    ProductB(int a, int b) : a_(a), b_(b) {} 

    virtual ~ProductB(); 

    virtual T get() { 
     init(); // U is being used here 
     return T(a_, b_); 
    } 

protected: 
    void init() {} 

private: 
    int a_; 
    int b_; 
}; 

ich brauche zusätzliche Ebene der Vererbung zu verwenden, da verschiedene Schnittstellen von ProductA und ProductB - sie haben unterschiedliche C-Tors.

Und hier sind konkrete Produkte:

class ProductA1 : public ProductA<Foo> { 

    typedef ProductA<Foo> base_type; 

public: 
    ProductA1(int a) : base_type(a) { std::cout << "A1 created" << std::endl; } 

    virtual ~ProductA1() { std::cout << "A1 deleted" << std::endl; } 
}; 

class ProductB1 : public ProductB<Bar, Spam> { 

    typedef ProductB<Bar, Spam> base_type; 

public: 
    ProductB1(int a, int b) : base_type(a, b) { std::cout << "B1 created" << std::endl; } 

    virtual ~ProductB1() { std::cout << "B1 deleted" << std::endl; } 
}; 

Jetzt habe ich einen Mechanismus der „unified“ Schaffung von Produkten haben will (in diesem Beispiel zwei Typen ProductA1 und ProductB1) irgendwie mit der Zeichenfolge in eine Methode übergeben . Offensichtlich muss ich die Fabrik ...

So implementiert I Fabriken für verschiedene Zweige der Hierarchie (auf Objekte der Typen erstellen ProductA und ProductB), so dass ich Objekte vorbei ihre Typen durch den Template-Parameter erstellen konnte:

template <class P> 
struct ProductAFactory { 

    typedef typename P::T_type  T_type; 
    typedef ProductA<T_type>  product_type; 

    static 
    product_type* create(int a) { 
     return new P(a); 
    } 
}; 


template <class P> 
struct ProductBFactory { 

    typedef typename P::T_type  T_type; 
    typedef typename P::U_type  U_type; 
    typedef ProductB<T_type, 
        U_type>  product_type; 

    static 
    product_type* create(int a, int b) { 
     return new P(a, b); 
    } 
}; 

diese Fabriken zu haben ich habe eine Fabrik soll haben Produkte der notwendigen Art konstruieren und einen Zeiger auf Produkt der Product<T> Schnittstelle zurück:

template <class T> 
class ProductFactory { 

public: 

    static 
    Product<T>* 
    create(const std::string& product_name, 
      const int a, 
      const int b) { 

     const std::string product_a1 = "A1"; 
     const std::string product_b1 = "B1"; 

     if (product_name == product_a1) 
      return ProductAFactory<ProductA1>::create(a); 
     else if (product_name == product_b1) 
      return ProductBFactory<ProductB1>::create(a, b); // (*) <--- compiler error 
     else 
      throw std::runtime_error("Unsupported product: " + product_name); 
    } 
}; 
Alle 0 diese Codes sollen in einer solchen Art und Weise verwendet werden:

void main() { 

    typedef Foo T; 

    std::shared_ptr<Product<T>> p(ProductFactory<T>::create("A1", 1, 1)); 
    T t = p->get(); 

    std::cout << t.get_name() << ": " << t() << std::endl; 
} 

Und hier stand ich mit einem Problem, diesen Code kompilieren - der Fehler ist return value type does not match the function type bei (*). Es scheint, dass ProductB<Foo, Spam> nicht automatisch in seinen Basistyp konvertiert werden kann

Ich bin kein guter Fabrikentwickler, vielleicht verstehe ich nicht die grundlegenden Prinzipien und Konzepte. Könnte jemand helfen, diesen Code oder diesen Ansatz zu korrigieren? Vielen Dank!

Antwort

0

Wenn diese neu zu erstellen, die Fehler, die ich bekommen ist (etwas anders, was Sie veröffentlicht):

error: cannot convert 'ProductBFactory<ProductB1>::product_type* {aka ProductB<Bar, Spam>*}' to 'Product<Foo>*' in return 
     return ProductBFactory<ProductB1>::create(a, b); // (*) <--- compiler error 

Der Kern des Problems ist, dass Sie eine ProductB<Bar, Spam>* aus einer Funktion zurück sind versucht, erklärt zurückzukehren Product<Foo>*. Sie scheinen zu denken, dass diese Typen durch Vererbung irgendwie verwandt sind, aber sie sind überhaupt nicht. ProductB<Bar, Spam> ist in der Tat eine Unterklasse von Product<Bar> - diese letzte ist in keiner Weise verwandt oder konvertierbar zu einem Product<Foo> mehr als ein vector<int> ist zu einem vector<float> - d. H. Überhaupt nicht. Vorlagenklassen mit unterschiedlichen Vorlagenparametern unterscheiden sich vollständig voneinander.

Also entweder haben Sie einen Fehler in Ihrem Code gemacht, und versuchen, eine ProductB<Bar,Spam>* zurückgeben, wenn Sie eigentlich versuchen sollten, eine ProductB<Foo,Spam>* (sagen wir), oder Sie missverstehen die polymorphe Beziehung (oder deren Fehlen) zwischen Vorlage Vererbungshierarchien mit verschiedenen Vorlagenparametern.

UPDATE: Als Antwort auf Ihren Kommentar: Es ist möglich, eine Fabrik zu schaffen, die so lange Objekte unterschiedlicher Typen als Templat erzeugt, wie sie einen gemeinsamen Vorfahren in der Typenhierarchie haben, die als Rückgabetyp verwendet werden kann. In Ihrem Fall erfordert dies zumindest teilweise eine Neugestaltung. Eine Möglichkeit, dies zu tun wäre, um eine Basisklasse zu schaffen für Product<T>, sagt ProductBase, die nicht auf einer Vorlage abhängt:

template <class T> 
class Product : public ProductBase 
{ 
    ... 
}; 

Dann wird der Rückgabetyp Ihrer create Funktion ProductBase* sein kann. Genauso kann man die Vorlage einfach aus der Definition Product herausnehmen. In jedem Fall werden einige weitere Änderungen an der Benutzeroberfläche erforderlich sein.

Zum Beispiel, siehe live demo, wo ich die ProductBase Ansatz implementieren. Dein Beispiel kann also zum kompilieren gebracht werden, aber nur dank eines ekligen static_cast in der Hauptfunktion eingeführt. Dies ist die Art von Problem, die Sie beheben müssen, indem Sie Ihre Basisschnittstelle entsprechend ändern.

+0

Ok, vielleicht verstehe ich die polymorphe Beziehung zwischen Templates wirklich falsch, aber könnten Sie mir raten, wie dieses Problem gelöst werden könnte? Ist es möglich, eine Fabrik zu implementieren, die ein Templatobjekt erzeugt? – lexxa2000

+0

'' Sie scheinen zu denken, dass diese Typen irgendwie durch Vererbung verwandt sind'' - natürlich, ich nicht :) Ich schrieb diesen Code, um ein Konzept zu demonstrieren - es wird nicht kompilieren, weil in einer Funktion zwei verschiedene Typen zurückgegeben werden . – lexxa2000

+0

Siehe mein Update zu der obigen Antwort, einschließlich der Live-Demo – Smeeheey