2011-06-19 83 views
12

有誰知道如何讓派生類自動實例化一個帶有模板類型的靜態變量(這或者不需要派生類的編寫者,或者強制他調用這個靜態方法以使派生類定義有效)。派生類型的自動靜態調用

這可能是不可能理解的,我會試着更好地定義它。

基本上我有一個全局工廠類與模板函數稱爲registerType。對於從實體派生的每個類,我需要使用派生類型的模板參數調用此函數。目前,我必須在一些初始化函數中手動執行它,這會導致對該函數的大量調用,這種方式違背了模板的原理。

所以我有這樣的:

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

我踐踏,第一條路徑是使'Entity'一個模板類,因此它知道派生類型。有派生類型的問題要麼必須是模板(因此是抽象的),要麼從不用作基類。我也看到了在win32包裝庫中使用的宏。而且,這個問題有點相關 - http://stackoverflow.com/questions/138600/initializing-a-static-stdmapint-int-in-c –

+0

Nm,它似乎被稱爲CRTP,並且答案捕獲了我的越來越:) –

回答

2

如果有人仍然有興趣,我想通了。除非使用靜態模板成員變量,否則不會自動實例化。我需要它在構造函數被調用之前被實例化,所以我不能使它成爲一個靜態本地。解決方法是使其成爲靜態模板成員變量,然後在成員函數(我使用構造函數)中使用它(只需調用一個空函數)。這迫使編譯器實例化靜爲每個模板參數曾經宣佈過,因爲實例化的構造函數代碼使用它,例如:

我的註冊表類,其空函數調用

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

你也許能夠得到你想要使用一個mix-in和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> { ... }; 

[更新]

您也可以德e 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並謹慎使用靜態局部變量,以避免訂單的初始化的問題。

+0

如果可能的話,我建議組合mix-in和實體類。 –

+0

因此,我將mixin和實體類分開來用於語義對吧? (因爲在一個班級中都有醜陋和衝突)還是有其他原因? – deek0146

+0

mixin是一個模板類。公共基地實體類不是(至少,我認爲這是你想要的......)。因此它們不可能是相同的。 – Nemo

10

我推薦一個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>(); 

儘管正如所見,你現在需要通過GetGlobalFactory功能,懶初始化的工廠,以確保它已經被初始化,讓您的工廠在強制登記發生之前:

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

@Merlyn:對,我知道我想鏈接到它,但不知何故忘了它。 – Xeo

+0

+1。這是我在評論OP時試圖得到的結果,但我忘了它是一個成熟的成語。這些日子沒有做太多的C++工作:) –