[解決済み] クラス名を持つ文字列からオブジェクトをインスタンス化する方法はありますか?
質問
ファイルを持っています。Base.h
class Base;
class DerivedA : public Base;
class DerivedB : public Base;
/*etc...*/
と別のファイル。BaseFactory.h
#include "Base.h"
class BaseFactory
{
public:
BaseFactory(const string &sClassName){msClassName = sClassName;};
Base * Create()
{
if(msClassName == "DerivedA")
{
return new DerivedA();
}
else if(msClassName == "DerivedB")
{
return new DerivedB();
}
else if(/*etc...*/)
{
/*etc...*/
}
};
private:
string msClassName;
};
/*etc.*/
この文字列を実際の型(クラス)に変換する方法はないでしょうか。そうすれば BaseFactory は派生クラスの可能性をすべて把握し、それぞれのクラスに対して if() を持つ必要がなくなるでしょう?この文字列からクラスを生成することはできますか?
これはC#ではReflectionでできると思います。C++でも同じようなことができるのでしょうか?
どのように解決するのですか?
いや、自分でマッピングをしない限りは無理だ。C++には、実行時に型が決定されるオブジェクトを作成する仕組みがないのです。しかし、マップを使って自分でマッピングを行うことは可能です。
template<typename T> Base * createInstance() { return new T; }
typedef std::map<std::string, Base*(*)()> map_type;
map_type map;
map["DerivedA"] = &createInstance<DerivedA>;
map["DerivedB"] = &createInstance<DerivedB>;
そして、次のようにします。
return map[some_string]();
新しいインスタンスを取得する。もう一つのアイデアは、型を自分で登録させることです。
// in base.hpp:
template<typename T> Base * createT() { return new T; }
struct BaseFactory {
typedef std::map<std::string, Base*(*)()> map_type;
static Base * createInstance(std::string const& s) {
map_type::iterator it = getMap()->find(s);
if(it == getMap()->end())
return 0;
return it->second();
}
protected:
static map_type * getMap() {
// never delete'ed. (exist until program termination)
// because we can't guarantee correct destruction order
if(!map) { map = new map_type; }
return map;
}
private:
static map_type * map;
};
template<typename T>
struct DerivedRegister : BaseFactory {
DerivedRegister(std::string const& s) {
getMap()->insert(std::make_pair(s, &createT<T>));
}
};
// in derivedb.hpp
class DerivedB {
...;
private:
static DerivedRegister<DerivedB> reg;
};
// in derivedb.cpp:
DerivedRegister<DerivedB> DerivedB::reg("DerivedB");
登録用のマクロを作成することにしました。
#define REGISTER_DEC_TYPE(NAME) \
static DerivedRegister<NAME> reg
#define REGISTER_DEF_TYPE(NAME) \
DerivedRegister<NAME> NAME::reg(#NAME)
でも、この2つにはもっといい名前があるはずです。もうひとつ、ここで使うのが理にかなっていると思われるのが
shared_ptr
.
共通のベースクラスを持たない無関係な型の集合がある場合、関数ポインタの戻り値の型に
boost::variant<A, B, C, D, ...>
代わりに 例えば、Foo, Bar, Bazというクラスがあった場合、次のようになります。
typedef boost::variant<Foo, Bar, Baz> variant_type;
template<typename T> variant_type createInstance() {
return variant_type(T());
}
typedef std::map<std::string, variant_type (*)()> map_type;
A
boost::variant
はユニオンのようなものです。どのオブジェクトが初期化または代入に使われたかを調べることで、どの型がそこに格納されているかを知ることができます。そのドキュメントを見てみましょう。
ここで
. 最後に、生の関数ポインタの使用も少し古臭いです。現代のC++コードは、特定の関数/型から切り離されているはずです。次のようなことを調べるとよいでしょう。
Boost.Function
を参照して、より良い方法を探してください。そうすると、こんな感じになります(地図)。
typedef std::map<std::string, boost::function<variant_type()> > map_type;
std::function
は、次のバージョンの C++ でも利用できるようになります。
std::shared_ptr
.
関連
-
[解決済み】getline()が何らかの入力の後に使用されると動作しない 【重複あり
-
[解決済み】クラステンプレートの引数リストがない
-
[解決済み】C++ - 解放されるポインタが割り当てられていないエラー
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】Visual C++で "Debug Assertion failed "の原因となる行を見つける。
-
[解決済み] 非静的データメンバの無効な使用
-
[解決済み] 解決済み] `pthread_create' への未定義の参照 [重複] [重複
-
[解決済み】Enterキーを押して続行する
-
[解決済み] なぜList<T>を継承しないのですか?
-
[解決済み】プロトタイプベース継承とクラスベース継承の比較
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】coutはstdのメンバではない
-
[解決済み】非静的メンバ関数への参照を呼び出す必要がある
-
[解決済み】C++のGetlineの問題(オーバーロードされた関数 "getline "のインスタンスがない
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み】関数名の前に期待されるイニシャライザー
-
[解決済み】テンプレートの引数1が無効です(Code::Blocks Win Vista) - テンプレートは使いません。
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み】Enterキーを押して続行する
-
[解決済み】警告 - 符号付き整数式と符号なし整数式の比較