2012-06-24 13 views
5

実行時に拡張機能を動的にロードするアプリケーションを作成しようとしています。 Boost Preprocessorライブラリを使用して、名前のリストが与えられ、それぞれの名前のクラスを宣言し(そして、それらのすべてをいくつかのAbstractPluginクラスのサブクラスにする)、そのクラスを含むBoost MPLシーケンスを宣言するプリプロセッサ関数を書きました。それから、そのMPLシーケンス内のどのタイプにもキャストできる場合、AbstractPluginへのポインタを試すクラスを作成しました。 ここでの問題は、私のプリプロセッサ関数が、作成してロードするすべての拡張機能の完全なリストを必要としていることです。各拡張機能を別々のファイルに登録できるいくつかの方法がありますか?C++クラスを登録して、後で関数が登録されたすべてのクラスを反復処理できるようにする

更新:

私は信じて、状況の私の説明はあまりにも漠然としたので、私はそれをより具体的にすることにしました。

拡張タイプのコレクションを定義したいと思います。各拡張タイプには、任意の数の拡張が存在する可能性があります。実行時に、プログラムは外部ライブラリをロードし、エントリポイント関数を解決し、それを呼び出し、結果としてポインタを取得します。次に、登録されたすべてのエクステンションタイプにポインタをキャストしようとします(dynamic_castを使用しているため、エクステンションタイプのクラスはすべて、多態ベースクラスから継承されます)。ある拡張タイプへのキャストが成功すると、その拡張タイプの特殊ハンドラの呼び出しでキャストされたポインタが使用されます。

コンパイル時に拡張タイプの数が分かります(ただし、拡張の数は無限です)。私のaproachを使用すると、loaderクラスはこの知識を使用して、各拡張タイプのハンドラが存在するかどうかをチェックします(そうでなければ、プログラムはコンパイルされません)。また、私のaproachは、エクステンション型のクラスを強制的にロードしないので、ローダーを変更するのは簡単です。しかし、各エクステンションタイプが登録されていると便利です。

+0

ヘッダーを受け入れ可能な解決策にしていますか? – Arpegius

答えて

0

私が求めていることは判りません。その理由は、この文脈での「レジスタ」は「型を型の中に入れる」ことを意味し、型のシーケンスは型自体であるため不変です。したがって、このタイプのシーケンスを手動で作成するか、または「登録」を実行時に移動するように提案されている人がいます。

0

抽象的なファクトリパターンを使用してそのような拡張フレームワークを実装することは困難ではありません。

http://en.wikipedia.org/wiki/Abstract_factory_pattern

あなたはグローバルリスト内の抽象ファクトリ関数/オブジェクトを登録し、あなたがそれにベースをやりたいものは何でも行うことができます。

8

すべてのクラスを何らかのコレクションで自己登録することができます。

Base.hpp:

#include <memory> 
#include <unordered_map> 
#include <string> 

struct Base 
{ 
    virtual ~Base() = default; 

    using create_f = std::unique_ptr<Base>(); 

    static void registrate(std::string const & name, create_f * fp) 
    { 
     registry()[name] = fp; 
    } 

    static std::unique_ptr<Base> instantiate(std::string const & name) 
    { 
     auto it = registry().find(name); 
     return it == registry().end() ? nullptr : (it->second)(); 
    } 

    template <typename D> 
    struct Registrar 
    { 
     explicit Registrar(std::string const & name) 
     { 
      Base::registrate(name, &D::create); 
     } 
     // make non-copyable, etc. 
    }; 

private: 
    static std::unordered_map<std::string, create_f *> & registry(); 
}; 

Base.cpp:今

#include "Base.hpp" 

std::unordered_map<std::string, Base::create_f *> & Base::registry() 
{ 
    static std::unordered_map<std::string, Base::create_f *> impl; 
    return impl; 
} 

クライアントでこれを使用する:

は派生ここでスケルトンアプローチがあります。 hpp:

#include "Base.hpp" 

struct Derived : Base 
{ 
    static std::unique_ptr<Base> create() { return std::make_unique<Derived>(); } 
    // ... 
}; 

Derived.cpp:

#include "Derived.hpp" 

namespace 
{ 
    Base::Registrar<Derived> registrar("MyClass"); 
} 

Base::Registrar<Derived>のコンストラクタは、名前"MyClass"下クラスDerivedを登録するの面倒を見ます。私たちは、私が実際に作る任意の静的初期化順序の問題を回避する方法に注意してくださいコードは/など、使用可能なクラスのリストを印刷し、繰り返し登録を検出することにより、改善されなければならない可能性が

std::unique_ptr<Base> p = Base::instantiate("MyClass"); 

:あなたは経由で動的に Derivedのインスタンスを作成することができますレジストリマップオブジェクトはブロックスタティックオブジェクトで、最初に使用する前に初期化され、最後に使用された後にのみ破棄されることが保証されています。

+0

私はこのようなことを考えましたが、私はコンパイル時にもっと興味があります。私はそこに1つが存在するかどうかは確信していませんが、それでもC++テンプレートの土地は奇跡でいっぱいです。 – grisumbras

+0

静的なものはありません。各実装ファイルには独自のコンパイル単位があり、それぞれ異なるライブラリに置くことができるため、クラスを自分で登録することはできません。このソリューションの強みは、動的ライブラリを読み込むと、拡張クラスが自動的に利用できるようになったことです。それはよく知られており、非常に有用なパターンです。 – Arpegius

+0

これはとても良い実装です。私が理解できない唯一の事は静的なcreate()関数の= 0です。私が知る限り、それは静的関数は決して仮想ではないので、無効なC++です。その部分はどのように機能するはずですか? – Shenjoku

関連する問題