使用界面C ++命令行操作的抽象 [英] C++ Command line action abstraction using interface

查看:155
本文介绍了使用界面C ++命令行操作的抽象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我建立一个应用程序使用的是要看起来像这样:

 应用--command --option1 =? --option2 = 2?

基本上,可以有任何数量的选项,但根据应用的实例仅有一个命令。类似的方式git的工作。

现在,我想我应该把它写在C ++中得到一些提​​升和STL经验,并与其中的几个设计模式,我一直在阅读有关的走了。所以,我实现了这个:

 集体诉讼
{
上市:
    无效AddParameter(标准::字符串键,提振::任何P);
    虚拟无符号整型ExecuteAction();保护:
    性病::地图<的std ::字符串,提振::任何>参数;
};

无论如何,我会解释我的逻辑,只是为了检查它 - 这是一个抽象十岁上下的行动。所有操作都需要选择加入,因此参数映射,这样我们就可以在这个水平上实现,但我们预计 ExecuteAction 由派生类来实现,诸如我简单的例子 DisplayHelpAction ,这确实pretty多是什么在锡说。

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

 类DetermineAction
{
上市:
    DetermineAction();
    VX :: Modero触摸::行动的getAction(标准::字符串ActionString);
私人的:
    性病::地图<的std ::字符串,VX :: Modero触摸::作用> cmdmap;
};

是,构造函数将创建的地图可能的字符串你可以要求和的getAction会做什么它说的逻辑 - 给它一个命令字符串,它会给你一个从动作它实现所需的功能。

我在使用该构造函数的麻烦。我想这样的:

 这个 - > cmdmap =的std ::地图<的std ::字符串,作用>();
这 - > cmdmap.insert(对<字符串,作用>(帮助,DisplayHelpAction()));
这 - > cmdmap.insert(对<字符串,作用>(许可证,DisplayLicenseAction()));

这是造成很多误区。现在,我已经习惯了的接口,Java的方式,让你用:

 接口I =新的具体类();

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

 回报这个 - > cmdmap [A​​ctionString]

应返回从操作派生的类,关于这一点我就可以开始添加参数,调用execute。

因此​​,要总结,我有两个这是密切相关的问题:


  • 音板。我特意练抽象的东西,所以有一些额外的复杂性存在,但在原则上,是我的做法声音?有没有我已经错过了一个疯狂的明显的捷径?有没有更好的方法,我应该使用?

  • 我如何设置我的类映射解决方案,使我可以返回正确的类?具体的投诉链接时间是:

     链接CXX可执行的myapp
    CMakeFiles / myapp.dir / abstractcmd.cpp.o:在功能`NF ::动作::动作():
    abstractcmd.cpp :( text._ZN2vx6modero6ActionC2Ev [_ZN2vx6modero6ActionC5Ev] + 0x13)均禁用:未定义的参考`虚表的NF ::行动


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


修改1 :好了,我现在已经换成动作行动* 作为每尤金的回答,我试图新SomethingThatSubclassesAction 添加到地图。我仍然得到虚函数表的错误。


解决方案

  1. 不是需要有一件事值得一说了蝙蝠的权利是通过指针不是值在C,它运行时多态性工程++基类。所以,你的的std ::地图<的std ::字符串,作用> 必须的std ::地图<的std ::字符串,行动*> 或派生操作(即 DisplayHelpAction )将的切片当复制到地图。存储行动* 也意味着你需要时,即可大功告成明确把释放地图值的护理。 注意:您可以使用提高:: ptr_map 的boost :: ptr_map<的std ::字符串,作用> )(如@Fred Nurk指出的)或的的boost :: shared_ptr的的std ::地图<标准::字符串,提高:: shared_ptr的<作用>> )不用担心明确释放行动* 分配结果
    同样的事情行动的getAction(标准::字符串ActionString);'它需要成为行动*的getAction(标准::字符串ActionString);


  2. 链接器错误的是(最有可能)造成无法提供)的实现虚拟unsigned int类型ExecuteAction(; 。另外,我会说这是有道理的,使其纯虚(虚拟unsigned int类型ExecuteAction()= 0; ) - 在这种情况下,你不需要提供实现为了它。它还将提供关闭语义到Java接口,为动作类。


  3. 除非你有一个很好的理由动作派生的对象,不知道整个升压:program_options 我要传下来,并让他们每个人访问它,而不是直接建的std ::地图&LT的;的std :: string的,提振::任何方式> p>


  4. 我要重命名 DetermineAction 来像 ActionManager 的ActionHandler


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.

解决方案

  1. One thing than needs to be said right off the bat is that runtime polymorphism works in C++ via pointers to the base class not by value. So your std::map<std::string, Action> needs to be std::map<std::string, Action*> or your derived Actions (i.e. DisplayHelpAction) will be sliced when copied into the map. Storing Action* also mean that you'll need to explicitly take care of freeing the map values when you're done. Note: you can use a boost::ptr_map (boost::ptr_map<std::string,Action>) (as @Fred Nurk pointed out) or a boost::shared_ptr (std::map<std::string,boost::shared_ptr<Action> >) to not worry about explicitly freeing the Action* allocated.
    The same thing about 'Action getAction(std::string ActionString);' it needs to become Action* getAction(std::string ActionString);.

  2. The linker error is (most likely) caused by not providing an implementation for virtual unsigned int ExecuteAction();. Also I'd say it makes sense to make it pure virtual (virtual unsigned int ExecuteAction() = 0;) - in which case you don't need to provide an implementation for it. It will also provide the closes semantics to a Java interface for the Action class.

  3. Unless you have a very good reason for the Action derived objects to not know the entire boost:program_options I'd pass it down and let each of them access it directly instead of constructing std::map<std::string, boost::any>.

  4. I'd rename DetermineAction to something like ActionManager or ActionHandler.

这篇关于使用界面C ++命令行操作的抽象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆