Автоматический статический вызов производных типов
Кто-нибудь знает, как заставить производные классы автоматически создавать статическую переменную с типом шаблона (это либо не требует ничего от писателя производного класса, либо заставляет его называть этот статический метод, чтобы сделать определение производного класса).
Это, вероятно, невозможно понять, я попытаюсь определить его лучше.
В принципе у меня есть глобальный класс factory с шаблонизированной функцией, называемой registerType. Для каждого класса, производного от Entity, мне нужно, чтобы эта функция вызывалась с параметром шаблона производного типа. На данный момент мне приходится вручную делать это в некоторой функции init, что приводит к большому блоку вызовов этой функции, что противоречит принципу шаблонов для меня.
Итак, у меня есть это:
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
}
тогда как я предпочел бы это:
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: это статический повторяющийся код шаблона, который не работает:
Это мой базовый класс, а класс для автоматической регистрации материала
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;
};
Конструктор регистратора
template <typename DerivedType>
Registrar<DerivedType>::Registrar()
{
std::cout << DerivedType::name() << " initialisation" << std::endl;
g_AbstractFactories.registerFactoryType<DerivedType>(DerivedType::name());
}
И производный тип
class CrateFactory : public AbstractFactory<Entity, CrateFactory>
{
public:
CrateFactory(FactoryLoader* loader);
virtual ~CrateFactory();
Entity* useFactory(FactoryParameters* parameters);
static std::string name()
{
return "CrateFactory";
}
Ответы
Ответ 1
Если кому-то все еще интересно, я понял это. Статические переменные-члены шаблона не создаются автоматически, если только они не используются. Мне нужно было создать экземпляр перед вызовом конструктора, поэтому я не мог сделать его статическим локальным. Решение состоит в том, чтобы сделать его статической переменной-члена шаблона, а затем использовать его (просто вызывать пустую функцию на нем, если хотите) в функции-члене (я использую конструктор). Это заставляет компилятор создавать экземпляр для каждого параметра шаблона, который когда-либо был объявлен, потому что его использует код созданного конструктора, например:
Мой класс реестра, с его пустой функцией для вызова
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;
};
Конструктор регистратора
template <typename DerivedType>
Registrar<DerivedType>::Registrar()
{
std::cout << DerivedType::name() << " initialisation" << std::endl;
g_AbstractFactories.registerFactoryType<DerivedType>(DerivedType::name());
}
И мой конструктор классов
template <typename Product, typename DerivedType>
AbstractFactory::AbstractFactory()
{
registrar.check();
}
Ответ 2
Я бы рекомендовал CTRP -backed подход:
// 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>();
Хотя, как видно, теперь вам нужно получить factory через функцию GetGlobalFactory
, которая ленив инициализирует factory, чтобы убедиться, что она была инициализирована до того, как произойдет принудительная регистрация:
Factory& GetGlobalFactory(){
static Factory _factory;
return _factory;
}
Ответ 3
Возможно, вы сможете получить то, что хотите, с помощью микширования и CRTP.
Но сначала вам нужно позаботиться о проблеме "порядка инициализации". Чтобы гарантировать, что gFactory
существует до того, как вы попытаетесь его использовать, вам действительно нужно сделать его подходящим классом "singleton", например:
class Factory {
public:
static Factory &getFactory() { static Factory f; return f; }
template <typename EntityType>
void registerEntityType { ... }
};
Затем "mix-in" будет выглядеть так:
template <typename T>
class EntityMixin {
private:
struct RegisterMe {
RegisterMe() { Factory::getFactory().registerEntityType<T>(); }
};
EntityMixin() {
static RegisterMe r;
}
};
И вы будете использовать его следующим образом:
class EntityType1 : public Entity, EntityMixin<EntityType1> { ... };
class EntityType2 : public Entity, EntityMixin<EntityType2> { ... };
class EntityType3 : public Entity, EntityMixin<EntityType3> { ... };
[Обновление]
Вы также можете взять идею Xeo/Merlyn о создании EntityBase
, переименовать EntityMixin
в Entity
и избежать необходимости наследовать из двух мест. Я действительно думаю, что мое первоначальное предложение более ясное; вы можете даже вызвать mixin FactoryMixin
и привязать его к любому классу, который хотите зарегистрировать.
Но версия Xeo/Merlyn будет выглядеть так:
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> { ... };
Ключами к любому решению являются CRTP и тщательное использование статических локальных переменных, чтобы избежать проблемы с порядком инициализации.