2009-08-21 10 views
2

オブジェクトを型ID名で作成する便利なオブジェクトファクトリテンプレートがあります。実装はかなり明白です:ObjectFactoryにはstd::stringからオブジェクト作成者関数へのマップが含まれています。次に、作成するすべてのオブジェクトがこのファクトリに登録されます。オブジェクトファクトリにオブジェクト作成者を登録する

は、私はそれを行うには、次のマクロを使用:

#define REGISTER_CLASS(className, interfaceName) \ 
    class className; \ 
    static RegisterClass<className, interfaceName> regInFactory##className; \ 
    class className : public interfaceName 

RegisterClassが使用

class IFoo 
{ 
public: 
    virtual Do() = 0; 
    virtual ~IFoo() {} 
} 

REGISTER_CLASS(Foo, IFoo) 
{ 
    virtual Do() { /* do something */ } 
} 

REGISTER_CLASS(Bar, IFoo) 
{ 
    virtual Do() { /* do something else */ } 
} 

クラスが定義されたと同時に、工場に登録されている

template<class T, class I> 
    struct RegisterClass 
    { 
     RegisterClass() 
     { 
     ObjectFactory<I>::GetInstance().Register<T>(); 
     } 
    }; 

です。

regInFactory...静的オブジェクトは.hファイルで定義されているため、すべての翻訳単位に追加されるという問題があります。同じオブジェクト作成者が数回登録され、さらに重要なのは、静的な記憶期間を持つ多くの冗長オブジェクトが存在することです。

クラスやインターフェイスの名前をコピー/ペーストするのではなく、エレガントな登録を行う方法はありますか?冗長な静的オブジェクトを世界中に広げないでください。

VC++固有の拡張機能(C++標準に準拠していないもの)が必要な場合は、これで問題ありません。

答えて

5

ですから、ヘッダファイル内の変数の定義を入れたいですか?移植可能な方法があります:テンプレートクラスの静的変数。私たちは次のようになります:

template <typename T, typename I> 
struct AutoRegister: public I 
{ 
    // a constructor which use ourRegisterer so that it is instantiated; 
    // problem catched by bocco 
    AutoRegister() { &ourRegisterer; } 
private: 
    static RegisterClass<T, I> ourRegisterer; 
}; 

template <typename T, typename I> 
RegisterClass<T, I> AutoRegister<T, I>::ourRegisterer; 

class Foo: AutoRegister<Foo, IFoo> 
{ 
public: 
    virtual void Do();   
}; 

私はIFooの仮想を継承していないことに注意してください。そのクラスから何度も継承するリスクがある場合は、仮想である必要があります。

+0

素晴らしいアイデア!私はその方法を完全に忘れてしまった。しかし、あなたのバージョンはourRegistererが一度も使用されないので動作しないので、インスタンス化されません。 "AutoRegister(){&ourRegisterer;}"のようなコンストラクタを追加することも、明示的なインスタンス化 "template struct AutoRegister ;"を実行することもできます。あなたが気にしているなら、あなたの答えを更新してください:-) – bocco

2

なぜマクロを変更しないと、REGISTER_CLASSは宣言せずにクラスを登録するだけですか?

インターフェイスを実装している間に別のクラスから派生させることができます.1つのコンパイル単位内にある.cppファイルに登録することができます。ヘッダーの依存関係を最小限に抑えます。すべてのパブリックヘッダーのオブジェクトファクトリヘッダー)。

リンカーにグローバルのうちの1つだけを選択させ、未使用のものを破棄させるVC++拡張があります。明らかに

#define REGISTER_CLASS(className, interfaceName) \ 
    class className; \ 
    __declspec(selectany) \ 
    RegisterClass<className, interfaceName> regInFactory##className; \ 
    class className : public interfaceName 
+0

実際の作業を行うには、Create()やClone()のような便利なメソッドがいくつかあるので、ファクトリヘッダをインクルードする必要があります。 このオブジェクトファクトリはオブジェクト階層のためのものなので、開発者が階層に追加されたときに新しいオブジェクトを登録することを忘れないようにしたいと思います。 selectanyに感謝します!私のリンカオプションがそれに適していることを願っています。 MSDNを掘り起こす: – bocco

1

__declspec(selectany) 

使い方は次のように次のようになります。問題の変数は、(コンパイラが拡張をサポートしていない場合、リンカエラーの原因となる、=なし静的)グローバル見えるようにする必要がありますクラスレジストラ宣言を.cppファイルに移動するだけで済みます。おそらく、そのレジストラに登録するクラスの実装を含む.cppファイルである必要があります。

レジストラクラスは実際には必要ありません。インスタンスを1つだけ必要とします。 .cppファイルに入れることで、他のすべてのソースから完全に隠しますが、起動時にグローバルオブジェクトが初期化されます。

+0

それは登録から宣言を分けるが、それは – Eugene

+0

私はそれに問題が表示されません。 – sharptooth

+0

このクラスはこの工場に登録する必要があります。したがって、宣言と同時に登録を行う方が便利で安全です。 – bocco

3

Cygonの答えはおそらくあなたが必要としているものですが、あなたはまた、登録されているものに応じて、怠惰な登録を取り除くかもしれません。

特殊な基本クラスに登録を移動し、マクロの中にインターフェースを追加します。

内側のコンストラクタではローカルスタティックフラグを使用しているため、最初のインスタンス作成時に各クラスが1回だけ登録されます。

はおそらくコンパイルされませんが、あなたは私が何を意味するのかを取得する必要があります

template<className, interfaceName> 
class RegistratorBase 
{ 
public: 
    RegistratorBase() 
    { 
      static bool registered = false; 
      if(!registered) 
       ObjectFactory<interfaceName>::GetInstance().Register<className>(); 
    } 
}; 

#define REGISTER_CLASS(className, interfaceName) \ 
    class className : public interfaceName, private RegistratorBase<className, interfaceName> 
+0

面白い音。ありがとう – bocco

+0

私はこのソリューションが本当に好きです。私は今でもマルコの必要性があるとは思わない。 – iain

関連する問題