2011-06-19 23 views
12

¿Alguien sabe de una forma de hacer que las clases derivadas instancian automáticamente una variable estática con un tipo de plantilla (esto no requiere nada del escritor de la clase derivada, o lo fuerza a llamarlo estático? método para hacer válida la definición de clase derivada).Invocación estática automática de tipos derivados

Esto es probablemente imposible de entender, intentaré definirlo mejor.

Básicamente tengo una clase de fábrica global con una función de plantilla llamada registerType. Para cada clase derivada de Entity, necesito que esta función sea llamada con el parámetro template del tipo derivado. Por el momento, tengo que hacerlo manualmente en alguna función init, lo que resulta en un gran bloque de llamadas a esta función, lo que va en contra del principio de las plantillas para mí.

Así que tengo esto:

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 
} 

mientras que yo preferiría tener esto:

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: Este es el código de la plantilla estática recurrentes que no está funcionando:

Este es mi clase base, y la clase para registrar automáticamente cosas

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; 
}; 

constructor

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

del registrador y un tipo derivado

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

El primer camino Me hollé está haciendo 'Entity' una clase de plantilla, por lo que sabe el tipo derivado . El problema allí es que los tipos derivados tendrían que ser ellos mismos plantillas (y, por lo tanto, ser abstractos), o nunca utilizados como clases base. También he visto macros utilizadas para esto en las bibliotecas de contenedor Win32. Y esta pregunta está relacionada de alguna manera: http://stackoverflow.com/questions/138600/initializing-a-static-stdmapint-int-in-c –

+0

Nm, parece que se llama CRTP, y las respuestas captaron lo que era getting at :) –

Respuesta

2

Si alguien todavía está interesado, lo descubrí. Las variables del miembro de la plantilla estática no se crean automáticamente a menos que se utilicen. Necesitaba que se creara una instancia antes de que se llamara al constructor, por lo que no pude convertirlo en un local estático. La solución es convertirlo en una variable de miembro de plantilla estática, y luego usarlo (solo llame a una función vacía si lo desea) en una función miembro (yo uso el constructor). Esto obliga al compilador para crear instancias de la estática para cada parámetro de plantilla cada vez declarado, ya que el código constructor de instancia lo utiliza, por ejemplo:

Mi clase de registro, con su función en blanco para llamar

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

Mi clase I querer registrado

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

constructor

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

del Registrador y mis clases constructor

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

Usted puede ser capaz de obtener lo que desea, utilizando una combinación de entrada y el CRTP.

Pero primero, debe ocuparse del problema de "orden de inicialización". Para garantizar la gFactory existe antes de intentar usarlo, que realmente necesita para que sea una clase propia "Singleton", así:

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

Entonces el "mix-in" se vería así:

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

Y que usaría así:

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

[actualización]

también puede tak La idea de Xeo/Merlyn de crear un EntityBase, renombrar EntityMixin a Entity, y evitar la necesidad de heredar desde dos lugares. De hecho, creo que mi propuesta original es más clara; incluso puede llamar al mixin FactoryMixin y colocarlo en cualquier clase que desee registrar.

Pero la versión Xeo/Merlyn se vería así:

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> { ... }; 

Las claves para cualquier solución son el CRTP y el uso cuidadoso de las variables locales estáticas para evitar el problema de orden de inicialización.

+0

Sugiero combinar el mix-in y la clase entity, si es posible. –

+0

Así que estoy separando el mixin y la clase de entidad solo por semántica ¿verdad? (porque tener ambos en una clase es feo y conflictivo) o hay alguna otra razón? – deek0146

+0

La mixin es una clase de plantilla. La clase de entidad de base común no es (al menos, supuse que es lo que quieres ...). Por lo tanto, no pueden ser lo mismo. – Nemo

10

lo recomiendo un enfoque -backed CTRP:

// 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>(); 

Aunque, como se ha visto, ahora se necesita para obtener su fábrica a través de una función GetGlobalFactory, que perezosa inicializa la fábrica para asegurarse de que se ha inicializado antes de que ocurra el registro forzado:

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

@Merlyn: correcto, sabía que quería vincularlo, pero de alguna manera lo olvidé. – Xeo

+0

+1. Esto es a lo que estaba tratando de llegar en mi comentario sobre el OP, pero olvidé que era un modismo completamente desarrollado. No estoy haciendo mucho C++ en estos días :) –

Cuestiones relacionadas