I\'m trying to create a structure array which links input strings to classes as follows:
struct {string command; CommandPath cPath;} cPathLookup[] = {
{\
Personally I don't see it as being a huge problem if you just have 1 factory for creating different "CommandPaths" for different values of the string it receives. Anyway, your code won't work because you can't store types the way you're trying.
If I had to do this, then for one I would use function pointers to factory functions and use a std::map to map strings to these, like shown in this code, and maybe wrap the pointers in an appropriate smart-pointer, instead of using raw pointers:
#include <string>
#include <map>
struct A {
};
struct B : public A {
};
struct C : public A {
};
A *BFactory(){
return new B();
}
A *CFactory(){
return new C();
}
typedef A *(*Factory)();
typedef std::pair<std::string,Factory> Pair;
typedef std::map<std::string,Factory> Map;
Pair Pairs[] =
{
std::make_pair( "alarm", BFactory ),
std::make_pair( "email", CFactory )
};
Map Lookup( Pairs, Pairs + sizeof(Pairs)/sizeof(*Pairs) );
A *CreateInstance( const std::string &Type )
{
Map::const_iterator i = Lookup.find( Type );
if( i != Lookup.end() )
return i->second();
else
return 0;
}
As for your question about pointers and abstract classes, you can have a pointer to an abstract class, but you can't instantiate an abstract class.
I assume you are trying to implement a table lookup as a replacement to a large if\else statement in your system.
For clarity I would go with a Factory design pattern instead. Having large if/else logic is only really bad if it is repeated around your code in many places. As long as it is in one place i.e. a Factory then in my opinion, you have a good design.
AlarmCommandPath and EmailCommandPath are derived from COmmandPath, correct?
In this case you cannot assign an instance of AlarmCommandPath/EmailCommandPath to CommandPath - it is technically possible, but it won't do what you want. The instance CommandPath will remain an instance of CommandPath (it will have virtual function table of CommandPath), no matter what you assign to it.
You need to use factory methods (a function that will return CommandPath*). Something like that:
struct A{
};
struct B: public A{
};
struct C: public A{
};
A* factoryA(){
return new A();
}
A* factoryB(){
return new B();
}
A* factoryC(){
return new C();
}
typedef A* (*FactoryMethod)();
struct{
const char* command;
FactoryMethod factoryMethod;
} factoryTable[] = {
{"A", factoryA},
{"B", factoryB},
{"C", factoryC},
{0,0}
};
You can't store a type in a struct, but you can store a pointer to a function that creates a type.
CommandPath * CreateEmail() {
return new EmailCommandPath;
}
CommandPath * CreateAlarm() {
return new AlarmCommandPath;
}
Then your struct looks like:
typedef Command * (* CreateFunc)();
struct MyMap {
string command;
CreateFunc func;
};
and the map:
MyMap m[] = {{"email", CreateEmail }, {"alarm", CreateAlarm}};
You then look up as before to get some index i, and use it:
CommandPath * p = m[i].func():
And you can create pointers to abstract types - you can't create instances of them.