Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

What about something simple like this?

std::map<string, std::function<void(CommandArgs const&)>> action_table = 
{
    {"hello", [](CommandArgs const& args) { /* do something */ }},
};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top