Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ generic object factory by string name

Tags:

c++

I need a way to instantiate objects based on its class name passed by as a std::string. This is working right now, but need to be generalized:

void* create(std::string name) {
    if(name == "classOne") return new ClassOne();
    else if(name == "classTwo") return new ClassTwo();
    /* ... */
}

What i do not have:

  • Control over the classes to be instantiated: could be thirty party classes. No changes may be done to this classes (i.e. base ancestor, polymorphic creator method, etc...)
  • Full class name listing: more classes could be added later and should not incur in changes to this factory.
  • Wrappers around the classes to be instantiated: As a result of the previous two points.

Anything else is a go.

The best use case scenario will be:

int main() {
    void *obj = create("classTree"); // create object based on the string name
    /* ... */
    // once we know by context which specific class we are dealing with
    ClassTree *ct = (ClassTree*)obj; // cast to appropiate class
    std::cout << ct->getSomeText() << std::endl; // use object
}

As a side, and maybe irrelevant note, take in account the object to be instantiated may come from a class or a struct.

ADDED INFORMATION

I see more context is needed. Here is my particular use case, simplified:

// registration mechanism
int main() {
    std::map< std::string, void(*func)(std::string, void*) > processors; // map of processors by class name
    processors["ClassFour"] = (void(*)(std::string, void*)) &classFourMessageProcessor; // register processor (cast needed from specific to generic)
}
// function receiving string messages
void externalMessageHandler(std::string msg) {
    std::string objType = extractTypeFromMessageHeader(msg); // extract type from message
    // now that we know what we are dealing with, create the specific object
    void *obj = create(objType); // << creator needed
    processors[objType](msg, obj); // dispatch message to process
}
// previously registered message processor
void classFourMessageProcessor(std::String msg, ClassFour *obj) {
    std::string streetAddress = msg.substr(10, 15); // knowing the kind of message we can extract information
    obj->moveTheEtherTo(streetAddress); // use the created object
}

ADDED INFORMATION

I am using C++11 with the latest GNU compiler.

like image 338
yiown Avatar asked Sep 26 '13 18:09

yiown


1 Answers

You can just store a factory function for every class type. An easy way is to use a template

template <typename T>
void* creator() {
  return new T();
}

and store those in the map as well (i.e. "ClassFour" links to creator<ClassFour> and to ClassFourMessageProcessor).

Edit: for clarification, processors becomes a

typedef void* (*CreatorFunc)();
typedef void (*ProcessorFunc)(std::string, void*);

typedef std::pair<CreatorFunc, ProcessorFunc> Entry;
std::map< std::string, Entry > processors;

Adding a new class is as simple as

processors["SomeClass"] = Entry(creator<SomeClass>, ClassFourMessageProcessor);
like image 190
Alexander Gessler Avatar answered Dec 10 '22 23:12

Alexander Gessler