Looking for a way to avoid a massive IF/ELSE and use a lookup table to resolve strings to particular classes to instantiate, that all derive from a base class. Is something like this possible, and if so, how?
typedef struct BaseClass
{
} BaseClass;
typedef struct DerivedClassOne : BaseClass
{
} DerivedClassOne;
typedef struct DerivedClassTwo : BaseClass
{
} DerivedClassTwo;
typedef struct
{
const char *name;
BaseClass class;
} LookupList;
LookupList list[] = {
{"ClassOne", DerivedClassOne},
{"ClassTwo", DerivedClassTwo}
};
BaseClass *InstantiateFromString(char *name)
{
int i;
for (i = 0; i < 2; i++)
{
if (!strcmp(name, list[i].name))
return new list[i].class();
}
}
int main (int argc, char *argv[])
{
BaseClass *myObjectFromLookup = InstantiateFromString("ClassOne");
}
If your compiler is compatible with C++11, you can easily do that with lambdas and std::map
:
#include <iostream>
#include <string>
#include <map>
#include <functional>
using namespace std;
struct BaseClass {virtual void foo()=0;};
struct DerivedClass1 : public BaseClass {void foo() {cout << "1" << endl;}};
struct DerivedClass2 : public BaseClass {void foo() {cout << "2" << endl;}};
// Here is the core of the solution: this map of lambdas does all the "magic"
map<string,function<BaseClass*()> > factory {
{"one", [](){return new DerivedClass1();}}
, {"two", [](){return new DerivedClass2();}}
};
int main() {
BaseClass *a = factory["one"](); // Note the function call () at the end
BaseClass *b = factory["two"]();
a->foo();
b->foo();
delete a;
delete b;
return 0;
}
The idea is to make a map that gives you a function that makes an appropriate subclass.
Demo on ideone.
First of all, a syntax primer:
struct Base {
virtual ~Base() {} // do not forget this if you need polymorphism
};
Then, a "factory" function:
template <typename T>
std::unique_ptr<Base> makeBase() { return std::unique_ptr<Base>(new T{}); }
The type of this function is:
using BaseMaker = std::unique_ptr<Base>(*)();
And finally, putting it altogether:
struct DerivedOne: Base {}; struct DerivedTwo: Base {};
using BaseMakerMap = std::map<std::string, BaseMaker>;
BaseMakerMap const map = { { "DerivedOne", makeBase<DerivedOne> },
{ "DerivedTwo", makeBase<DerivedTwo> } };
std::unique_ptr<Base> makeFromName(std::string const& n) {
BaseMakerMap::const_iterator it = map.find(n);
if (it == map.end()) { return std::unique_ptr<Base>(); } // not found
BaseMaker maker = it->second;
return maker();
}
You should be able to do the following:
template<class C>
BaseClass * makeObject<C> () {
return new C;
}
struct LookupList {
const char* name;
BaseClass * (*factoryFunction) ();
};
LookupList list [] = {
{"ClassOne", &makeObject<DerivedClassOne>},
{"ClassTwo", &makeObject<DerivedClassTwo>}
};
...
... instantiateFromString ...
return list[i].factoryFunction ();
I would, however, prefer a Map over an array for the LookupList
. In addition, you might want to get familiar with the functional syntax in C++11.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With