本篇文章主要提炼自github上CLI11的官方文档,取出自己感兴趣的内容,记录下来方便以后使用
CLI11是一个基于C++开发的命令行解析库,目前最新版本1.9
其优点:
#include <CLI11.hpp>
,当然也可以使用cmake编译版本关于编译
g++ -std=c++11 xx.cpp -I path_with_CLI11 -o app
(path_with_CLI11是一个路径,其内有CLI11.hpp, app是编译后的可执行程序名)
运行:
需要提示信息的时候运行./app -h
#include "CLI11.hpp"
#include <iostream>
int main(int argc, char **argv) {
CLI::App app{"App description"};
// Define options
int p = 0;
app.add_option("-p", p, "Parameter");
CLI11_PARSE(app, argc, argv);
std::cout << "Parameter value: " << p << std::endl;
return 0;
}
只接受一个可选参数-p
CLI::App 是与库的所有交互的基础
CLI11_PARSE 宏内部执行app.parse(argc,argv)对命令行参数解析,出错时抛出ParseError,然后捕获异常,打印错误信息并退出程序
即必须参数,使用方法是add_xxx方法的第一个参数如”-a” 把”-“ 去掉,换成有意义的名字,如”outputDir” 位置参数就是没有这些参数就无法运行,没有默认值;多个位置参数按定义顺序传递
命令行输入只填flag名字就行,不接受参数;函数为add_flag,有以下三种类型:
boolean flags绑定flag -f 到布尔变量my_flag,默认是flase,如果用户输入了-f 则为true
integer flags同样的绑定,范围变成整数
pure flags
使用my_flag->count()来确定值是true/false,默认为0则false,大于等于1次则true;也可以bool(*my_flag)来使用
其他有callback flags,add_flag_function()可以打印每个参数输入了多少次
每个add_*方法的第一个参数,即指定的命令参数,可以有多个名字,逗号分隔即可,如”-a,—alpha,-b”;另外一个比较有用的是->ignore_case() 方法,忽略大小写,方便用户输入
多个参数可以组合如”-i -a -b”等价于”-iab”
与flags区别就是接受参数;函数为add_option
基本形式:
int int_option = 0;
app.add_option("-i", int_option, "Optional description");
其行为:绑定选项-i到int_option,解析其后的数据转换为整型,类型不对会失败;如果没有此选项则使用初始值
可接受类型包括:整型/浮点/字符串/vector/函数
接受多个值,直到下一个值不合法;也可以用->expected(N)指定需要几个值
如果出现重复option,会进行组合,即”-v 1 2 -v 3 4”等同于”-v 1 2 3 4”(新版本才支持此功能)
链式使用,当作装饰器,可以同时添加多个装饰
列举几个可能会常用到的:
sets使用集合来限定输入范围;如果输入不在集合范围内,会打印提示信息
complex number
std::complex<float> val;
app.add_complex("--cplx", val);
optional
windows风格
验证器有两种形式
子命令就是包含了一系列选项的一个关键字,如git commit/clone 这里面的commit clone后面还可以跟各种选项,他们就是git程序的子命令
子命令的类类型和App相同,因此可以任意嵌套
关于App的功能
CLI::App* sub = app.add_subcommand("sub", "This is a subcommand");
第一个参数就是子命令的名字,第二个参数是描述
设置必须的子命令个数,只传一个参数则限定了个数 app.require_subcommand(/ min / 0, / max / 1);
编写个实例,把subcommand flag 各种option,check等常用功能都演示一遍
代码:
//把CLI11.hpp放到当前目录下
#include "CLI11.hpp"
#include <iostream>
using namespace std;
int main(int argc, char **argv) {
CLI::App app{"App description"}; // 软件描述出现在第一行打印
app.footer("My footer"); // 最后一行打印
app.get_formatter()->column_width(40); // 列的宽度
app.require_subcommand(1); // 表示运行命令需要且仅需要一个子命令
auto sub1 = app.add_subcommand("sub1", "subcommand1");
auto sub2 = app.add_subcommand("sub2", "subcommand1");
sub1->fallthrough(); // 当出现的参数子命令解析不了时,返回上一级尝试解析
sub2->fallthrough();
// 定义需要用到的参数
string filename;
int threads = 10;
int mode = 0;
vector<int> barcodes;
bool reverse = false;
string outPath;
if (sub1)
{
// 第一个参数不加-, 表示位置参数,位置参数按出现的顺序来解析
// 这里还检查了文件是否存在,已经是必须参数
sub1->add_option("file", filename, "Position paramter")->check(CLI::ExistingFile)->required();
// 检查参数必须大于0
sub1->add_option("-n,-N", threads, "Set thread number")->check(CLI::PositiveNumber);
}
if (sub2)
{
// 设置范围
sub2->add_option("-e,-E", mode, "Set mode")->check(CLI::Range(0,3));
// 将数据放到vector中,并限制可接受的长度
sub2->add_option("-b", barcodes, "Barcodes info:start,len,mismatch")->expected(3,6);
}
// 添加flag,有就是true
app.add_flag("-r,-R", reverse, "Apply reverse");
// 检查目录是否存在
app.add_option("-o", outPath, "Output path")->check(CLI::ExistingDirectory);
CLI11_PARSE(app, argc, argv);
// 判断哪个子命令被使用
if (sub1->parsed())
{
cout<<"Got sub1"<<endl;
cout<<"filename:"<<filename<<endl;
cout<<"threads:"<<threads<<endl;
}
else if (sub2->parsed())
{
cout<<"Got sub2"<<endl;
cout<<"mode:"<<mode<<endl;
cout<<"barcodes:";
for (auto& b : barcodes)
cout<<b<<" ";
cout<<endl;
}
cout<<endl<<"Comman paras"<<endl;
cout<<"reverse:"<<reverse<<endl;
cout<<"outPath:"<<outPath<<endl;
return 0;
}
编译:
g++ -std=c++11 run.cpp -o myapp
用的gcc4.8
运行:
-h 查看提示
给正确的参数
给错误参数
允许读写配置文件
允许定制自己的帮助打印信息 app.get_formatter() 获取当前格式
column_width(width) 设置列的宽度
lable(key, value) 将lable设置一个不同的值
例子
部分的替换格式
作用是,如果命令行参数没有给定,则从环境变量中获取,如果存在的话
std::string opt;
app.add_option("--my_option", opt)->envname("MY_OPTION");
a->nees(b) a依赖b
a->excludes(c) a与c不共存
custom option callbacks
custom converters