I'm building an application whose usage is going to look something like this:
application --command --option1=? --option2=2?
Basically, there can be any number of options, but only one command per instance of the application. Similar to the way git works.
Now, I thought I'd write it in C++ to get some boost and stl experience and have a go with a few of those design patterns I keep reading about. So, I implemented this:
class Action
{
public:
void AddParameter(std::string key, boost::any p);
virtual unsigned int ExecuteAction();
protected:
std::map<std::string, boost::any> parameters;
};
I'll explain my logic anyway, just to check it - this is an abstract-ish action. All actions need option adding, hence the parameters map, so that we can implement at this level, but we expect ExecuteAction
to be implemented by derived classes, such as my simple example DisplayHelpAction
, which does pretty much what it says on the tin.
So now I've written a factory, like so:
class DetermineAction
{
public:
DetermineAction();
vx::modero::Action getAction(std::string ActionString);
private:
std::map<std::string, vx::modero::Action> cmdmap;
};
The logic being that the constructor will create a map of possible strings you can ask for and getAction will do what it says - give it a command string and it'll give you a class that is derived from Action
which implements the desired functionality.
I'm having trouble with that constructor. I am trying this:
this->cmdmap = std::map<std::string, Action>();
this->cmdmap.insert(pair<string, Action>("help", DisplayHelpAction()));
this->cmdmap.insert(pair<string, Action>("license", DisplayLicenseAction()));
Which is causing a lot of errors. Now, I'm used to the Java Way of interfaces, so you use:
Interface I = new ConcreteClass();
and Java likes it. So that's the sort of idea I'm trying to achieve here, because what I want do have for the implementation of getAction
is this:
return this->cmdmap[ActionString];
Which should return a class derived from Action, on which I can then start adding parameters and call execute.
So, to summarise, I have two questions which are closely related:
- Soundboard. I'm deliberately practising abstracting things, so there's some additional complexity there, but in principle, is my approach sound? Is there an insanely obvious shortcut I've missed? Is there a better method I should be using?
How can I set up my class mapping solution so that I can return the correct class? The specific complaint is link-time and is:
Linking CXX executable myapp CMakeFiles/myapp.dir/abstractcmd.cpp.o: In function `nf::Action::Action()': abstractcmd.cpp:(.text._ZN2vx6modero6ActionC2Ev[_ZN2vx6modero6ActionC5Ev]+0x13): undefined reference to `vtable for nf::Action'
Just because it might be relevant, I'm using boost::program_options
for command line parsing.
Edit 1: Ok, I have now replaced Action
with Action*
as per Eugen's answer and am trying to add new SomethingThatSubclassesAction
to the map. I'm still getting the vtable error.