Автоматический статический вызов производных типов - PullRequest
12 голосов
/ 19 июня 2011

Кто-нибудь знает способ заставить производные классы автоматически создавать экземпляры статической переменной с типом шаблона (это либо не должно ничего требовать от автора производного класса, либо заставлять его вызывать этот статический метод, чтобы сделатьопределение производного класса действительно).

Это, вероятно, невозможно понять, я постараюсь определить его лучше.

В основном у меня есть глобальный фабричный класс с шаблонной функцией registerType.Для каждого класса, производного от Entity, мне нужно, чтобы эта функция вызывалась с параметром шаблона производного типа.На данный момент я должен сделать это вручную в некоторой функции инициализации, что приводит к большому блоку вызовов этой функции, что противоречит принципу шаблонов для меня.

Итак, у меня есть это:

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

РЕДАКТИРОВАТЬ: Это статический повторяющийся код шаблона, который не работает:

Это моя базакласс и класс для автоматической регистрации вещи

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

Ответы [ 3 ]

10 голосов
/ 19 июня 2011

Я бы рекомендовал 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>();

Хотя, как видно, теперь вам нужно получить фабрику с помощью функции GetGlobalFactory, которая лениво инициализирует фабрику, чтобы убедиться, что она была инициализирована до того, как произойдет принудительная регистрация:

Factory& GetGlobalFactory(){
  static Factory _factory;
  return _factory;
}
8 голосов
/ 19 июня 2011

Возможно, вы сможете получить то, что вы хотите, используя микширование и CRTP.

Но сначала вам нужно позаботиться о проблеме «порядка инициализации».Чтобы убедиться, что gFactory существует до того, как вы попытаетесь его использовать, вам действительно нужно сделать его подходящим «синглтонным» классом, например так:

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

Тогда «дополнение» будет выглядеть так:

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 и осторожное использование статических локальных переменных, чтобы избежать проблемы порядка инициализации.

2 голосов
/ 24 июня 2011

Если кому-то еще интересно, я разобрался.Переменные-члены статического шаблона не создаются автоматически, если только они не используются.Мне нужно было создать его экземпляр перед вызовом конструктора, поэтому я не мог сделать его статическим локальным.Решение состоит в том, чтобы сделать его статической переменной-членом шаблона, а затем использовать ее (просто вызовите пустую функцию, если хотите) в функции-члене (я использую конструктор).Это вынуждает компилятор создавать экземпляр static для каждого когда-либо объявленного параметра шаблона, потому что его использует экземпляр конструкторского кода, например:

Мой класс реестра с пустой функцией для вызова

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();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...