Extensible way to access class information at runtime

I am making a simple messaging system. I have an Entity descriptor table connected to a factory to subclass Entity at runtime, and I would like to have it so that they can be created line by line:

EntityManager manager; //managers have all of the entity table information (See below) //Counter is a sample class that inherits from Entity Counter* counter = manager.makeEntity("Counter"); //the string doesn't have to match the class name. 

Now I know that I can do a simple switch statement, but I would like the system to be extensible, that is, when I (or other users of my system) want to create a new Entity subclass, I don’t have to go to the switch block and add it . I am currently using macros to create helper classes that I create statically, so the constructor adds an entry to the entity table. These classes also initialize entities and remove many templates from constructors.

 //EHandle is a wrapper for Entity*. Currently std::shared_ptr<Entity> class GenericDesc { public: virtual ~GenericDesc() {} virtual EHandle makeEntity() const =0; }; namespace Descriptor { //Adds a descriptor to an internal map<string, GenericDesc*> void addEntityDescriptor(const std::string& type, GenericDesc& desc); EHandle newEntity(const std::string& type); //Factory method } //Add this to every entity class definition #define DECLARE_ENTITY_CLASS(CLASS_NAME) \ friend class CLASS_NAME##Descriptor; //Use these after a class definition to add the entity class to the descriptor table #define BEGIN_ENTITY_TYPE(ENTITY_NAME, CLASS_NAME, BASE_NAME) \ BEGIN_ENTITY_TYPE_GUTS(ENTITY_NAME, CLASS_NAME) \ BASE_NAME##Descriptor::prepareEntity(ent); #define BEGIN_ENTITY_TYPE_BASELESS(ENTITY_NAME, CLASS_NAME) \ BEGIN_ENTITY_TYPE_GUTS(ENTITY_NAME, CLASS_NAME) \ ent->self = ent; #define BEGIN_ENTITY_TYPE_GUTS(ENTITY_NAME, CLASS_NAME) \ class CLASS_NAME##Descriptor : public GenericDesc \ { \ private: \ typedef CLASS_NAME ClassName; \ public: \ CLASS_NAME##Descriptor() \ { \ Descriptor::addEntityDescriptor(ENTITY_NAME, *this); \ } \ virtual ~CLASS_NAME##Descriptor() {} \ virtual EHandle makeEntity() const\ { \ auto ent = std::shared_ptr<CLASS_NAME>(new CLASS_NAME); \ prepareEntity(ent); \ ent->type = ENTITY_NAME; \ return ent; \ } \ static void prepareEntity(std::shared_ptr<ClassName> ent) \ { //These functions are caled between BEGIN_ENTITY_TYPE and END_ENTITY_TYPE //ADD_ENTITY_INPUT binds a function to a string #define ADD_ENTITY_INPUT(INPUT_NAME, INPUT_FUNC) \ ent->addInput(INPUT_NAME, std::bind(&ClassName::INPUT_FUNC, ent, std::placeholders::_1)); //ADD_ENTITY_OUTPUT binds an Output object to a string #define ADD_ENTITY_OUTPUT(OUTPUT_NAME, OUTPUT_OBJECT) \ ent->addOutput(OUTPUT_NAME, ent->OUTPUT_OBJECT); #define END_ENTITY_TYPE(CLASS_NAME) \ } \ }; \ static CLASS_NAME##Descriptor CLASS_NAME##Desc; //TODO: find a way to fix the multiple-static-allocation issue 

The idea is that you create a BEGIN_ENTITY_TYPE (...) END_ENTITY_TYPE (...) condition, in which the ADD_ENTITY_x bit is in the middle. My question is whether there is a way to do this with smaller macros, which still minimizes the template and does not require changing any files outside the one defined by the Entity subclass. The template class may work, but I do not know how I would do ADD_ENTITY_INPUT / OUTPUT with the template class.

+4
source share
1 answer

This may not be exactly what you need, but think about something like:

 class Factory { public: virtual void InitialiseFactory() = 0; Entity* CreateEntity( string type ) { // (obviously, you must handle the string not being found.) return m_MapEntities[ type ]->Clone(); } }; class MyFactory : public Factory { public: void InitialiseFactory() { m_MapEntities["typea"] = new MyEntity(); } }; 

The idea here is that the search is concretized in a base class, which can remain unchanged, but the specificity of which objects are provided lies in the implementation of the derived class.

Another way would be for a static method in every Entity making a factory call that needs to be added, but this method still needs to be called from somewhere.

0
source

Source: https://habr.com/ru/post/1398667/


All Articles