があるので、インスタンスを生成するオブジェから文字列を持が自分のクラスです。
-
06-09-2019 - |
質問
いファイル:ます。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ない可能なすべての派生クラスとしている場合()それぞれのしてみてはいかがだろうか。できるだけクラスからこの文字列は?
と思うことができるアプリケーションにより反射を生み出します。あう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クラス、バーやバズを持っている場合と同様に、それは次のようになります:
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;
boost::variant
は、労働組合のようなものです。これは、初期化やそれに割り当てるために使用したものをオブジェクト見ることで、それに格納されているタイプを知っています。ここをそのドキュメントを見てみましょう。最後に、生の関数ポインタの使用もビットやや古いです。現代のC ++コードは、特定の機能/タイプから切り離さなければなりません。あなたは探すために Boost.Function
のに見てみたいことがありますもっといい方法。それは次のようになります(マップ):
typedef std::map<std::string, boost::function<variant_type()> > map_type;
std::function
は、C ++、あまりにも、std::shared_ptr
を含む次のバージョンで利用できるようになります。
他のヒント
はありませんありません。この問題への私の好適な解決策は、作成方法に名前をマップする辞書を作成することです。辞書作成方法を登録し、このように作成することにしたいクラス。これは GoFのパターンブックの中でいくつかの詳細に議論されています。
その答えはできません。これで問題はなぜ:
私はC ++の工場について、別のSOの質問に答えてきました。柔軟な工場に関心がある場合がありを参照してください。私は私のために素晴らしい仕事をしているマクロを使用するET ++からの古い方法を記述してみます。
ET ++ には、C ++およびX11に移植古いMacAppのプロジェクトでした。その努力にはエリックガンマなどは、のデザインパターンの
について考え始めましたブ::機能は、工場のテンプレートが非常に柔軟性: http://www.boost.org/doc/libs/1_54_0/libs/functional/factory/doc/html/index.html
私の好みのものを生み出すラッパークラスを非表示にマッピングやオブジェクトを生成機構。共通のシナリオにまとの出会いは、地図の異なる導出クラスの基底クラスをキー場に由来クラスはすべて共通するコンストラクタ署名が可能です。こちらは液思っています。
#ifndef GENERIC_FACTORY_HPP_INCLUDED
//BOOST_PP_IS_ITERATING is defined when we are iterating over this header file.
#ifndef BOOST_PP_IS_ITERATING
//Included headers.
#include <unordered_map>
#include <functional>
#include <boost/preprocessor/iteration/iterate.hpp>
#include <boost/preprocessor/repetition.hpp>
//The GENERIC_FACTORY_MAX_ARITY directive controls the number of factory classes which will be generated.
#ifndef GENERIC_FACTORY_MAX_ARITY
#define GENERIC_FACTORY_MAX_ARITY 10
#endif
//This macro magic generates GENERIC_FACTORY_MAX_ARITY + 1 versions of the GenericFactory class.
//Each class generated will have a suffix of the number of parameters taken by the derived type constructors.
#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
#define BOOST_PP_ITERATION_LIMITS (0,GENERIC_FACTORY_MAX_ARITY)
#include BOOST_PP_ITERATE()
#define GENERIC_FACTORY_HPP_INCLUDED
#else
#define N BOOST_PP_ITERATION() //This is the Nth iteration of the header file.
#define GENERIC_FACTORY_APPEND_PLACEHOLDER(z, current, last) BOOST_PP_COMMA() BOOST_PP_CAT(std::placeholders::_, BOOST_PP_ADD(current, 1))
//This is the class which we are generating multiple times
template <class KeyType, class BasePointerType BOOST_PP_ENUM_TRAILING_PARAMS(N, typename T)>
class BOOST_PP_CAT(GenericFactory_, N)
{
public:
typedef BasePointerType result_type;
public:
virtual ~BOOST_PP_CAT(GenericFactory_, N)() {}
//Registers a derived type against a particular key.
template <class DerivedType>
void Register(const KeyType& key)
{
m_creatorMap[key] = std::bind(&BOOST_PP_CAT(GenericFactory_, N)::CreateImpl<DerivedType>, this BOOST_PP_REPEAT(N, GENERIC_FACTORY_APPEND_PLACEHOLDER, N));
}
//Deregisters an existing registration.
bool Deregister(const KeyType& key)
{
return (m_creatorMap.erase(key) == 1);
}
//Returns true if the key is registered in this factory, false otherwise.
bool IsCreatable(const KeyType& key) const
{
return (m_creatorMap.count(key) != 0);
}
//Creates the derived type associated with key. Throws std::out_of_range if key not found.
BasePointerType Create(const KeyType& key BOOST_PP_ENUM_TRAILING_BINARY_PARAMS(N,const T,& a)) const
{
return m_creatorMap.at(key)(BOOST_PP_ENUM_PARAMS(N,a));
}
private:
//This method performs the creation of the derived type object on the heap.
template <class DerivedType>
BasePointerType CreateImpl(BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& a))
{
BasePointerType pNewObject(new DerivedType(BOOST_PP_ENUM_PARAMS(N,a)));
return pNewObject;
}
private:
typedef std::function<BasePointerType (BOOST_PP_ENUM_BINARY_PARAMS(N,const T,& BOOST_PP_INTERCEPT))> CreatorFuncType;
typedef std::unordered_map<KeyType, CreatorFuncType> CreatorMapType;
CreatorMapType m_creatorMap;
};
#undef N
#undef GENERIC_FACTORY_APPEND_PLACEHOLDER
#endif // defined(BOOST_PP_IS_ITERATING)
#endif // include guard
私は反対の重いマクロを使用、また例外です。上記のコードを生成すGENERIC_FACTORY_MAX_ARITY+1バージョンという名前のクラスGenericFactory_N、Nは0GENERIC_FACTORY_MAX_ARITYイルが含まれます。
に発生するクラステンプレートは簡単です。また工場をBaseClass由来のものを使用したオブジェクトの文字列マッピングしました。それぞれの派生物3には整数としてコンストラクタのパラメータ
#include "GenericFactory.hpp"
typedef GenericFactory_3<std::string, std::shared_ptr<BaseClass>, int, int int> factory_type;
factory_type factory;
factory.Register<DerivedClass1>("DerivedType1");
factory.Register<DerivedClass2>("DerivedType2");
factory.Register<DerivedClass3>("DerivedType3");
factory_type::result_type someNewObject1 = factory.Create("DerivedType2", 1, 2, 3);
factory_type::result_type someNewObject2 = factory.Create("DerivedType1", 4, 5, 6);
のGenericFactory_Nクラスデストラクタは仮想できる。
class SomeBaseFactory : public GenericFactory_2<int, BaseType*, std::string, bool>
{
public:
SomeBaseFactory() : GenericFactory_2()
{
Register<SomeDerived1>(1);
Register<SomeDerived2>(2);
}
};
SomeBaseFactory factory;
SomeBaseFactory::result_type someObject = factory.Create(1, "Hi", true);
delete someObject;
なお、このラインの一般的な工場発生装置マクロ
#define BOOST_PP_FILENAME_1 "GenericFactory.hpp"
想定した汎用工場のヘッダファイル名をGenericFactory.hpp
オブジェクトを登録し、文字列名とそれらにアクセスするための詳細ソリューションます。
common.h
ます:
#ifndef COMMON_H_
#define COMMON_H_
#include<iostream>
#include<string>
#include<iomanip>
#include<map>
using namespace std;
class Base{
public:
Base(){cout <<"Base constructor\n";}
virtual ~Base(){cout <<"Base destructor\n";}
};
#endif /* COMMON_H_ */
test1.h
ます:
/*
* test1.h
*
* Created on: 28-Dec-2015
* Author: ravi.prasad
*/
#ifndef TEST1_H_
#define TEST1_H_
#include "common.h"
class test1: public Base{
int m_a;
int m_b;
public:
test1(int a=0, int b=0):m_a(a),m_b(b)
{
cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
}
virtual ~test1(){cout <<"test1 destructor\n";}
};
#endif /* TEST1_H_ */
3. test2.h
#ifndef TEST2_H_
#define TEST2_H_
#include "common.h"
class test2: public Base{
int m_a;
int m_b;
public:
test2(int a=0, int b=0):m_a(a),m_b(b)
{
cout <<"test1 constructor m_a="<<m_a<<"m_b="<<m_b<<endl;
}
virtual ~test2(){cout <<"test2 destructor\n";}
};
#endif /* TEST2_H_ */
main.cpp
ます:
#include "test1.h"
#include "test2.h"
template<typename T> Base * createInstance(int a, int b) { return new T(a,b); }
typedef std::map<std::string, Base* (*)(int,int)> map_type;
map_type mymap;
int main()
{
mymap["test1"] = &createInstance<test1>;
mymap["test2"] = &createInstance<test2>;
/*for (map_type::iterator it=mymap.begin(); it!=mymap.end(); ++it)
std::cout << it->first << " => " << it->second(10,20) << '\n';*/
Base *b = mymap["test1"](10,20);
Base *b2 = mymap["test2"](30,40);
return 0;
}
(Eclipseのでこれを行っている)、それをコンパイルし、実行します。
出力:
Base constructor
test1 constructor m_a=10m_b=20
Base constructor
test1 constructor m_a=30m_b=40
のJavaのような反射を意味。 ここではいくつかの情報があります: http://msdn.microsoft.com/en-us /library/y0114hz2(VS.80).aspxする
一般的に言えば、 "C ++反射"
をGoogleで検索TorのBredeのVekterliは正確にあなたが求める機能を提供しますブースト拡張を提供します。現在のところ、それは現在のブーストLIBSと少し厄介なフィッティングですが、私はそれがそのベースの名前空間を変更した後1.48_0での作業を取得することができました。
(反射など)そのようなことは、C ++のために有用であろう理由を問う人たちへの答えに - 私は、UIとエンジンとの間の相互作用のためにそれを使用する - ユーザーがUIでオプションを選択し、エンジンがUIを取ります選択文字列、及び所望のタイプのオブジェクトを生成します。
(どこかの果物・リストを維持する上で)ここでフレームワークを使用することの主な利点は、登録機能は、各クラスの定義である(とのみ登録クラスごとの登録機能を呼び出す1行のコードが必要)ということである - とは対照的に、手動で新しいクラスを派生されるたびに追加する必要があり、果実リストを含むファイル。
私は工場出荷時の私の基本クラスの静的メンバを作っています。
この工場出荷時のパターンです。ウィキペディア(およびこのの一例)を参照してください。あなたは、いくつかの悪質なハッキングのない文字列からそれ自体の種類を作成することはできません。なぜあなたはこれが必要なのでしょうか?