2011-06-19 7 views
12

Est-ce que quelqu'un sait comment créer des classes dérivées instancie automatiquement une variable statique avec un type de modèle (cela ne doit rien exiger de l'auteur de la classe dérivée, ou le forcer à appeler cela statique méthode afin de rendre la définition de classe dérivée valide).Invocation statique automatique de types dérivés

Ceci est probablement impossible à comprendre, je vais essayer de mieux le définir. Fondamentalement, j'ai une classe de fabrique globale avec une fonction de modèle appelée registerType. Pour chaque classe dérivée de l'entité, j'ai besoin que cette fonction soit appelée avec le paramètre template du type dérivé. Pour le moment, je dois le faire manuellement dans une fonction init, ce qui se traduit par un gros bloc d'appels à cette fonction, ce qui va à l'encontre du principe des templates pour moi.

J'ai donc ceci:

class Factory 
{ 
    template <typename EntityType> 
    registerEntityType(); 
}; 

void someInitFunction() 
{ 
    /// All of these are derived from Entity 
    gFactory.registerEntityType<EntityType1>(); 
    gFactory.registerEntityType<EntityType2>(); 
    gFactory.registerEntityType<EntityType3>(); 
    /// and so on 
} 

alors que je préférerais avoir ceci:

class Factory 
{ 
    template <typename EntityType> 
    registerEntityType(); 
}; 

class Entity // Abstract 
{ 
    /// This function should be called automatically with the derived 
    /// type as a parameter 
    SomeStaticConstructor<MDerivedType>() 
    { 
     gFactory.registerEntityType<MDerivedType>(); 
    } 
}; 

EDIT: Voici le code du modèle récurrent statique qui ne fonctionne pas:

Cette est ma classe de base, et la classe pour l'enregistrement automatique des choses

template <typename DerivedType> 
class Registrar 
{ 
    public: 
     Registrar(); 
     void check(); 
}; 
template <typename Product, typename DerivedType> 
class AbstractFactory: public AbstractFactoryBase<Product> 
{ 
    public: 
     AbstractFactory(); 
     ~AbstractFactory(); 
    private: 
     static Registrar<DerivedType> registrar; 
}; 

constructeur

template <typename DerivedType> 
Registrar<DerivedType>::Registrar() 
{ 
    std::cout << DerivedType::name() << " initialisation" << std::endl; 
    g_AbstractFactories.registerFactoryType<DerivedType>(DerivedType::name()); 
} 

du bureau d'enregistrement et un type dérivé

class CrateFactory : public AbstractFactory<Entity, CrateFactory> 
{ 
    public: 
     CrateFactory(FactoryLoader* loader); 
     virtual ~CrateFactory(); 
     Entity* useFactory(FactoryParameters* parameters); 
     static std::string name() 
     { 
      return "CrateFactory"; 
     } 
+0

Le premier chemin je foulera fait 'Entity' une classe de modèle, il connaît le type dérivé . Le problème est que les types dérivés doivent eux-mêmes être des modèles (et donc être abstraits), ou ne jamais être utilisés comme classes de base. J'ai également vu des macros utilisées pour cela dans les bibliothèques wrapper win32. Et, cette question est en quelque sorte liée - http://stackoverflow.com/questions/138600/initializing-a-static-stdmapint-int-in-c –

+0

Nm, il semble qu'il s'appelle CRTP, et les réponses capturent ce que j'étais obtenir à :) –

Répondre

2

Si quelqu'un est toujours intéressé, je l'ai compris. Les variables de membre de modèle statique ne sont pas automatiquement instanciées sauf si elles sont utilisées. J'avais besoin qu'il soit instancié avant que le constructeur ne soit appelé, donc je ne pouvais pas en faire un local statique. La solution consiste à en faire une variable membre de modèle statique, puis à l'utiliser (appelez simplement une fonction vide si vous le souhaitez) dans une fonction membre (j'utilise le constructeur). Cela force le compilateur à instancier statique pour chaque paramètre de modèle jamais déclaré, parce que le code constructeur instancié utilise, par exemple:

Ma classe de registre, avec sa fonction vide pour appeler

template <typename DerivedType> 
class Registrar 
{ 
    public: 
     Registrar(); 
     void check(){} 
}; 

Ma classe I veux enregistré.

template <typename Product, typename DerivedType> 
class AbstractFactory: public AbstractFactoryBase<Product> 
{ 
    public: 
     AbstractFactory(); 
     ~AbstractFactory(); 
    private: 
     static Registrar<DerivedType> registrar; 
}; 

Le constructeur de registraire

template <typename DerivedType> 
Registrar<DerivedType>::Registrar() 
{ 
    std::cout << DerivedType::name() << " initialisation" << std::endl; 
    g_AbstractFactories.registerFactoryType<DerivedType>(DerivedType::name()); 
} 

Et mes cours constructeur

template <typename Product, typename DerivedType> 
AbstractFactory::AbstractFactory() 
{ 
    registrar.check(); 
} 
7

Vous pourriez être en mesure d'obtenir ce que vous voulez à l'aide d'un mélange-et le CRTP.

Mais d'abord, vous devez vous occuper du problème de "l'ordre d'initialisation". Pour assurer la gFactory existe avant d'essayer de l'utiliser, vous avez vraiment besoin de faire un bon « singleton » classe, comme ceci:

class Factory { 
public: 
    static Factory &getFactory() { static Factory f; return f; } 
    template <typename EntityType> 
    void registerEntityType { ... } 
}; 

ensuite le « mix-in » ressemblerait à ceci:

template <typename T> 
class EntityMixin { 
private: 
    struct RegisterMe { 
     RegisterMe() { Factory::getFactory().registerEntityType<T>(); } 
    }; 
    EntityMixin() { 
     static RegisterMe r; 
    } 
}; 

Et vous l'utiliser comme ceci:

class EntityType1 : public Entity, EntityMixin<EntityType1> { ... }; 
class EntityType2 : public Entity, EntityMixin<EntityType2> { ... }; 
class EntityType3 : public Entity, EntityMixin<EntityType3> { ... }; 

[Mise à jour]

vous pouvez également tak e l'idée Xeo/Merlyn de créer un EntityBase, renommer EntityMixin en Entity, et d'éviter le besoin d'hériter de deux endroits. Je pense en fait que ma proposition originale est plus claire; vous pouvez même appeler le mix FactoryMixin et l'ajouter à n'importe quelle classe que vous souhaitez enregistrer.

Mais la Xeo/version Merlyn ressemblerait à ceci:

class Factory { 
    public: 
    static Factory &getFactory() { static Factory f; return f; } 
    template <typename EntityType> 
    void registerEntityType { ... } 
}; 

class EntityBase { ... } ; 

template <typename T> 
class Entity : public EntityBase { 
private: 
    struct RegisterMe { 
     RegisterMe() { Factory::getFactory().registerEntityType<T>(); } 
    }; 
    Entity() { 
     static RegisterMe r; 
    } 
}; 

class EntityType1 : public Entity<Entitytype1> { ... }; 
class EntityType2 : public Entity<Entitytype2> { ... }; 
class EntityType3 : public Entity<Entitytype3> { ... }; 

Les clés de toute solution sont les CRTP et l'utilisation prudente des variables locales statiques pour éviter le problème d'ordre de l'initialisation.

+0

Je suggérerais de combiner le mix-in et la classe d'entité, si possible. –

+0

Donc je sépare le mixin et la classe d'entité juste pour la sémantique? (parce que avoir les deux dans une classe est laide et contradictoire) ou y a-t-il une autre raison? – deek0146

+0

Le mixin est une classe modèle. La classe d'entité de base commune n'est pas (au moins, j'ai supposé que c'est ce que vous voulez ...). Par conséquent, ils ne peuvent pas être les mêmes. – Nemo

10

je recommande une approche CTRP -grièche:

// Entity.h 
class EntityBase 
{ // abstract 
}; 

template<class Derived> 
class Entity 
    : public EntityBase 
{ // also abstract thanks to the base 
    static char _enforce_registration; // will be instantiated upon program start 
}; 

// your actual types in other headers 
class EntityType1 
    : public Entity<EntityType1> 
{ // automatic registration thanks to the _enforce_registration of the base 
    // ... 
}; 

// Entity.cpp 
#include "Entity.h" 

template<class T> 
char RegisterType(){ 
    GetGlobalFactory().registerEntityType<T>(); 
    return 0; // doesn't matter, never used. 
} 

template<class Derived> 
char Entity<Derived>::_enforce_registration = RegisterType<Derived>(); 

Bien que, comme on le voit, vous avez besoin maintenant d'obtenir votre usine grâce à une fonction GetGlobalFactory, qui paresseux initialise l'usine pour veiller à ce qu'il a été initialisé avant l'enregistrement forcé:

Factory& GetGlobalFactory(){ 
    static Factory _factory; 
    return _factory; 
} 
+0

@Merlyn: oui, je savais que je voulais faire un lien, mais je l'ai oublié. – Xeo

+0

+1. C'est ce que j'essayais d'obtenir dans mon commentaire sur le PO, mais j'ai oublié que c'était un idiome à part entière. Ne pas faire beaucoup de C++ ces jours-ci :) –

Questions connexes