2009-11-11 5 views
2

Est-il possible de créer une fabrique de classes de modèles composites sans spécifier manuellement toutes les combinaisons? Ce que je veux dire est que si je ces classes:Modèle composite C++ Classe Factory

class CompositeBase {}; 

template< typename C1, typename C2, typename C3 > 
class Composite : public CompositeBase 
{ 
private: 
    C1 component1; 
    C2 component2; 
    C3 component3; 
}; 

class Component0 {}; //Also have Component1-9 

Je voudrais créer une fonction comme ceci:

CompositeBase *CreateComposite(int c1, int c2, int c3); 

afin que

CreateComposite(4,3,7); 

pourrait créer et retourner une

Composite<Component4,Component3,Component7> 

La raison pour cela est afin que je puisse charger les données d'un fichier et créer les différents objets composites. Le fichier aurait les trois valeurs pour les composants pour créer chaque objet composite, puis les autres données requises par celui-ci.

Le problème est avec 10 composants différents, il existe 1000 différentes classes composites possibles. Pour les spécifier, il faudrait une instruction switch pour le premier composant, 10 instructions switch pour le second composant dans celui-là, et 100 instructions switch pour le troisième composant dans ces dix, et la fonction serait de plus de 1000 lignes.

Existe-t-il un autre moyen d'écrire la fonction CreateComposite? Autre que ceci:

CompositeBase *CreateComposite(int c1, int c2, int c3) 
{ 
    switch(c1) 
    { 
    case 0: 
     switch(c2) 
     { 
     case 0: 
      switch(c3) 
      { 
       case 0: return new Composite<Component0,Component0,Component0>; 
       case 1: return new Composite<Component0,Component0,Component1>; 
       //etc 
      } 
      //etc 
     } 
     //etc 
    } 
} 

Répondre

2

Vous pouvez éviter O (n^3) complexité O (n) en utilisant des méthodes de modèle en cascade avec interrupteur unique cas en elle:

#include <iostream> 
using namespace std; 

class CompositeBase 
{ 
public: 
    virtual void print(std::ostream& o_out) = 0; 
}; 

template< typename C1, typename C2, typename C3 > 
class Composite : public CompositeBase 
{ 
public: 
    void print(std::ostream& o_out) 
    { 
     o_out << typeid(*this).name(); 
    } 
private: 
    C1 component1; 
    C2 component2; 
    C3 component3; 
}; 


class Component0 {}; 
class Component1 {}; 
class Component2 {}; 
class Component3 {}; 
class Component4 {}; 
class Component5 {}; 
class Component6 {}; 
class Component7 {}; 
class Component8 {}; 
class Component9 {}; 

template<typename C1,typename C2,typename C3> 
CompositeBase *CreateComposite0() 
{ 
    return new Composite<C1,C2,C3>(); 
} 

template<typename C1,typename C2> 
CompositeBase *CreateComposite1(int c3) 
{ 
    switch(c3) 
    { 
    case 0: return CreateComposite0<C1,C2,Component0>(); 
    case 1: return CreateComposite0<C1,C2,Component1>(); 
    case 2: return CreateComposite0<C1,C2,Component2>(); 
    case 3: return CreateComposite0<C1,C2,Component3>(); 
    case 4: return CreateComposite0<C1,C2,Component4>(); 
    case 5: return CreateComposite0<C1,C2,Component5>(); 
    case 6: return CreateComposite0<C1,C2,Component6>(); 
    case 7: return CreateComposite0<C1,C2,Component7>(); 
    case 8: return CreateComposite0<C1,C2,Component8>(); 
    case 9: return CreateComposite0<C1,C2,Component9>(); 
    default: return 0; 
    } 
} 

template<typename C1> 
CompositeBase *CreateComposite2(int c2, int c3) 
{ 
    switch(c2) 
    { 
    case 0: return CreateComposite1<C1,Component0>(c3); 
    case 1: return CreateComposite1<C1,Component1>(c3); 
    case 2: return CreateComposite1<C1,Component2>(c3); 
    case 3: return CreateComposite1<C1,Component3>(c3); 
    case 4: return CreateComposite1<C1,Component4>(c3); 
    case 5: return CreateComposite1<C1,Component5>(c3); 
    case 6: return CreateComposite1<C1,Component6>(c3); 
    case 7: return CreateComposite1<C1,Component7>(c3); 
    case 8: return CreateComposite1<C1,Component8>(c3); 
    case 9: return CreateComposite1<C1,Component9>(c3); 
    default: return 0; 
    } 
} 

CompositeBase *CreateComposite(int c1,int c2, int c3) 
{ 
    switch(c1) 
    { 
    case 0: return CreateComposite2<Component0>(c2,c3); 
    case 1: return CreateComposite2<Component1>(c2,c3); 
    case 2: return CreateComposite2<Component2>(c2,c3); 
    case 3: return CreateComposite2<Component3>(c2,c3); 
    case 4: return CreateComposite2<Component4>(c2,c3); 
    case 5: return CreateComposite2<Component5>(c2,c3); 
    case 6: return CreateComposite2<Component6>(c2,c3); 
    case 7: return CreateComposite2<Component7>(c2,c3); 
    case 8: return CreateComposite2<Component8>(c2,c3); 
    case 9: return CreateComposite2<Component9>(c2,c3); 
    default: return 0; 
    } 
} 

int main() 
{ 
    CompositeBase* base1 = CreateComposite(4,5,6); 
    CompositeBase* base2 = CreateComposite(8,2,0); 
    base1->print(cout); 
    cout << endl; 
    base2->print(cout); 
    return 0; 
} 

Juste pour le plaisir, vous pouvez utiliser boost proprocessor O (1) complexité

#include <iostream> 
#include <boost/preprocessor/repetition.hpp> 
using namespace std; 

class CompositeBase 
{ 
public: 
    virtual void print(std::ostream& o_out) = 0; 
}; 

template< typename C1, typename C2, typename C3 > 
class Composite : public CompositeBase 
{ 
public: 
    void print(std::ostream& o_out) 
    { 
     o_out << typeid(*this).name(); 
    } 
private: 
    C1 component1; 
    C2 component2; 
    C3 component3; 
}; 

#define DIM 10 

#define COMPONENT_DECLARATION(z,n,unused) class BOOST_PP_CAT(Component,n) {}; 
BOOST_PP_REPEAT(DIM,COMPONENT_DECLARATION, ~) 
#undef COMPONENT_DECLARATION 

template<typename C1,typename C2,typename C3> 
CompositeBase *CreateComposite0() 
{ 
    return new Composite<C1,C2,C3>(); 
} 

template<typename C1,typename C2> 
CompositeBase *CreateComposite1(int c3) 
{ 
#define COMPOSITE(z,n,unused) case n: return CreateComposite0<C1,C2,BOOST_PP_CAT(Component,n)>(); 
    switch(c3) 
    { 
BOOST_PP_REPEAT(DIM,COMPOSITE,~) 
    default: return 0; 
    } 
#undef COMPOSITE 
} 

template<typename C1> 
CompositeBase *CreateComposite2(int c2, int c3) 
{ 
#define COMPOSITE(z,n,unused) case n: return CreateComposite1<C1,BOOST_PP_CAT(Component,n)>(c3); 
    switch(c2) 
    { 
BOOST_PP_REPEAT(DIM,COMPOSITE,~) 
    default: return 0; 
    } 
#undef COMPOSITE 
} 

CompositeBase *CreateComposite(int c1,int c2, int c3) 
{ 
#define COMPOSITE(z,n,unused) case n: return CreateComposite2<BOOST_PP_CAT(Component,n)>(c2,c3); 
    switch(c1) 
    { 
BOOST_PP_REPEAT(DIM,COMPOSITE,~) 
    default: return 0; 
    } 
#undef COMPOSITE 
} 

#undef DIM 

int main() 
{ 
    CompositeBase* base1 = CreateComposite(4,5,6); 
    CompositeBase* base2 = CreateComposite(8,2,0); 
    base1->print(cout); 
    cout << endl; 
    base2->print(cout); 
    return 0; 
} 

Dans tous les cas, je recommande d'éviter ces solutions si possible. Pourquoi voudriez-vous même une classe Composite générique avec 3 membres de types complètement arbitraires?

0

Nope. Cela nécessiterait une réflexion, ce que C++ n'a pas.

Pourquoi ne pas simplement CreateComposite<T1, T2, T3>()?

EDIT: Cela ressemble à un cas où le modèle état/stratégie serait utile.

+0

J'ai besoin de lire ces données à partir d'un fichier, donc je ne peux pas appeler ainsi. – Frank

3

Il n'est pas possible de générer toutes les combinaisons possibles d'un modèle lors de l'exécution. Le compilateur doit savoir lesquels vous allez utiliser pour pouvoir générer du code pour eux.

Avez-vous envisagé d'utiliser composition plutôt que d'hériter?

Edit: Par composition, je pensais plus le long des lignes de ce qui suit:


class Composite 
{ 
private: 
    ComponentBase * component1; 
    ComponentBase * component2; 
    ComponentBase * component3; 
}; 
class Component0 : public ComponentBase {}; //Also have Component1-9 

Si vous ne pouvez pas utiliser des pointeurs à une classe de base pour une raison quelconque, vous êtes coincé avec code générer toutes les permutations des classes de modèles. Mais plutôt que d'utiliser des instructions de cas imbriquées, vous pouvez utiliser une table pour créer de nouvelles instances et des macros pour faciliter le code.

typedef CompositeBase * NewComposite(); 
#define NEW_COMPOSITE(P1,P2,P3) CompositeBase * NewComposite##P1##P2##P3() { return new Composite<Component##P1,Component##P2,Component##P3>; } 
NEW_COMPOSITE(0,0,0) NEW_COMPOSITE(0,0,1) NEW_COMPOSITE(0,0,2) NEW_COMPOSITE(0,0,3) 
NEW_COMPOSITE(0,0,4) ... 
... 
CompositeBase *CreateComposite(int c1, int c2, int c3) 
{ 
    static NewComposite * newCompositeTable[10][10][10] = {{{NewComposite000,NewComposite001,...},{NewComposite010,...}}}; 
    return newCompositeTable[c1][c2][c3](); 
} 

Je ne peux pas vraiment dire que c'est une meilleure approche que celle avec laquelle vous avez commencé, mais c'est une alternative à considérer.

+0

J'ai mis à jour le post. J'utilise la composition, du moins je le pense. C'est juste que les composants de Composite sont spécifiés par les arguments du template. – Frank

3

Les arguments de modèle doivent être connus au moment de la compilation. Une solution possible serait: CreateComposite<int, int, int>() et vous pouvez le spécialiser pour chaque cas possible. Oh, eh bien: c'est plutôt un non go .. Je suggérerais que tu préfères aller avec un polymorphisme dynamique démodé et un std::vector<ComponentBase>.

+1

+ 1,000,000 pour proposer la seule solution rationnelle, il suffit d'utiliser le polymorphisme! –

+0

Eh bien, c'est la chose. Je connais tous les arguments possibles au moment de la compilation, ils ne peuvent être que Component0-9. Il n'y aura jamais de Composite par exemple. J'ai ajouté mon exemple d'une fonction CreateComposite, mais longue. Il semble juste qu'il devrait y avoir un moyen de le faire sans tout ce type de frappe. Je peux avoir à faire ce que vous suggérez, il semblait juste que c'était une bonne façon de créer un composite sans allouer les composants individuellement et sans avoir à utiliser des fonctions virtuelles pour y accéder. – Frank

+0

Vous connaissez les arguments possibles au moment de la compilation, mais pas les arguments -actual-; ils entrent par l'intermédiaire de vos 3 paramètres entiers, au moment de l'exécution, ce qui est trop tard pour générer des modèles. – Kylotan

0

Alors que je suis d'accord avec les autres que le polymorphisme dynamique est probablement la voie à suivre, techniquement, vous pouvez faire ce que vous demandez. Tout le monde, vos yeux maintenant éviter ...:

#include <stdexcept> 
#include <iostream> 

struct CompositeBase 
{ 
    virtual ~CompositeBase() = 0 {} 
    virtual std::ostream& output (std::ostream& os) const = 0; 
}; 

template<typename CA, typename CB, typename CC> 
struct Composite : public CompositeBase 
{ 
    std::ostream& output (std::ostream& os) const 
    { 
     return os << componentA.id() << "," << componentB.id() << "," << componentC.id(); 
    } 

    CA componentA; 
    CB componentB; 
    CC componentC; 
}; 

struct Component0 {int id() const {return 0;}}; 
struct Component1 {int id() const {return 1;}}; 
struct Component2 {int id() const {return 2;}}; 
struct Component3 {int id() const {return 3;}}; 
struct Component4 {int id() const {return 4;}}; 
// ... 

template<int N> struct IntToType {}; 
template<> struct IntToType<0> {typedef Component0 Type;}; 
template<> struct IntToType<1> {typedef Component1 Type;}; 
template<> struct IntToType<2> {typedef Component2 Type;}; 
template<> struct IntToType<3> {typedef Component3 Type;}; 
template<> struct IntToType<4> {typedef Component4 Type;}; 
// ... 

// Change 4 to match number of composites. 
template<int N1 = 4, int N2 = 4, int N3 = 4> 
struct CompositeFactory 
{ 
    static CompositeBase* create (int c1, int c2, int c3) 
    { 
     if (c1 == N1) 
     { 
      if (c2 == N2) 
      { 
       if (c3 == N3) 
        return new Composite<IntToType<N1>::Type, IntToType<N2>::Type, IntToType<N3>::Type>; 
       else 
        return CompositeFactory<N1, N2, N3-1>::create (c1, c2, c3); 
      } 
      else 
       return CompositeFactory<N1, N2-1, N3>::create (c1, c2, c3); 
     } 
     else 
      return CompositeFactory<N1-1, N2, N3>::create (c1, c2, c3); 
    } 
}; 

template<int N2, int N3> 
struct CompositeFactory<-1, N2, N3> 
{ 
    static CompositeBase* create (int c1, int c2, int c3) 
    { 
     throw std::runtime_error ("Could not create Composite"); 
    } 
}; 

template<int N1, int N3> 
struct CompositeFactory<N1, -1, N3> 
{ 
    static CompositeBase* create (int c1, int c2, int c3) 
    { 
     throw std::runtime_error ("Could not create Composite"); 
    } 
}; 

template<int N1, int N2> 
struct CompositeFactory<N1, N2, -1> 
{ 
    static CompositeBase* create (int c1, int c2, int c3) 
    { 
     throw std::runtime_error ("Could not create Composite"); 
    } 
}; 

CompositeBase* createComposite (int c1, int c2, int c3) 
{ 
    return CompositeFactory<>::create (c1, c2, c3); 
} 

int main (int argc, char* argv[]) 
{ 
    CompositeBase* comp = createComposite (4,1,2); 

    comp->output (std::cout); 

    return 0; 
} 

qui fonctionne pour 5 types et il devrait être évident comment l'étendre à 10 types.

Je ne suis pas sûr que je voudrais jamais utiliser réellement cependant - pour 10 composites le compilateur générerait toutes les 1000 instanciations du modèle composite.

0

Pourquoi voudriez-vous même une classe composite générique avec 3 membres de types complètement arbitraires? Je ne peux pas imaginer que vous puissiez écrire un code utile pour une telle classe dans cette situation. Le Composite lui-même ne peut contenir aucun code utile et tout code qui utilise un composite devra connaître les types qu'il contient (que vous ne stockez actuellement nulle part). Cependant, si les 3 types mettent chacun en œuvre une sorte d'interface connue - par exemple. le premier est toujours un type numérique, le second est un type de séquence, etc - alors vous pouvez simplement utiliser une fabrique pour chaque type et utiliser le polymorphisme pour retourner quelque chose avec l'interface correcte, et vous n'avez pas du tout besoin des modèles.