1

I have a very simple C++ lookup table for dispatching commands:

template <class T> Action* CreateAction(Command *c)
{
    return new T(c);
}

typedef Action* CreateActionFunc(Command *c);

typedef struct ActionTable {
    string name;
    CreateActionFunc *func;
} ActionTableEntry;

vector<ActionTableEntry> GlobalActionTable = {

    { "quit"      , &CreateAction<DoQuit> },
};

This works fine, but I would rather have my CreateAction function construct the new object on the stack and return it by value. But when I write this:

template <class T> T CreateAction(Command *c)
{
    return T(c);
}

typedef Action CreateActionFunc(Command *c);

Then the program will no longer compile. First I get an error that an abstract class cannot be instantiated (on the typedef line) and also an error that the initialization list for the table doesn't match the type of the vector.

There is a very similar question here but every answer uses new in the factory methods, which is explicitly what I'm trying to avoid. How can this be done?

Community
  • 1
  • 1
Segfault
  • 8,036
  • 3
  • 35
  • 54

3 Answers3

1

The Action sub class has more information than the Action class itself - pointers to a table of it's member function, data members etc. There's not enough memory to hold this information if you return by value. Something called slicing would occur.

This answer explains it better.


How about doing something like this instead:

class Action {
    void do_something(Command& params) = 0;
};
class SayHello {
    void do_something(Command& params) { std::cout << "Hi!" << std::endl; }
}
class SayBye {
    void do_something(Command& params) { std::cout << "Goodbye." << std::endl; }
}

.....

SayHello hello;
SayBye   bye;
Quit     quit;
std::map<string, Action&> action_table = {
    {"hello", hello},
    {"bye", bye},
    {"quit", quit},
};

....

Action& getAction(Command* command) {
   ...;
   return action_from_map;
}

This creates the action once, and returns them by reference.

Community
  • 1
  • 1
nishantjr
  • 1,788
  • 1
  • 15
  • 39
  • Yes, that makes sense. I don't really want to return the (abstract) Action type anyway, I want to return an instance of one of the concrete subclasses.... but how can I write the lookup table correctly to do this? Can I use lambdas or functors? Is there a syntax for a typedef for a function pointer that can point to any specialization of a template function? – Segfault Jun 05 '14 at 15:14
  • 1
    You can't have a function pointer that can point to any specialization of template. The template type has to be specified. – excray Jun 05 '14 at 16:58
  • @excray Which template? I removed all templates. I think that just inheritance will work in this case. – nishantjr Jun 05 '14 at 23:05
1

You can't use polymorphism with objects by value. Need to be pointers or reference. I'm guessing here you have an Action interface (so an abstract class), so you can't create an object of this dynamic type. All you can do is send a pointer of type Action with a dynamic type of a Derived Class (so what you are already doing i assume). You could create a value object of a derived type on the stack and return a reference on the Base class and still use polymorphism, but then you'll need to address the lifetime of the Derived object problem.

Kiroxas
  • 891
  • 10
  • 24
1

What about something simple like this?

std::map<string, std::function<void(CommandArgs const&)>> action_table = 
{
    {"hello", [](CommandArgs const& args) { /* do something */ }},
};
Andriy Tylychko
  • 15,967
  • 6
  • 64
  • 112