C ++使用接口的命令行动作抽象

时间:2011-02-14 23:24:06

标签: c++ design-patterns boost stl command-line-arguments

我正在构建一个应用程序,其用法如下所示:

application --command --option1=? --option2=2?

基本上,可以有任意数量的选项,但每个应用程序实例只能有一个命令。类似于git的工作方式。

现在,我以为我会用C ++编写它来获得一些提升和体验,并且我会继续阅读一些我一直在阅读的设计模式。所以,我实现了这个:

class Action
{
public:
    void AddParameter(std::string key, boost::any p);
    virtual unsigned int ExecuteAction();

protected:
    std::map<std::string, boost::any> parameters;
};

无论如何我会解释我的逻辑,只是为了检查它 - 这是一个抽象的行动。所有操作都需要添加选项,因此参数映射,以便我们可以在此级别实现,但我们希望ExecuteAction由派生类实现,例如我的简单示例DisplayHelpAction,它几​​乎可以实现它在锡上说的是什么。

所以现在我写了一个工厂,就像这样:

class DetermineAction
{
public:
    DetermineAction();
    vx::modero::Action getAction(std::string ActionString);
private:
    std::map<std::string, vx::modero::Action> cmdmap;
};

逻辑是构造函数将创建一个可以请求的可能字符串的映射,getAction将执行它所说的 - 给它一个命令字符串,它将为您提供一个派生自Action的类它实现了所需的功能。

我遇到了那个构造函数的问题。我正在尝试这个:

this->cmdmap = std::map<std::string, Action>();
this->cmdmap.insert(pair<string, Action>("help", DisplayHelpAction()));
this->cmdmap.insert(pair<string, Action>("license", DisplayLicenseAction()));

这导致了很多错误。现在,我习惯了Java的接口方式,所以你使用:

Interface I = new ConcreteClass();

和Java喜欢它。所以这就是我想在这里实现的那种想法,因为我想要实现的getAction就是:

return this->cmdmap[ActionString];

哪个应该返回一个派生自Action的类,然后我就可以开始添加参数并调用execute。

总而言之,我有两个密切相关的问题:

  • 音板。我故意练习抽象的东西,所以那里有一些额外的复杂性,但原则上,我的方法是否合理?我错过了一条非常明显的捷径吗?我应该使用更好的方法吗?
  • 如何设置我的类映射解决方案,以便我可以返回正确的类?具体投诉是链接时间,并且是:

    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'
    

仅仅因为它可能是相关的,我正在使用boost::program_options进行命令行解析。


修改1 :好的,我已根据Eugen的回答将Action替换为Action*,并尝试将new SomethingThatSubclassesAction添加到地图中。我仍然得到vtable错误。

1 个答案:

答案 0 :(得分:3)

  1. 需要立即说明的一点是,运行时多态性在C ++中通过指向基类的指针而不是值。因此,当std::map<std::string, Action>复制到std::map<std::string, Action*>时,您的DisplayHelpAction必须为map或您的衍生操作(即Action*)为sliced。存储map也意味着您在完成后需要明确地处理释放boost::ptr_map<std::string,Action>值。 注意:您可以使用boost::ptr_mapstd::map<std::string,boost::shared_ptr<Action> >)(如@Fred Nurk指出的那样)或boost::shared_ptrAction*)来担心关于明确释放分配的Action* getAction(std::string ActionString); 关于'Action getAction(std :: string ActionString);'的相同事情它需要成为virtual unsigned int ExecuteAction();

  2. 由于未提供virtual unsigned int ExecuteAction() = 0;的实现而导致链接器错误(最有可能)。另外我认为将其设为纯虚拟(Action)是有意义的 - 在这种情况下,您不需要为它提供实现。它还将为Action类的Java接口提供关闭语义。

  3. 除非你有充分的理由让boost:program_options派生的对象不知道整个std::map<std::string, boost::any>我会将其传递下来并让他们每个人直接访问它而不是构建{ {1}}。

  4. 我将DetermineAction重命名为ActionManagerActionHandler