minimal reflection in C++
I want to create a class factory and I would like to use reflection for that. I just need to create a object with given string and invoke only few known met开发者_运维技巧hods.
How i can do that?
You will have to roll your own. Usually you have a map of strings to object creation functions.
You will need something like the follwing:
class thing {...};
/*
class thing_A : public thing {...};
class thing_B : public thing {...};
class thing_C : public thing {...};
*/
std::shared_ptr<thing> create_thing_A();
std::shared_ptr<thing> create_thing_C();
std::shared_ptr<thing> create_thing_D();
namespace {
typedef std::shared_ptr<thing> (*create_func)();
typedef std::map<std::string,create_func> creation_map;
typedef creation_map::value_type creation_map_entry;
const creation_map_entry creation_map_entries[] = { {"A", create_thing_A}
, {"B", create_thing_B}
, {"C", create_thing_C} };
const creation_map creation_funcs(
creation_map_entries,
creation_map_entries + sizeof(creation_map_entries)
/ sizeof(creation_map_entries[0] );
}
std::shared_ptr<thing> create_thing(const std::string& type)
{
const creation_ma::const_iterator it = creation_map.find(type);
if( it == creation_map.end() ) {
throw "Dooh!"; // or return NULL or whatever suits you
}
return it->second();
}
There are other ways to do this (like having a map of strings to objects from which to clone), but I think they all boil down to having a map of strings to something related to the specific types.
There is no reflection in C++, directly supported by the standard.
However C++ is sufficiently low-level that you can implement some minimal support for reflection to complete the task at hand.
For the simple task of creating a Factory, you usually use the Prototype
approach:
class Base
{
public:
virtual Base* clone() const = 0;
virtual ~Base();
};
class Factory
{
public:
std::unique_ptr<Base> get(std::string const& name);
void set(std::string const& name, std::unique_ptr<Base> b);
private:
boost::ptr_map<std::string,Base> mExemplars;
};
Of course, those "known methods" that you are speaking about should be defined within the Base
class, which acts as an interface.
There is no reflection in C++, so you should restate your question trying to explain what are the requirements that you would have fulfilled with the reflection part of it.
Depending on your actual constraints and requirements, there are a few things that you can do. The first approach that I would take would be creating an abstract factory where concrete factories can register and provide a simple interface:
class Base {}; // shared base by all created objects
class ConcreteFactoryBase {
public:
virtual ~ConcreteFactoryBase() {}
virtual Base* create() const = 0; // actual construction
virtual std::string id() const = 0; // id of the types returned
};
class AbstractFactory
{
typedef std::map<std::string, ConcreteFactory* > factory_map_t;
public:
void registerFactory( ConcreteFactoryBase* factory ) {
factories[ factory->id() ] = factory;
}
Base* create( std::string const & id ) const {
factory_map_t::const_iterator it = factories.find( id );
if ( it == factories.end() ) {
return 0; // or throw, or whatever makes sense in your case
}
return (*it)->create();
}
~AbstractFactory(); // ensure that the concrete factories are deleted
private:
std::map<ConcreteFactoryBase*> factories;
};
The actual concrete factories can be implemented manually but they can probably be templated, unless the constructors for the different types require different arguments:
template <typename T>
class ConcreteFactory : public ConcreteFactoryBase {
public:
ConcreteFactory( std::string const & id ) : myid(id) {}
virtual Base* create() const {
return new T;
}
virtual std::string id() const {
return myid;
}
private:
std::string myid;
};
class Test : public Base {};
int main() {
AbstracFactory factory;
factory.register_factory( new ConcreteFactory<Test>("Test") );
}
Optionally you could adapt the signatures so that you can pass arguments to the constructor through the different layers.
Then again, by knowing the actual constraints some other approaches might be better. The clone()
approach suggested elsewhere is good (either by actually cloning or by creating an empty object of the same type). That is basically blending the factory with the objects themselves so that each object is a factory of objects of the same type. I don't quite like mixing those two responsabilities but it might be one of the simplest approaches with less code to write.
You could use typeid & templates to implement the factory so you won't need strings at all.
#include <string>
#include <map>
#include <typeinfo>
//***** Base *****
class Base
{
public:
virtual ~Base(){} //needs to be virtual to make typeid work
};
//***** C1 *****
class C1 : public Base
{};
//***** Factory *****
class Factory
{
public:
template <class T>
Base& get();
private:
typedef std::map<std::string, Base> BaseMap;
BaseMap m_Instances;
};
template <class T>
Base& Factory::get()
{
BaseMap::const_iterator i = m_Instances.find(typeid(T).name());
if(i == m_Instances.end()) {
m_Instances[typeid(T).name()] = T();
}
return m_Instances[typeid(T).name()];
}
//***** main *****
int main(int argc, char *argv[])
{
Factory f;
Base& c1 = f.get<C1>();
return 0;
}
精彩评论