Automatic static invocation of derived types

前端 未结 3 1532
旧巷少年郎
旧巷少年郎 2021-02-02 18:03

Does anyone know of a way to make derived classes automatically instantiate a static variable with a template type (this either has to require nothing from the writer of the der

相关标签:
3条回答
  • 2021-02-02 18:23

    You might be able to get what you want using a mix-in and the CRTP.

    But first, you need to take care of the "order of initialization" problem. To ensure the gFactory exists before you try to use it, you really need to make it a proper "singleton" class, like this:

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

    Then the "mix-in" would look like this:

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

    And you would use it like this:

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

    [Update]

    You can also take the Xeo/Merlyn idea of creating an EntityBase, rename EntityMixin to Entity, and avoid the need to inherit from two places. I actually think my original proposal is more clear; you could even call the mixin FactoryMixin and tack it on to any class you want to register.

    But the Xeo/Merlyn version would look like so:

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

    The keys to any solution are the CRTP and careful use of static local variables to avoid the order-of-initialization problem.

    0 讨论(0)
  • 2021-02-02 18:27

    I'd recommend a CTRP-backed approach:

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

    Though, as seen, you now need to get your factory through a GetGlobalFactory function, which lazy initializes the factory to ensure that it has been initialized before the enforced registration happens:

    Factory& GetGlobalFactory(){
      static Factory _factory;
      return _factory;
    }
    
    0 讨论(0)
  • 2021-02-02 18:39

    If anyone is still interested, I figured it out. Static template member variables are not automatically instantiated unless they are used. I needed it to be instantiated before the constructor was called, so I couldn't make it a static local. The solution is to make it a static template member variable, and then use it (just call an empty function on it if you want) in a member function (I use the constructor). This forces the compiler to instantiate the static for every template parameter ever declared, because the instantiated constructor code uses it, for example:

    My registry class, with its blank function for calling

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

    My class I want registered.

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

    The registrar's constructor

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

    And my classes constructor

    template <typename Product, typename DerivedType>
    AbstractFactory::AbstractFactory()
    {
        registrar.check();
    }
    
    0 讨论(0)
提交回复
热议问题