cxxopts–readme分析文档
cxxopts是一个轻量级命令行文本分析项目,我门可以自定义选项来解析命令行中输入的字符,达到一定的目的,转化为我们想让计算机得到的结果
[github地址](arqqqq/cxxopts: Lightweight C++ command line option parser (github.com))
1.快速开始
cxxopt支持传统的gun标准语法,任何如下的选项定义是被允许的
--long
--long = argument
--long argument
-a
-ab
-abc
任何的**--
**标识后面附带的字符都会被解析为位置参数
2.基础
你需要包含cxxxopts头文件并创建选项options才能使用cxxopts
#include<cxxopts.hpp>
创建一个解析选项实例
//cxxopts::Options option("名称","描述");
cxxopts::Options option("MyProgram","this is My Program");
1.可以用Options添加一些解析选项
options.add_options()
("d,debug", "Enable debugging") // a bool parameter
("i,integer", "Int param", cxxopts::value<int>())
("f,file", "File name", cxxopts::value<std::string>())
("v,verbose", "Verbose output", cxxopts::value<bool>()->default_value("false"));
方法add_options返回一个OptionAdder类的引用,OptionAdder重载了()运算符,在cxxopts中的源码为
std::basic_regex<char> option_specifier
("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?");
inline
OptionAdder&
OptionAdder::operator()
(
const std::string& opts,
const std::string& desc,
const std::shared_ptr<const Value>& value,
std::string arg_help
)
{
std::match_results<const char*> result;
std::regex_match(opts.c_str(), result, option_specifier); //通过正则表达式来匹配参数opts中的目标字串
if (result.empty())
{
throw_or_mimic<invalid_option_format_error>(opts); //如果解析出来的结果是无效的,会抛出异常
}
const auto& short_match = result[2]; //子式1
const auto& long_match = result[3]; //子式2
if (!short_match.length() && !long_match.length()) //如果长短选项都没有的话,会抛出异常
{
throw_or_mimic<invalid_option_format_error>(opts);
} else if (long_match.length() == 1 && short_match.length()) //长选项的字符数量不能够为1
{
throw_or_mimic<invalid_option_format_error>(opts);
}
auto option_names = [] //一个lambda表达式,
(
const std::sub_match<const char*>& short_,
const std::sub_match<const char*>& long_
)
{
if (long_.length() == 1)
{
return std::make_tuple(long_.str(), short_.str());
}
return std::make_tuple(short_.str(), long_.str()); //返回一个通过make_tuple自动推断构造器类型,返回一个OptionAdder
}(short_match, long_match);
m_options.add_option
(
m_group,
std::get<0>(option_names),
std::get<1>(option_names),
desc,
value,
std::move(arg_help)
);
return *this;
}
在类OptionAdder中,有两个成员变量,一个Option对象my_options,表示当前是在那个选项组别下,另外一个是一个string对象my_group,表示我的选项组别,重载的OptionAdder一共有四个参数,有两个参数具有默认值,分别是
- opts——解析选项,可以是长选项,也可以是短选项,也可以是两者的组合
- desc——选项的描述,用于在帮助文档中的选项功能的描述,不具默认值,必须传入
- cxxopts::value<>()——一个cxxopts头文件中定义的泛型类,默认值为cxxopts(),代表着与声明值对应的value
- arg_help——具体还不清楚有什么功效,默认值为**""**
Options are declared with a long and an optional short option. A description must be provided. The third argument is the value, if omitted(默认值) it is boolean. Any type can be given as long as it can be parsed, with operator>>.(任何类型都可以给出,只要他可以被解析,使用操作符<<)
2.通过prase来解析命令行
可以通过prase来解析命令行中输入的选项
auto result = options.prase(argc,argv);
options.prase返回一个PraseResult类,这个类也是在cxxopts中定义的,存放着解析结果
3.通过as来获取value
可以通过results[“之前添加的选项”]来获取value值
auto res = result["opt"].as<type>();
opt必须是之前通过add_options添加的选项,否则会抛出异常,并且使用的选项一定是要在作用域内声明的
3.未被确认的参数
可以通过参数设置方法**allow_unrecognised_options()**来跳过没有被定义的选项
options.allow_unrgcognised_options();
通过这个方法来跳过那些没有在options中定义的选项里面,并且可以通过**results.unmatched()**来取得这些跳过的字符串,比如可以看到代码
#include "cxxopts.hpp"
#include <iostream>
void prase(int argc,const char* argv[]){
try{
cxxopts::Options options("MyTest","a test program"); //搭建Option类
options.positional_help("[Positional help]").show_positional_help();
options.set_width(70).allow_unrecognised_options().add_options("myopt")
("i,int","a integer",cxxopts::value<int>())
("f,float","a float",cxxopts::value<float>())
("vector","a list of double",cxxopts::value<std::vector<double>>());
auto result = options.parse(argc,argv);
if(result.count("int")){
std::cout<<"int ="<<result["int"].as<int>()<<std::endl;
}
if(result.count("float")){
std::cout<<"float ="<<result["float"].as<float>()<<std::endl;
}
if(result.count("vector")){
std::cout<<"vector = ";
const auto res = result["vector"].as<std::vector<double>>();
for(const auto v:res){
std::cout<<v<<" , ";
}
std::cout<<std::endl;
}
const auto res = result.unmatched(); //收取那些没有被定义的字符串
std::cout<<"unmatched opt: ";
for(auto vs:res){
std::cout<<vs<<" , ";
}
std::cout<<std::endl;
std::cout<<"Saw "<<result.arguments().size()<<" arguments"<<std::endl;
}catch (cxxopts::OptionException &e){
std::cout<<"parse error "<<e.what()<<std::endl;
}
}
int main(){
int argc = 10;
const char* argv[] = {"a","-i","100","vv","cc","hello","--float","1000","--vector=1.0,11.0,112.2","hhh"};
prase(argc,argv);
return 0;
}
//在运行结果中会有一个
//unmatched opt: vv,cc,hello,hhh, 这些都是没有被定义的选项
可以通过unmatched方法来获取那些被跳过的字符串,注意这里是字符串,这也就意味着只能够存入不带
-
的字符,如果一个命令行字符是形如-abc
的形式,他会被认定为一个字符,而不会被存入到unmatched方法获得的这个vector里面
4.异常
cxxopts中的异常分为两个方面,一个是定义选项的错误,另一个是解析参数列表的错误,这些异常都定义在cxxopts的OptionException里面,定义选项的错误为派生出来的optionspecException,解析参数的异常OptionParseException,所有异常都可以通过what()函数来解释异常信息
5.帮助文档
在cxxopts中,如果定义了选项,可以通过调用**help()**方法来获取这些选项的帮助文档,可以看下面的代码
#include<iostream>
#include "cxxopts.hpp"
using namespace std;
void help(){
try{
cxxopts::Options options("MyOpt","this is a opt group");
options.positional_help("help");
options.show_positional_help();
options.set_width(70).allow_unrecognised_options().add_options("mygroup")
("i,int","a integer",cxxopts::value<int>())
("f,float","a float",cxxopts::value<float>())
("v,vector","a list of int",cxxopts::value<std::vector<int>>());
cout<<options.help(); //关键就在于这个help方法,能将添加的选项整理出来供使用者参考
}catch (cxxopts::OptionException &e){
cout<<e.what()<<endl;
exit(1);
}
}
int main(){
help();
return 0;
}
/home/zrshonor/CLionProjects/cxxopts/cmake-build-debug/src/example
this is a opt group—>之前的options类的描述
Usage:
MyOpt [OPTION…]mygroup options:—>选项组别,小分组,可以有很多个小分组
-i, --int arg a integer //从左到右依次为,短选项,长选项,选项描述
-f, --float arg a float
-v, --vector arg a list of intProcess finished with exit code 0
至于这个help输出的帮助文档,会看到在选项后面会有一个arg,这是OptionAdder类的最后一个参数,默认为arg(argument),也可以自定义
6.位置参数
位置参数可以被选择性地解析一个或者多个选项,
options.parse_positional({"first", "second", "last"})
"last"应该是一个容器类型的选项的名称,而其他的应该只有一个值,大致想表达的意思是,你可以用三个位置参数来获取到在这个const char* argv[]字符数组中没有被匹配到的数据,相当于去首先很明显通过函数传入的option必须是在之前定义了的,对项目中的example.cpp的代码进行测试发现,在parse方法定义中有这么一句
options.parse_positional({"input", "output", "positional"});
通过使用不同的参数传入得到的解析方法,可以分析得到
- input是option里面定义的一个选项,如果没有在数组里面没有匹配到
--input
,那么第一个不符合选项的第一个字符将是input的值 - output也是同样的道理
- positional这个位置参数对应的value是一个容其类型,他相当于一个位置参数,其对应的value值不仅可以收纳匹配到的选项值,而且可以收纳那些没有被匹配到的剩余字符,当此时,你再去调用**unmatched()**方法来获取到没有匹配到的字符是一个无效的操作
总结的来说的话,调用parse_positional方法后,传入的那些选项参数会首先在数组中查找匹配的的选项,如果有匹配的选项,那么就跟正常匹配没有什么区别,否则他会按照顺序,以分配的方式将选项分配到位
可以看测试例子,代码是cxxopts项目中自带的example.cpp,下面是main函数的测试用例
int argc = 7;const char* argv[] = {"a","--input","inp","--output=b.bin","t1","t2","t3"};parse(argc,argv);
结果为
Input = inp
Output = b.bin
Positional = {t1, t2, t3, }Arguments remain = 7
Saw 5 arguments可以看到input选项匹配到了一个结果,output同样的也匹配到了一个结果,但是positional选项没有与之匹配到的选项,所以说就按照顺序,将后面的没有匹配到的字符分配给它,那么这样的话,可以对代码进行一定的更改,将符合前两个的选项撤销掉,可以看到
int argc = 4;const char* argv[] = {"a","t1","t2","t3"};parse(argc, argv);
Input = t1
Output = t2
Positional = {t3, }Arguments remain = 4
Saw 3 arguments因为都没有匹配项,那么就是按照顺序分配,第一个给input,第二个给output,最后的都给positional,
需要注意的是,传入的字符数组中的第一个元素是会被忽略掉的
7.默认值和隐式值
对于某些选项,我们可以为其设置默认值,假如有下面的一个实例
cxxopts::Option options("mytest","a test example");
options.positional_help("[optional args]");
options.show_positional_help();
options.set_width(70)
.set_tab_expansion()
.allow_unrecognised_options()
.add_options("my_opt")
("f,float","a float number",cxxopts::value<float>())
("i,int","a integer",cxxopts::value<int>()->default_value("0"));
可以看到,在上面的代码中,设置了两个选项,一个具备默认值,另一个不具备,这样就在解析命令行参数时也会存在着部分差异
int argc = 10;const char* argv[] = {"a","--float","--int"};
可以看到上面的示例代码中,–float这样的声明解析会报错,因为float不具备默认值,一旦解析到声明语句,那么就必将会对这个语句进行初始化,程序在后面搜索与之匹配的初始化值,如果搜索不到,对于设置了默认值的选项就会以默认值代替,没有设置默认值的话就会抛出异常,所以需要这样地修改
int argc = 10;const char* argv[] = {"a","--float=100","--int"};
隐式值也是如此,在两个选项被同时定义的时候,如果没有对其赋值,选项值会用隐式值代替
cxxopts::value<type>()->implicit_value("隐式值")
这样就说明了--opt
这样的选项被解析出来设置了隐式值的会用隐式值代替,没有隐式值的话看有没有设置默认值,有的话用默认值代替,没有的话抛出异常,--opt=value
这样的声明就会用value来给声明值赋值
8.布尔值
布尔选项有一个默认的隐式值"true",他可以被重写,其效果是,假如"-o"的value为布尔类型,因为没有初始值,会被置为true,当然也可以用
-o=value
来赋值,并且也可以自己设置默认值和隐式值
9.列表声明
可以采用这样的定义方式来获取一个列表声明,比如cxxopts::value<std::vector<type>>()
来匹配一个列表,比如对于下面的代码
"--list = 1,1.0,1.2,1.3" //用一个value值为std::vector<double>的选项list来匹配
可以匹配一个列表,通过定义value值为vector的选项,列表的不同元素之间用,
隔开,他是定义在cxxopts中的一个宏CXXOPTS_VECTOR_DELIMITER
10.相同的选项可以定义不同的数值
相同的声明可以赋予不同的寓意,这样的选项设置可以通过列表来实现,比如
--use train --use plane
可以将其value设置为vector