【问题标题】:Automatic static invocation of derived types派生类型的自动静态调用
【发布时间】:2011-09-17 23:50:08
【问题描述】:

有谁知道让派生类自动实例化具有模板类型的静态变量的方法(这要么不需要派生类的编写者任何东西,要么强迫他调用这个静态方法以使派生类定义有效)。

这可能无法理解,我会尝试更好地定义它。

基本上,我有一个全局工厂类,其中包含一个名为 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>();
    }
};

编辑:这是不起作用的静态重复模板代码:

这是我的基类,也是自动注册东西的类

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

【问题讨论】:

  • 我要走的第一条路是让Entity 成为一个模板类,所以它知道派生类型。派生类型的问题要么必须是模板(因此是抽象的),要么永远不会用作基类。我还在 win32 包装器库中看到了用于此的宏。而且,这个问题有点相关 - stackoverflow.com/questions/138600/…
  • Nm,它似乎被称为 CRTP,答案捕捉到了我所得到的 :)

标签: c++ templates static


【解决方案1】:

您可以使用混入和 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 和谨慎使用静态局部变量以避免初始化顺序问题。

【讨论】:

  • 如果可能的话,我建议将 mix-in 和实体类结合起来。
  • 所以我将 mixin 和实体类分开只是为了语义对吧? (因为两者都在一个班级是丑陋和冲突的)还是有其他原因?
  • mixin 是一个模板类。公共基础实体类不是(至少,我认为这是你想要的......)。因此它们不可能相同。
  • 好酷。我不太了解静态,有没有办法在程序加载时调用注册函数,而不是在分配对象的第一个实例时?
  • @Alasdair:是的,看看我的答案,静态 char 在程序启动时被初始化。
【解决方案2】:

我推荐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;
}

【讨论】:

  • @Merlyn:对,我知道我想链接到它,但不知何故忘记了。
  • +1。这就是我在对 OP 的评论中试图表达的意思,但我忘记了这是一个成熟的成语。这些天没有做太多 C++ :)
【解决方案3】:

如果有人仍然感兴趣,我想通了。除非使用静态模板成员变量,否则它们不会自动实例化。我需要在调用构造函数之前对其进行实例化,因此我无法将其设为静态本地。解决办法是把它变成一个静态模板成员变量,然后在成员函数中使用它(如果你想调用一个空函数就行了)(我用的是构造函数)。这迫使编译器为曾经声明的每个模板参数实例化静态,因为实例化的构造函数代码使用它,例如:

我的注册表类,其空白函数用于调用

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-06-25
    • 1970-01-01
    • 2012-05-24
    • 2018-11-30
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2013-01-10
    相关资源
    最近更新 更多