ZLToolKit模块(二)cmd

功能描述

ZLToolKit的命令行解析模块。

  1. 支持常见的命令行解析功能,如–help
  2. 支持命令简写形式,如-h。
  3. 支持默认值。
  4. 示例代码在test_shell.cpp。

使用示例

class CMD_http: public CMD 
{
public:
    CMD_http() {
        _client.reset(new TestClient);
        _parser.reset(new OptionParser([this](const std::shared_ptr<ostream> &stream,mINI &args){
            //所有选项解析完毕后触发该回调,我们可以在这里做一些全局的操作
            if(hasKey("connect")){
                //发起连接操作
                connect(stream);
                return;
            }
            if(hasKey("commit")){
                commit(stream);
                return;
            }
        }));

        (*_parser) << Option('s',/*该选项简称,如果是\x00则说明无简称*/
                             "server",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             "139.224.212.51:8080",/*该选项默认值*/
                             false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "tcp服务器地址,以冒号分隔端口号",/*该选项说明文字*/
                             [this](const std::shared_ptr<ostream> &stream, const string &arg){/*解析到该选项的回调*/
                                 if(arg.find(":") == string::npos){
                                     //中断后续选项的解析以及解析完毕回调等操作
                                     throw std::runtime_error("\t地址必须指明端口号.");
                                 }
                                 //如果返回false则忽略后续选项的解析
                                 return true;
                             });

        (*_parser) << Option('d', "disconnect", Option::ArgNone, nullptr ,false, "是否断开连接",
                             [this](const std::shared_ptr<ostream> &stream, const string &arg){
                                 //断开连接操作,所以后续的参数我们都不解析了
                                 disconnect(stream);
                                 return false;
                             });

        (*_parser) << Option('c', "connect", Option::ArgNone, nullptr, false, "发起tcp connect操作", nullptr);
        (*_parser) << Option('m', "method", Option::ArgRequired, "GET",false, "HTTP方法,譬如GET、POST", nullptr);
        (*_parser) << Option('p', "path", Option::ArgRequired, "/index/api/listRtpServer",false, "HTTP url路径", nullptr);
        (*_parser) << Option('C', "commit", Option::ArgNone, nullptr, false, "提交HTTP请求", nullptr);

    }

    ~CMD_http() {}

    const char *description() const override {
        return "http测试客户端";
    }

private:
    void connect(const std::shared_ptr<ostream> &stream){
        (*stream) << "connect操作" << endl;
        _client->connect(splitedVal("server")[0],splitedVal("server")[1], 30);
    }
    void disconnect(const std::shared_ptr<ostream> &stream){
        (*stream) << "disconnect操作" << endl;
        _client->disconnect();
    }
    void commit(const std::shared_ptr<ostream> &stream){
        (*stream) << "commit操作" << endl;
        _client->commit((*this)["method"],(*this)["path"],(*this)["server"]);
    }

private:
    TestClient::Ptr _client;
};

源码分析

类图

请添加图片描述


源码剖析

  1. Option
    选项类,存储一条选项相关信息。包括短选项名、长选项名、选项额外参数类型(无参、有参、可选参)、选项额外参数默认值、该选项是否必须给额外参数赋值、选项描述信息、选项对应回调。
    在构造函数里默认添加一个helper选项,可以打印出支持的所有选项。

  2. OptionParser
    选项解析器。每个命令(CMD)都有一个选项解析器成员对象,OptionParser内部使用map存储了该命令支持的所有选项信息。
    用户输入的命令信息最终会通过CMD类调用到该类进行解析,在map中查找option执行其回调函数,并填充该CMD的key和value。

  3. CMD
    命令类,继承自map,每次调用operator()操作后,CMD本身存储的是用户当前输入的选项以及非用户输入但具有默认值的选项,而该命令支持的所有选项,存储在_parser中(OptionParser)的_map_options中。

  4. CMDRegister
    命令管理单例类。
    所有的注册,查找,匹配和运行的入口。


C++11

forward:

如果是模板类型T&&可以实现完美转发。

```
 OptionParser &operator<<(Option &&option) {
    int index = 0xFF + (int) _map_options.size();
    if (option._short_opt) {
        _map_char_index.emplace(option._short_opt, index);
    }
    // 如果没有forward转发,那么option的类型就是Option,而不是Option&&
    _map_options.emplace(index, std::forward<Option>(option));
    return *this;
}
```

这里是为保持option的右值属性,否则option已经是左值了,永远调不到移动构造版本。

emplace优化了什么?

```
_map_options.emplace(index, std::forward<Option>(option)); //一次移动构造
_map_options.insert(std::make_pair(index, std::forward<Option>(option))); //两次移动构造
```

在上面的代码中,调用 map::insert 函数时会先调用 std::make_pair 函数创建一个临时 pair 对象,再调用 pair 的移动构造函数将临时 pair 对象移动到 map 中。而调用 map::emplace 函数时,则可以直接在 map 中构造元素,避免了一次移动操作。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值