1. CMD功能描述
ZLToolKit的命令行解析模块。
- 支持常见的命令行解析功能,如
--help, 命令简写形式,如-h。 - 示例代码在
test_shell.cpp。
2. 使用示例
- http测试客户端
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;
};
3. 源码分析
3.1 类图

3.2 源码剖析
Option
- 选项类,存储一条选项相关信息。包括短选项名、长选项名、选项额外参数类型(无参、有参、可选参)、选项额外参数默认值、该选项是否必须给额外参数赋值、选项描述信息、选项对应回调。
- -在构造函数里默认添加一个helper选项,可以打印出支持的所有选项。
OptionParser
- 选项解析器。每个命令(
CMD)都有一个选项解析器成员对象,OptionParser内部使用map类型的成员存储了该命令支持的所有选项信息。 - 用户输入的命令信息最终会通过
CMD类调用到该类进行解析,在map中查找option执行其回调函数。
CMD
- 命令类,继承自
map,每次调用operator()操作后,CMD本身存储的是用户当前输入的选项以及非用户输入但具有默认值的选项,而该命令支持的所有选项,存储在_parser中(OptionParser)的_map_options中。
CMDRegister
- 命令管理单例类。
- 所有的注册,查找,匹配和运行的入口。
4. C++11
4.1 forward到底做了什么?
-
- 虽然名字叫“转发”(forward),但它本质上就是一个条件
static_cast。
在代码中(非模板函数场景):
- 虽然名字叫“转发”(forward),但它本质上就是一个条件
std::forward<Option>(option)
等价于:
static_cast<Option&&>(option)
-
- 强制转右值
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;
}
// 原始模板
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) {
return static_cast<_Tp&&>(__t);
}
// 代入 _Tp = Option 后:
// 1. remove_reference<Option>::type 就是 Option
// 2. 参数类型变成 Option& __t (这正好能接收你的左值 option)
// 3. 返回类型 _Tp&& 变成 Option&& (右值引用)
constexpr Option&& forward(Option& __t) {
return static_cast<Option&&>(__t); // 强制转为右值引用
}
-
- 配合模板类型
T&&可以实现完美转发(保留其左值/右值属性)
- 配合模板类型
假设我们有一个包装函数:
template<typename T>
void wrapper(T&& arg) {
// 这里的 T&& 是万能引用,既能接左值,也能接右值
process(std::forward<T>(arg)); // 正确做法
// process(std::move(arg)); // 错误做法(危险!)
}
对完美转发的原理不理解的朋友,可以看看下面链接:
完美转发的详细讲解
4.2 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中分配空间直接构造元素,避免了一次移动操作。

文章介绍了ZLToolKit中的命令行解析模块,支持命令简写、默认值等功能。通过CMD_http类展示了如何使用OptionParser进行选项解析,包括添加选项、回调处理等。同时,文章讨论了C++11中的forward和emplace优化,解释了emplace如何避免一次额外的移动构造操作,提高效率。
406

被折叠的 条评论
为什么被折叠?



