CLI11: C++11 命令行解析器

[![Build Status Azure][azure-badge]][azure] [![Actions Status][actions-badge]][actions-link] [![Code Coverage][codecov-badge]][codecov] [![Codacy Badge][codacy-badge]][codacy-link] ![License: BSD][license-badge] [![DOI][doi-badge]][doi-link]
[![Gitter chat][gitter-badge]][gitter] [![Latest GHA release][releases-badge]][github releases] [![Latest release][repology-badge]][repology] [![Conan.io][conan-badge]][conan-link] [![Conda Version][conda-badge]][conda-link] [![Try CLI11 online][wandbox-badge]][wandbox-link]
What's new • [Documentation][gitbook] • [API Reference][api-docs]
CLI11 是一个用于 C++11 及更高版本的命令行解析器,它通过简单直观的接口提供丰富的功能集。
Table of Contents
在上一个发布的小版本中添加的功能标记为 "🆕"。仅在 main 分支中可用的功能标记为 "🚧"。
背景
简介
CLI11 提供了你在强大的命令行解析器中期望的所有功能,
拥有美观、极简的语法,且除 C++11 外无任何依赖。它是
纯头文件库,并提供单文件形式以便轻松集成到项目中。它
对于小型项目易于使用,但对于复杂的命令行项目也足够强大,并且可以针对框架进行定制。它在 [Azure][] 和
[GitHub Actions][actions-link] 上进行了测试,最初由 [GooFit GPU
拟合框架][goofit] 使用。它受 Python 的 [plumbum.cli][plumbum] 启发。CLI11 在本 README 中提供了用户友好的介绍,一本更深入的
教程 [book][gitbook],以及由
Doxygen 生成的 [API 文档][api-docs]。有关
当前和过去版本的详细信息,请参阅 changelog 或 [GitHub Releases][]。另请参阅 [Version 1.0 post][]、[Version 1.3
post][]、[Version 1.6 post][] 或 [Version 2.0 post][] 以获取更多信息。
你可以通过在 RSS 阅读器(如 Feedly)中订阅 https://github.com/CLIUtils/CLI11/releases.atom 来接收新版本的发布通知, 或使用 GitHub 关注工具的 releases 模式。
为什么要编写另一个 CLI 解析器?
一个可接受的 CLI 解析器库应该具备以下所有特性:
- 易于集成(即,仅头文件,尽可能单文件,无外部 依赖)。
- 简短、简单的语法:这是使用 CLI 解析器的主要原因之一,它 应使从命令行定义变量几乎与定义其他任何变量一样容易。如果程序的大部分 都隐藏在 CLI 解析中,这将是一个可读性问题。
- C++11 或更高版本:应支持 GCC 4.8+(CentOS/RHEL 7 的默认版本)、Clang 3.4+、AppleClang 7+、NVCC 7.0+ 或 MSVC 2015+。
- 支持 Linux、macOS 和 Windows。
- 在所有常见平台和编译器上经过充分测试。“充分”定义为 由 [CodeCov][] 测量的良好覆盖率。
- 清晰的帮助输出。
- 友好的错误信息。
- 自然支持标准 shell 惯用法,如标志分组、位置参数 分隔符等。
- 易于执行,帮助、解析错误等提供正确的退出码和 详细信息。
- 易于作为提供“应用程序”给用户的框架的一部分进行扩展。
- 可用的子命令语法,支持多个子命令、嵌套 子命令、选项组以及可选的 fallthrough(稍后解释)。
- 能够添加配置文件(
TOML、INI或自定义格式),并 生成该文件。 - 生成可直接在代码中使用的真实值,而不是需要 花费计算时间去查找的值,适用于 HPC 应用程序。
- 支持常见类型、简单自定义类型,并可扩展至特殊类型。
- 宽松许可。
其他解析器
C++ 的主要 CLI 解析器包括(带有我个人的偏见观点):(点击展开)
| Library | 我的主观看法 |
|---|---|
| [Boost Program Options][] | 如果你已经依赖 Boost,这是一个很棒的库,但其 C++11 之前的语法确实很奇怪,而且在 main 函数中设置正确调用的文档说明很差(几乎需要一页代码)。最初开发了一个针对 Boost 库的简单封装,但随着 CLI11 变得更加强大,该封装被弃用了。捕获值并设置它的想法源自 Boost PO。[参见此比较。][cli11-po-compare] |
| [The Lean Mean C++ Option Parser][] | 单头文件固然不错,但在我看来,其语法极其糟糕。封装该语法或在复杂项目中使用它都相当不切实际。它似乎能很好地处理标准解析。 |
| [TCLAP][] | 其非标准的命令行解析导致常见的快捷方式失效。此外,它似乎支持度很低,仅接受最少的错误修复。虽然是纯头文件,但分布在相当多的文件中。尚未获得足够的支持以迁移到 GitHub。不支持子命令。会生成换行后的值。 |
| [Cxxopts][] | 基于 C++11,单文件,且拥有良好的 CMake 支持,但需要正则表达式,因此 GCC 4.8(CentOS 7 默认版本)无法使用。语法紧密基于 Boost PO,因此并非理想选择,但较为熟悉。 |
| [DocOpt][] | 一种在 C++11 中处理程序选项的完全不同方法,你编写文档,接口随之生成。过于脆弱且过于专用。 |
在我写完这篇文章后,我还发现了以下库:
| Library | 我的主观评价 |
|---|---|
| [GFlags][] | Google 命令行标志库。大量使用宏,功能范围有限,缺少子命令等功能。它提供了简单的语法,并支持配置文件/环境变量。 |
| [GetOpt][] | 功能非常有限的 C 语言解决方案,语法冗长且复杂。不支持太多功能,例如帮助生成。不过在 UNIX 上总是可用的(但存在不同的变体)。 |
| [ProgramOptions.hxx][] | 一个有趣的库,功能较弱且不支持子命令。拥有不错的回调系统。 |
| [Args][] | 同样有趣,并且支持子命令。我喜欢其类似 optional 的设计,但 CLI11 更简洁,提供直接的值访问,且代码更简洁。 |
| [Argument Aggregator][] | 我是 [fmt][] 库的忠实粉丝,try-catch 语句看起来很熟悉。:thumbsup: 似乎不支持子命令。 |
| [Clara][] | 专为出色的 [Catch][] 测试框架构建的简单库。语法独特,范围有限。 |
| [Argh!][] | 极简的 C++11 解析器,单头文件。功能不多。没有帮助生成?!?! 至少它是无异常的。 |
| [CLI][] | 自定义语言和解析器。构建系统过度设计,收益甚微。最后一次发布在 2009 年,但偶尔仍有活动。 |
| [argparse][] | C++17 单文件参数解析器。设计在某些方面似乎与 CLI11 相似。作者还有其他几个有趣的项目。 |
| [lyra][] | 一个简单的仅头文件解析器,具有可组合的选项。可能适用于简单的标准化解析 |
参见 [Awesome C++][] 获取一份偏见更少的解析器列表。你还可以在 [Single file libs][] 找到其他单文件库。
这些库没有一个能满足上述所有要求,甚至都相差甚远。正如你可能已经猜到的那样,CLI11 做到了。因此,该库旨在提供出色的语法、良好的编译器兼容性以及最小的安装麻烦。
本库不支持的功能
有一些其他可能的“功能”是本库有意不支持的:
- 部分选项的补全,例如 Python 的
argparse会为不完整的参数提供补全。最好不要猜测。大多数 Python 的第三方命令行解析器实际上重新实现了命令行解析,而不是使用 argparse,原因就在于这种被认为存在的设计缺陷(较新版本确实有一个选项可以禁用它)。CLI11 的近期版本包含针对子命令名称的可选无歧义前缀匹配,通过.allow_subcommand_prefix_matching()启用,并附带一个生成建议的近似匹配示例。 - 自动补全:这最终可能会添加到 Plumbum 和 CLI11 中,但目前尚不支持。
- 虽然不推荐,但 CLI11 现在支持非标准选项名称,例如
-option。这是通过应用上的allow_non_standard_option_names()修饰符启用的。
安装
要使用,这里描述了最常见的方法,其他方法和详细信息可在 [installation][] 中找到:
- 一体化本地头文件:将
CLI11.hpp从[最新 版本][github releases] 复制到你的 include 目录中,即可使用。该文件 由每个版本的源文件合并而成。它包含完整的命令解析器库,但不包含独立的工具(如Timer、AutoTimer)。这些工具是完全自包含的,可以单独复制。 - 一体化全局头文件:与上述方法类似,但将文件复制到共享文件夹
位置,例如
/opt/CLI11。然后,必须扩展 C++ 包含路径以 指向该文件夹。对于 CMake 3.10+,请使用include_directories(/opt/CLI11) - 对于其他方法,包括使用 CMake、conan 或 vcpkg,以及针对 GCC 8 或 WASI 的特定 说明,请参阅 [installation][]。
- 实验性 C++20 命名模块:使用
-DCLI11_MODULES=ON进行构建(需要 CMake 3.28+ 和支持模块的生成器),链接CLI11::Module,并编写import cli11;。详情请参阅 [installation][]。
预编译模式
CLI11 默认是仅头文件库,因此每个包含它的翻译单元都会
编译完整的库。如果你使用 CMake 构建 CLI11(add_subdirectory 或
已安装的包),可以设置 CLI11_PRECOMPILED 将
实现部分一次性编译为静态库:
cmake -S . -B build -DCLI11_PRECOMPILED=ON
这可以显著减少包含 CLI11 的每个文件的编译时间(在
一次简单测量中,每个文件大约快 4 倍)。CLI11::CLI11
目标保持不变。有关详细信息,请参阅 [installation][]。
用法
添加选项
为了进行设置、添加选项并运行,你的 main 函数将类似于 以下内容:
int main(int argc, char** argv) {
CLI::App app{"App description"};
argv = app.ensure_utf8(argv);
std::string filename = "default";
app.add_option("-f,--file", filename, "A help string");
CLI11_PARSE(app, argc, argv);
return 0;
}
在添加选项时,名称之间不应发生冲突,如果添加的选项或修改的修饰符会导致命名冲突,则在 add_option 方法中会抛出运行时错误。这包括帮助 -h, --help 的默认选项。有关 ensure_utf8 的更多信息,请参阅下文中的
Unicode 支持] 部分。
注意:如果您不喜欢宏,这是该宏展开后的内容:(点击展开)
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
return app.exit(e);
}
try/catch 块确保 -h,--help 或解析错误会以正确的返回码退出(从 CLI::ExitCodes 中选取)。(此处的 return 应位于 main 内部)。你不应该在 catch 块内假设选项值已被设置;例如,帮助标志会故意短路所有其他处理,以提高速度并确保必需选项等不会干扰。
初始化只需一行,添加选项各需两行。解析
宏只需一行(或宏内容为 5 行)。应用程序运行后,
如果传入了文件名,它将被设置为正确的值,否则
将被设置为默认值。你可以使用 app.count("--file") 检查是否
在命令行中传入了该参数。
选项类型
尽管所有选项在内部都是相同的类型,但根据需求, 有几种添加选项的方式。支持的值为:
// Add options
app.add_option(option_name, help_str="")
app.add_option(option_name,
variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectors of any other supported type.
help_string="")
app.add_option_function<type>(option_name,
function <void(const type &value)>, // type can be any type supported by add_option
help_string="")
// char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values.
// There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type. The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version. If XC is a std::pair and T is some non pair type. Then a two argument constructor for T is called to assign the value. For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
app.add_option<typename T, typename XC>(option_name,
T &output, // output must be assignable or constructible from a value of type XC
help_string="")
// Add flags
app.add_flag(option_name,
help_string="")
app.add_flag(option_name,
variable_to_bind_to, // bool, int, float, complex, containers, enum, std::atomic, or string-like, or any singular object with a defined conversion from a string like add_option
help_string="")
app.add_flag_function(option_name,
function <void(std::int64_t count)>,
help_string="")
app.add_flag_callback(option_name,function<void(void)>,help_string="")
// Add subcommands
App* subcom = app.add_subcommand(name, description);
Option_group *app.add_option_group(name,description);
选项名称可以以除 ('-', ' ', '\n' 和 '!') 之外的任意字符开头。
对于长选项,在第一个字符之后,除 ('=',':','{',' ', '\n') 之外的所有字符都是允许的。对于 add_flag* 函数,'{' 和 '!' 具有特殊
含义,因此不允许使用。名称以逗号分隔的字符串形式给出,并带有连字符。一个选项或标志可以拥有任意数量的名称,之后,使用 count,你可以使用
其中任何名称,根据需要加上连字符,来统计选项。允许其中一个名称不带前导连字符;如果存在,该选项即为位置选项,并且该名称
将用于帮助行中其位置形式。字符串 ++ 也不允许作为选项名称,因为它在配置文件中用作数组分隔符和标记。
add_option_function<type>(... 函数通常要求提供模板
参数,除非传递了一个具有精确匹配的 std::function 对象。类型可以是 add_option 函数支持的
任何类型。如果值无效,该函数应抛出错误(可能是 CLI::ConversionError 或 CLI::ValidationError)。
双参数模板重载可用于需要 限制输入的情况,例如
double val;
app.add_option<double,unsigned int>("-v",val);
这将首先验证输入可转换为 unsigned int,然后再进行赋值。或者使用某种变体类型
using vtype=std::variant<int, double, std::string>;
vtype v1;
app.add_option<vtype,std::string>("--vs",v1);
app.add_option<vtype,int>("--vi",v1);
app.add_option<vtype,double>("--vf",v1);
否则输出将默认为字符串。add_option 可与任何整数或浮点类型、枚举或字符串一起使用。或者任何在赋值运算符或构造函数中接受 int、double 或 std::string 的类型。如果一个对象可以接受这些类型的多种变体,std::string 优先,其次是 double,最后是 int。为了更好地控制使用哪一个,或者使用另一种类型进行底层转换,请使用双参数模板直接指定转换类型。
诸如 (std 或 boost) optional<int>、optional<double> 和
optional<string> 以及其他任何包装器类型都直接受支持。就 CLI11 而言,包装器类型是指具有 value_type 定义的类型。
请参阅 [CLI11 Advanced Topics/Custom Converters][] 了解如何为其他类型添加自己的转换器。
向量类型也可以用于双参数模板重载
std::vector<double> v1;
app.add_option<std::vector<double>,int>("--vs",v1);
会加载一个 double 类型的向量,但确保所有值都可以表示为 整数。
使用 default_str(...) 或 default_val(...) 来设置选项或标志的默认字符串或值。
使用 ->default_function(std::string()) 直接自定义默认的捕获函数。
然后,默认值通过调用 ->capture_default_str() 来捕获。
通过 add_flag* 函数指定的标志选项允许一种语法,
用于将特定选项的选项名称默认为 false 值,或者在传递某些标志时默认为其他任何值。
例如:
app.add_flag("--flag,!--no-flag",result,"help for flag");
规定如果命令行上传递了 --flag,结果将为 true 或
包含值 1。如果传递了 --no-flag,则 result 将包含 false 或 -1
(如果 result 是有符号整数类型),或者 0(如果它是无符号类型)。
该语法的另一种形式更为明确:"--flag,--no-flag{false}";
这与前面的示例等效。这对于短格式
选项 "-f,!-n" 或 "-f,-n{false}" 也适用。如果 variable_to_bind_to 是
任何非整数值,默认行为是采用给定的最后一个值,而如果
variable_to_bind_to 是整数类型,行为将是求和所有
给定的参数并返回结果。如果需要,可以通过
更改每个标志上的 multi_option_policy 来修改此行为(这不会被继承)。
默认值可以是任何值。例如,如果您希望定义一个数值
标志:
app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag")
在命令行上使用这些标志中的任何一个,都会导致输出中显示指定的数量。 对于字符串值和枚举类型,只要默认值可以转换为给定类型,也可以执行类似的操作。
在 C++14 编译器上,你可以直接将回调函数传递给 .add_flag,
而在 C++11 模式下,如果你想要一个回调函数,则需要使用 .add_flag_function。
该函数将接收标志被传递的次数。你可以抛出相关的 CLI::ParseError 来指示失败。
示例
"one,-o,--one":只要不是标志,就是有效的,会创建一个可以 按位置指定,或使用-o或--one指定的选项"this"只能按位置传递"-a,-b,-c"非位置选项名称的数量没有限制
add 命令返回指向内部存储的 Option 的指针。此选项
可以直接用于在解析后检查计数(->count()),以避免
基于字符串的查找。
选项选项
在解析之前,你可以设置以下选项:
->required(): 如果此选项不存在,程序将退出。这在 Plumbum 中是mandatory,但必需选项似乎是一个更标准的术语。 为了兼容性,->mandatory()也有效。->expected(N): 对于向量参数,仅接受N个值,而不是尽可能多的值。如果为负数,则要求至少-N个;以--或另一个 已识别的选项或子命令结束。->expected(MIN,MAX): 设置伴随选项的预期值范围。expected(0,1)相当于创建一个标志。->type_name(typename): 设置 Option 类型的名称(type_name_fn允许使用函数代替)->type_size(N): 设置选项值的固有大小。如果为负数,解析器将 要求该数字的倍数。大多数情况下这会自动检测,但可以根据特定用例进行修改。->type_size(MIN,MAX): 将选项的固有大小设置为一个范围。->needs(opt): 此选项要求另一个选项也必须存在,opt 是 一个Option指针。可以使用remove_needs(opt)从needs中移除选项。也可以使用包含 选项名称的字符串来指定该选项->excludes(opt): 当opt存在时,不能提供此选项,opt 是 一个Option指针。也可以提供包含选项名称的 字符串。可以使用->remove_excludes(opt)从排除列表中移除选项->envname(name): 如果环境变量中存在且未在命令行上传递,则从环境变量获取值。该值还必须通过任何验证器才能 被使用。->group(name): 放置该选项的帮助组。对于位置 选项。默认值为"Options"。给定空字符串的选项不会 显示在帮助输出中(隐藏)。->ignore_case(): 忽略命令行上的大小写(同样适用于 子命令,不影响参数)。->ignore_underscore(): 忽略选项名称中的任何下划线(同样 适用于子命令,不影响参数)。例如 "option_one" 将与 "optionone" 匹配。这不适用于短格式选项,因为 它们只有一个字符->disable_flag_override(): 从命令行长格式标志选项可以 使用=符号--flag=value在命令行上分配值。 如果不希望此行为,disable_flag_override()会禁用它, 如果在命令行上执行此操作将生成异常。=不适用于 短格式标志选项。->allow_extra_args(true/false): 如果设置为 true,该选项将接受 无限数量的参数,类似于向量;如果为 false,则将参数数量 限制为选项中所用类型的大小。默认值取决于 所用类型的性质,容器默认为 true,其他默认为 false。->delimiter(char): 允许指定自定义分隔符,用于将 单个参数分隔为向量参数,例如在选项上指定->delimiter(',')将导致--opt=1,2,3生成向量的 3 个 元素,相当于 --opt 1 2 3,假设 opt 是 向量值。->description(str): 设置/更改描述。->multi_option_policy(CLI::MultiOptionPolicy::Throw): 设置多选项 策略。可用快捷方式:->take_last(),->take_first(),->take_all(), 以及->join()。这将仅影响期望 1 个参数或布尔标志的选项(这些选项不继承其默认值,而是始终以特定策略开始)。->join(delim)也可用于以特定分隔符进行连接。 这等效于调用->delimiter(delim)和->join()。有效值 为CLI::MultiOptionPolicy::Throw、CLI::MultiOptionPolicy::TakeLast、CLI::MultiOptionPolicy::TakeFirst、CLI::MultiOptionPolicy::Join、CLI::MultiOptionPolicy::TakeAll、CLI::MultiOptionPolicy::Sum以及CLI::MultiOptionPolicy::Reverse。->check(std::string(const std::string &), validator_name="",validator_description=""): 定义一个检查函数。如果检查失败,该函数应返回包含 错误信息的非空字符串->check(Validator):使用 Validator 对象执行检查,请参阅 Validators 了解可用 Validators 的描述以及如何 创建新的 Validators。->transform(std::string(std::string &), validator_name="",validator_description=""): 将输入字符串转换为输出字符串,就地修改已解析的 选项。->transform(Validator):使用 Validator 对象执行转换,请参阅 Validators 了解可用 Validators 的描述以及如何 创建新的 Validators。->each(void(const std::string &)>:在接收到每个值时 运行此函数。如果遇到错误,应抛出ValidationError。->configurable(false):禁用此选项出现在配置 文件中。->capture_default_str():存储当前附加的值并在 帮助字符串中显示。这应该适用于add_option可以 接受的几乎任何类型。->default_function(std::string()):高级:更改capture_default_str()使用的函数。->always_capture_default():在创建 新选项时始终运行capture_default_str()。仅对 App 的option_defaults有用。->default_str(string):直接设置默认字符串(无验证或 回调)。如果未提供参数,此字符串也将用作默认值 被传递并且请求该值。->default_val(value):从值生成默认字符串并验证 该值也是有效的。对于直接赋值给值类型的选项, 该类型中的值也会被更新。值必须可转换为 字符串(已知类型之一或具有流运算符)。如果设置了run_callback_for_default, 可能会触发回调。->run_callback_for_default():当设置default_val时,这将强制 执行选项回调或设置变量。->option_text(string):设置选项名称和 描述之间的文本。->force_callback():即使解析时不存在该选项,也会触发 选项回调或值设置。->trigger_on_parse():如果设置,当解析选项值时, 而不是在所有解析结束时,将执行该选项的回调和所有相关 验证检查。这可能会导致回调 被执行多次。也适用于位置选项。->callback_priority(CallbackPriority priority):更改 选项回调的执行顺序。有四个主要的回调调用点 可用。CallbackPriority::First在处理的最开始处执行, 在读取配置文件和解释环境变量之前。CallbackPriority::PreRequirementsCheck在配置和环境处理之后,但在 需求检查之前执行。CallbackPriority::Normal在需求检查之后,但在 重新抛出之前可能引发的任何异常之前执行。CallbackPriority::Last在异常处理完成后执行。对于 在每个位置,普通选项回调和帮助回调都会被调用。 它们之间的相对顺序可以使用对应的PreHelp变体来控制。CallbackPriority::FirstPreHelp在处理的最开始阶段, 在帮助回调之前执行普通选项 回调。CallbackPriority::PreRequirementsCheckPreHelp在配置和环境处理之后、 但在需求检查之前,在帮助回调之前执行普通选项 回调。CallbackPriority::NormalPreHelp在需求检查之后、 但在异常重新抛出之前,在帮助回调之前执行普通选项 回调。CallbackPriority::LastPreHelp在异常处理完成之后, 在帮助回调之前执行普通选项回调。当使用标准优先级(CallbackPriority::First、CallbackPriority::PreRequirementsCheck、CallbackPriority::Normal、CallbackPriority::Last)时,帮助回调在普通选项 回调之前执行。默认情况下,帮助回调使用CallbackPriority::First,而 普通选项回调使用CallbackPriority::Normal。此机制 提供了对选项值设置时机以及帮助或 需求检查发生时间的细粒度控制,从而能够精确地自定义处理 顺序。
这些选项返回 Option 指针,因此你可以将它们链接在一起,
甚至完全跳过存储该指针。each 函数接受任何具有
void(const std::string&) 签名的函数;当验证失败时,它应抛出
ValidationError。帮助消息会在前面加上父选项的名称。由于 each、check 和 transform 使用相同的
底层机制,你可以链接任意数量的选项,并且它们将按顺序
执行。通过 transform 添加的操作会按添加的逆序最先执行,而 check 和 each 则在
转换函数之后按添加顺序执行。如果你只想查看
未转换的值,请使用 .results() 获取结果的 std::vector<std::string>。
在命令行上,选项可以以以下形式给出:
-a(标志)-abc(标志可以组合)-f filename(选项)-ffilename(无需空格)-abcf filename(标志和选项可以组合)--long(长标志)--long_flag=true(带等号的长标志 -- 用于覆盖默认值)--file filename(空格)--file=filename(等号)
如果在应用程序或子命令中指定了 allow_windows_style_options(),
选项也可以以以下形式给出:
/a(标志)/f filename(选项)/long(长标志)/file filename(空格)/file:filename(冒号)/long_flag:false(带冒号的长标志,用于覆盖默认值)- Windows 风格的选项不允许组合短选项,也不允许值与短选项
之间没有分隔符,例如像
-选项那样
- Windows 风格的选项不允许组合短选项,也不允许值与短选项
之间没有分隔符,例如像
长标志选项可以带有一个 =<value> 来指定一个 false 值,或者为标志指定其他值。有关支持的值,请参阅 config files
以获取详细信息。注意:对于 windows 风格的选项,只能使用 = 或 :,使用空格会导致参数被
解释为位置参数。此语法可以覆盖默认
值,并可以通过使用 disable_flag_override() 来禁用。
额外的位置参数会导致程序退出,因此,如果您希望允许多余的
参数,建议至少使用一个带有 vector 的位置选项。如果在主 App 上设置了 .allow_extras(),则不会
出现错误。您可以使用 remaining 访问缺失的选项(如果
有子命令,app.remaining(true) 将获取所有剩余的选项,包括子
命令)。如果剩余参数将由另一个 App 处理,则
可以使用函数 remaining_for_passthrough() 按逆序获取剩余
参数,使得 app.parse(vector) 可以直接工作,
甚至可以在子命令回调中使用。
您可以使用 parse_order() 访问指向按原始顺序解析的选项的指针 vector。如果命令行中存在 -- 且该选项
未结束一个无限选项,则其后的所有内容仅为位置参数。
Validators
Validators 是用于检查或修改输入的结构,它们可用于验证输入是否满足特定条件,或将其转换为另一个值。它们通过 check 或 transform 函数添加。这两个函数的区别在于,checks 不会修改输入,而 transforms 可以修改输入,并且会在通过 check 添加的任何 Validators 之前执行。
CLI11 包含多个执行常见检查的 Validators。默认情况下,最常用的 Validators 是可用的。如果某些 Validators 不需要,可以通过使用
#define CLI11_DISABLE_EXTRA_VALIDATORS 1
默认验证器
无论定义如何,这些验证器始终可用。由于它们在内部使用或非常常用,因此无论标志如何设置,都将始终保持可用。
CLI::ExistingFile: 如果提供了文件,则要求该文件存在。CLI::ExistingDirectory: 要求目录存在。CLI::ExistingPath: 要求路径(文件或目录)存在。CLI::NonexistentPath: 要求路径不存在。CLI::FileOnDefaultPath: 最好用作转换,将检查文件是直接存在还是在默认路径中存在,并相应地更新路径。 有关更多详细信息,请参阅 转换验证器CLI::Range(min,max): 要求选项介于最小值和最大值之间(如有需要,请确保使用浮点数)。最小值默认为 0。CLI::PositiveNumber: 要求数字大于 0CLI::NonNegativeNumber: 要求数字大于或等于 0
可能被禁用的验证器
通过将 CLI11_DISABLE_EXTRA_VALIDATORS 设置为 1 来禁用,
或将 CLI11_ENABLE_EXTRA_VALIDATORS 设置为 1 来启用。默认情况下它们是
启用的。在 3.0 版本中,这些验证器很可能会默认禁用,并
完全由 CLI11_ENABLE_EXTRA_VALIDATORS 选项控制。这些
验证器使用频率较低,或者模板繁重,需要额外的
计算时间,这可能对某些用例没有价值。
-
CLI::IsMember(...): 要求选项必须是给定集合的成员。有关更多详细信息,请参阅 Transforming Validators。 -
CLI::Transformer(...): 使用映射修改输入。有关更多详细信息,请参阅 Transforming Validators。 -
CLI::CheckedTransformer(...): 使用映射修改输入,并要求 输入要么在集合中,要么已经是集合的输出之一。有关更多详细信息,请参阅 Transforming Validators。 -
CLI::AsNumberWithUnit(...): 通过匹配单位并将数字乘以相应的因子来修改<NUMBER> <UNIT>对。它可以 用作转换器的基础,接受诸如大小值 (1 KB) 或持续时间 (0.33 ms) 之类的东西。 -
CLI::AsSizeValue(...): 将诸如100b、42 KB、101 Mb、11 Mib之类的输入转换为绝对值。KB可以配置为解释为 10^3 或 2^10。 -
CLI::Bound(min,max): 修改输入,使其始终介于 min 和 max 之间(如有需要,请确保使用浮点数)。Min 默认为 0。如果 无法转换,将产生错误。 -
CLI::Number: 要求输入必须是数字。 -
CLI::ValidIPV4: 要求选项必须是有效的 IPv4 字符串,例如'255.255.255.255'、'10.1.1.7'。 -
CLI::TypeValidator<TYPE>:要求选项可以转换为 指定的类型,例如CLI::TypeValidator<unsigned int>()将要求 输入可以转换为unsigned int,无论最终 转换如何。
Extra Validators
新的验证器将放入必须通过
设置 CLI11_ENABLE_EXTRA_VALIDATORS 为 1 才能显式启用的代码部分中
CLI::ReadPermissions: 要求给定的文件或文件夹存在且具有 读取权限。需要 C++17。CLI::WritePermissions: 要求给定的文件或文件夹存在且具有 写入权限。需要 C++17。CLI::ExecPermissions: 要求给定的文件存在且具有执行 权限。需要 C++17。CLI::FileSizeValidator(min_size, max_size = 0): 🆕 要求文件 存在且其字节大小至少为min_size。如果max_size大于 0,大小也不得超过该值。需要 C++17。CLI::NonEmptyFile: 🆕 要求文件存在且不为空。等同于CLI::FileSizeValidator(1)。需要 C++17。
Validator 用法
启用这些 Validator 后,只需将名称传递给选项的
check 或 transform 方法即可使用
->check(CLI::ExistingFile);
->check(CLI::Range(0,10));
验证器可以使用 & 和 | 进行合并,并使用 ! 进行反转。例如:
->check(CLI::Range(0,10)|CLI::Range(20,30));
将生成一个检查,以确保值在 0 到 10 之间或 20 到 30 之间。
->check(!CLI::PositiveNumber);
将生成一个检查,判断数值是否小于或等于 0。
转换验证器
有几个内置的验证器,当与
transform 函数一起使用时,可以转换值。如果它们还执行一些检查,那么它们也可以
check,但有些在这种情况下可能不会执行任何操作。
CLI::Bound(min,max)会将值限制在最小值和最大值之间,超出 该范围的值将被限制为最小值或最大值,如果值无法 转换,它将失败并生成一个ValidationErrorIsMember验证器允许你指定一组预定义选项。你可以 向此验证器传递任何容器或可复制指针(包括指向 容器的std::shared_ptr);该容器只需可迭代并具有::value_type。键类型应可从字符串转换,你可以 直接使用初始化列表。如果你需要稍后修改该集合, 指针形式允许你这样做;类型消息和检查将 正确引用集合的当前版本。传入的容器可以 是 set、vector 或类似 map 的结构。如果在transform方法中 使用,输出值将是匹配的键,因为它可能会被过滤器修改。
在指定一组选项后,你还可以指定形式为
T(T) 的“过滤器”函数,其中 T 是值的类型。最常见的选择
可能是 CLI::ignore_case 和 CLI::ignore_underscore,以及
CLI::ignore_space。这些都作用于字符串,但也可以定义
作用于其他类型的函数。以下是一些 IsMember 的示例:
CLI::IsMember({"choice1", "choice2"}): 从精确匹配到选项。CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore): 也匹配诸如Choice_1之类的内容。CLI::IsMember(std::set<int>({2,3,4})): 大多数容器和类型都适用;你 只需要std::begin、std::end和::value_type。CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}})): 你 可以使用 maps;在->transform()中,它们会用匹配的键替换匹配的值。map 的 value 成员在IsMember中不会被使用,因此它可以是 任何类型。auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>{"one", "two"}); CLI::IsMember(p): 你可以稍后修改p。Transformer和CheckedTransformerValidators 将一个值转换 为另一个值。任何容器或可复制的指针(包括std::shared_ptr)指向生成值对的容器都可以传递给这些Validator's; 容器只需要是可迭代的,并且具有由对组成的::value_type。键类型应可从字符串转换,值 类型应可转换为字符串。如果你愿意,可以直接使用初始化列表。如果你需要稍后修改 map,指针形式 允许你这样做;描述消息将正确引用 map 的当前版本。Transformer不进行任何检查,因此 map 中不存在的值 将被忽略。CheckedTransformer会额外验证 该值是否为 map 键值之一(如果是,则进行转换),或者是预期输出值之一,否则将生成ValidationError。使用check放置的 Transformer 不会执行任何操作。
在指定选项 map 后,你还可以像在
CLI::IsMember 中那样指定 "filter"。以下是一些 Transformer 的示例(Transformer 和 CheckedTransformer
在示例中可以互换):
CLI::Transformer({{"key1", "map1"},{"key2","map2"}}): 从键值中选择 并生成映射值。CLI::Transformer(std::map<std::string,int>({{"two",2},{"three",3},{"four",4}})): 大多数类映射容器均可工作,::value_type需要生成某种 形式的配对。CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}})): 你可以使用映射;在->transform()中,这些会用值替换匹配的键。CheckedTransformer还要求值要么匹配其中一个键,要么匹配其中一个已知输出。auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>{{"key1", "map1"},{"key2","map2"}}); CLI::Transformer(p): 你可以稍后修改p。TransformPairs<T>是std::vector<std::pair<std::string,T>>的别名
注意:如果在 IsMember、Transformer 或
CheckedTransformer 中使用的容器具有类似 std::unordered_map 或
std::map 的 find 函数,则使用该函数进行搜索。如果没有
find 函数,则执行线性搜索。如果存在过滤器,则先执行快速搜索,
如果失败,则对键值执行带有过滤器的线性搜索。
-
CLI::FileOnDefaultPath(default_path): 可用于检查默认路径中的文件。 如果用作转换,它将首先检查文件是否存在, 如果存在则不再执行其他操作,如果不存在则尝试为文件添加默认 Path 并再次在该处搜索。如果文件不存在,通常 会返回错误,但可以使用CLI::FileOnDefaultPath(default_path, false)禁用此行为。这允许使用多个转换调用 将多个路径链接起来。 -
CLI::EscapedString: 可用于处理转义字符串。该处理 与用于 TOML 配置文件的处理等效,参见 TOML strings。有两个显著例外。 ` 也可用作字面字符串表示法,并且它还允许二进制 字符串表示法,参见 binary strings。 转义字符串处理会移除存在的外层引号,"将 指示可能包含转义序列的字符串,'和 ` 将指示 字面字符串并移除引号,但不会处理任何转义序列。这与配置文件中使用的转义处理相同。
Validator operations
Validators 是可复制的,并且有一些可以对其执行的操作
以更改设置。大多数内置的 Validators 都有一个默认描述,
该描述显示在帮助中。可以通过
.description(validator_description) 进行更改。Validator 的名称,这对于
从 Option 的 get_validator(name) 方法中稍后引用很有用,可以
通过 .name(validator_name) 设置。Validator 的操作函数可以通过
.operation(std::function<std::string(std::string &>) 设置。.active()
函数可以激活或停用操作中的 Validator。可以将 validator
设置为仅应用于输出的特定元素。例如,在
pair 选项 std::pair<int, std::string> 中,第一个元素可能需要是
正整数,而第二个元素可能需要是有效文件。
.application_index(int) 函数可以指定这一点。它是从零开始的,
负索引适用于所有值。
opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
opt->check(CLI::Validator(CLI::ExistingFile).application_index(1));
所有验证器操作函数都返回一个 Validator 引用,以便进行链式调用。例如
opt->check(CLI::Range(10,20).description("range is limited to sensible values").active(false).name("range"));
将指定一个名为 "range" 的选项检查,但暂时将其停用。该检查之后可以通过
opt->get_validator("range")->active();
自定义验证器
可以通过以下方式创建带有自定义函数的验证器对象
CLI::Validator(std::function<std::string(std::string &)>,validator_description,validator_name="");
或者如果操作函数是稍后设置的,它们可以在此时创建
CLI::Validator(validator_description);
也可以创建 CLI::Validator 的子类,在这种情况下,它还可以设置自定义的描述函数和操作函数。
一个示例位于
自定义验证器示例。
示例。如果你希望在多个位置重用验证器,或者验证器是可变且检查依赖于其他操作或具有可变性,那么 check 和 transform 操作也可以接受指向验证器的 shared_ptr。请注意,
在这种情况下,不建议对 check 和 transform 操作使用同一个对象,因为 check 会修改对象上的一些内部标志,
因此该对象将无法用于 transform 操作。
查询验证器
一旦加载到 Option 中,就可以通过以下方式获取指向命名验证器的指针
auto *validator = opt->get_validator(name);
这将检索具有给定名称的 Validator,或抛出
CLI::OptionNotFound 错误。如果未提供名称或名称为空,则返回第一个
未命名的 Validator,或者如果只有一个 Validator,则返回该 Validator。
或
auto *validator = opt->get_validator(index);
这将返回一个位于其应用索引处的验证器,该索引不一定与其定义顺序一致。如果给定的索引无效,指针可能为 nullptr。验证器具有几个用于查询当前值的函数:
get_description():将返回一个描述字符串get_name():将返回验证器名称get_active():将返回当前活动状态,如果验证器处于活动状态则为 true。get_application_index():将返回当前应用索引。get_modifying():如果允许验证器修改输入,则返回 true,这可以通过non_modifying()方法进行控制,不过建议让check和transform选项方法在需要时对其进行操作。
获取结果
在大多数情况下,最快且最简单的方法是通过在 add_* 函数之一中指定的回调或变量返回结果。但有些情况下,这样做是不可能的或不可取的。对于这些情况,可以通过以下函数之一获取结果。请注意,这些函数将在调用期间执行任何类型转换和处理,因此不应在性能关键代码中使用:
->results(): 按给定顺序获取包含所有结果的字符串向量。->results(variable_to_bind_to): 根据 MultiOptionPolicy 获取结果,并像add_option_function一样将其转换为 变量。Value=opt->as<type>(): 如果可能,直接以指定类型返回结果或默认值,可以是向量以返回所有结果,也可以是非向量以根据 MultiOptionPolicy 就地获取结果。如果预期结果将作为向量使用,建议 在选项上使用->expected(CLI::detail::expected_max_vector_size)或allow_extra_args()以告知 CLI11 预期并允许向量参数。
子命令
子命令是调用一组新选项和功能的关键词。例如,
git 命令有一长串子命令,如 add 和
commit。每个子命令都可以有自己的选项和实现。CLI11 支持子命令,并且可以无限嵌套。要添加子命令,请调用
add_subcommand 方法并传入名称和可选的描述。这将返回一个
指向 App 的指针,其行为与主应用程序相同,可以接受选项或
进一步的子命令。在子命令上添加 ->ignore_case() 以允许接受任何
大小写变体。->ignore_underscore() 类似,但
针对下划线。子命令从父命令继承当前设置。您不能在相同级别添加多个匹配的子命令名称(包括
ignore_case 和 ignore_underscore)。
如果你希望要求至少提供一个子命令,请在父应用上使用
.require_subcommand()。你也可以选择性地指定需要提供的子命令的确切
数量。如果你提供两个参数,这将设置允许的最小和最大数量。最大允许数量设为 0 将允许
无限数量的子命令。作为一个便捷的快捷方式,单个负值 N
将设置为“最多 N”个值。限制最大数量可以防止
与先前子命令名称匹配的参数被匹配。
如果命令行上已解析了 App(主命令或子命令),则 ->parsed
将为 true(或直接转换为 bool)。所有 App 都有一个
get_subcommands() 方法,该方法返回命令行上传递的子命令指针列表。还
提供了一个 got_subcommand(App_or_name) 方法,用于检查命令行上是否收集了
App 指针或字符串名称。
然而,在许多情况下,使用应用的回调功能可能更简单。
每个应用都有一组可以在解析的不同阶段执行的回调;一个 C++ lambda 函数(通过捕获获取解析值)可以用作
回调定义函数的输入。如果你抛出 CLI::Success 或
CLI::RuntimeError(return_value),你甚至可以通过
回调退出程序。
允许使用多个子命令,以支持类似 [Click][click] 的命令序列(顺序保持不变)。同一个子命令可以被触发多次,但所有位置参数都将优先于该子命令的第二次及后续调用。->count() 在子命令上将返回该子命令被调用的次数。除非设置了 .immediate_callback() 标志,或者通过 parse_complete_callback() 函数指定了回调,否则子命令回调只会触发一次。final_callback() 只触发一次。在这种情况下,回调在子命令参数完成时执行,但位于该子命令的参数解析之后,并且可以被触发多次。请注意,parse_complete_callback() 在处理任何配置文件之前执行。
final_callback() 在配置文件处理之后执行。
子命令也可以具有空名称,方法是在调用 add_subcommand 时将名称设为空字符串,或者不传递任何参数。无名称子命令的功能类似于主 App 中的组。请参阅 Option groups 以了解其工作原理。如果某个选项未在主 App 中定义,则所有无名称子命令也会被检查。这允许在可组合的组中定义选项。add_subcommand 函数有一个重载,用于添加 shared_ptr<App>,因此子命令可以在不同的组件中定义,并合并到主 App 中,或者合并到多个 Apps 中。允许存在多个无名称子命令。无名称子命令的回调仅在解析了该子命令的任何选项时才会触发。通过 add_subcommand 方法给出的子命令名称与选项名称具有相同的限制。
子命令中的选项或标志可以使用点符号直接指定
--subcommand.long=val(long subcommand option)--subcommand.long val(long subcommand option)--subcommand.f=val(short form subcommand option)--subcommand.f val(short form subcommand option)--subcommand.f(short form subcommand flag)--subcommand1.subsub.f val(short form nested subcommand option)
在此形式中使用点符号等同于 --subcommand.long <args> =>
subcommand --long <args> ++。嵌套子命令也有效,sub1.subsub 将
触发 sub1 中的 subsub 子命令。这等同于 "sub1 subsub"。
根据 TOML 标准,允许在子命令名称周围使用引号进行此类指定。
这包括允许使用转义序列。例如
"subcommand".'f' 或 "subcommand.with.dots".arg1 = value。
Subcommand options
主应用程序和子命令以及 option_groups 支持多个选项。这些选项如下:
.ignore_case(): 忽略此子命令的大小写。会被添加的子命令继承,因此通常用于主App。.ignore_underscore(): 忽略子命令名称中的任何下划线。 会被添加的子命令继承,因此通常用于主App。.allow_windows_style_options(): 允许以/s /long /file:file_name.ext的形式解析命令行选项。此选项不会更改在add_option调用中指定选项的方式,也不会影响以-s --long --file=file_name.ext形式处理选项的能力。.allow_non_standard_option_names(): 允许指定单-长 形式选项名称。不推荐这样做,但提供此功能以支持重构现有接口。如果此修饰符在应用或 子命令上启用,选项或标志可以像往常一样指定,但不会抛出异常,而是允许使用单破折号的长形式选项名称。 不允许以与单破折号长形式名称相同字符开头的单字符短选项;例如,-s和-single不 允许在同一应用程序中使用。.allow_subcommand_prefix_matching(): 如果启用此修饰符, 子命令的无歧义前缀部分将匹配。例如upgrade_package将匹配upgrade_、upg、u,只要没有其他 子命令也会匹配。它还禁止子命令名称是另一个子命令的完整 前缀。.fallthrough(): 允许额外的未匹配选项和位置参数“穿透” 并在父选项上匹配。子命令默认允许 即“fall through”(穿透),它们会首先尝试匹配当前的 子命令,如果失败则会逐级向上检查父级以匹配 子命令。此行为可通过subcommand_fallthrough(false)禁用。.subcommand_fallthrough():允许子命令“fall through”(穿透)并 匹配父级选项。禁用此行为可防止同一层级的其他子命令被匹配。在某些 子命令与位置参数可能存在歧义的情况下,此行为可能有用。 默认值为 true。.configurable():允许子命令由配置 文件触发。默认情况下,配置文件中的子命令选项不会触发 子命令,而只会更新默认值。.disable():指定子命令已禁用,如果提供 bool 值,它将启用或禁用该子命令或选项组。.disabled_by_default():指定在解析开始时 应禁用该子命令/option_group。这对于允许某些 Subcommands 触发其他子命令非常有用。.enabled_by_default():指定在每次解析开始时 应启用该子命令/option_group。这对于允许某些 Subcommands 禁用其他子命令非常有用。.silent():指定子命令是静默的,意味着如果使用了它, 它不会出现在子命令列表中。这允许将子命令用作 修饰符.validate_positionals():指定位置参数应在匹配之前 通过验证。验证通过transform、check和each用于选项。如果参数未通过验证,这不算错误, 匹配将继续进行到下一个可用的位置参数或额外参数。.validate_optional_arguments(): 指定可选参数在分配给选项之前应通过 验证。验证通过选项的transform、check和each指定。如果参数未通过 验证,这不算错误,匹配将继续进行到下一个可用的 位置子命令或额外参数。.excludes(option_or_subcommand): 如果给定选项指针或指向 另一个子命令的指针,这些子命令不能一起使用。对于 选项,如果传递了该选项,则不能使用子命令,并且 将生成错误。.needs(option_or_subcommand): 如果给定选项指针或指向 另一个子命令的指针,子命令将要求在使用此子命令之前已 给出给定的选项,这发生在执行任何回调之前或解析完成之后。.require_option(): 要求使用 1 个或多个选项或选项组。.require_option(N): 要求N个选项或选项组,如果N>0,或最多N个,如果N<0。N=0重置为默认的 0 个或多个。.require_option(min, max): 显式设置允许的最小和最大选项或 选项组数量。将max设置为 0 意味着选项数量无限制。.require_subcommand(): 要求 1 个或多个子命令。.require_subcommand(N): 如果N>0,则要求N个子命令,或如果N<0,则最多N个。N=0重置为默认的 0 个或多个。.require_subcommand(min, max): 显式设置允许的最小和最大 子命令。将max设置为 0 表示无限制。.add_subcommand(name="", description=""):添加一个子命令,返回指向内部存储的子命令的指针。.add_subcommand(shared_ptr<App>):通过 shared_ptr 添加一个子命令,返回指向内部存储的子命令的指针。.remove_subcommand(App):从应用程序或子命令中移除一个子命令。.got_subcommand(App_or_name):检查是否从命令行接收到了某个子命令。.get_subcommands(filter):匹配特定过滤函数的子命令列表。.add_option_group(name="", description=""):向 App 添加一个 选项组,选项组是一种专门用于包含选项组或其他组的子命令, 用于控制选项之间的交互方式。.get_parent():获取父级 App,如果在主 App 上调用则返回nullptr。.get_option(name):通过选项名称获取选项指针,如果指定的选项不可用则抛出异常, 无名称的子命令也会与父级一起搜索具有 fallthrough 的子命令。.get_option_no_throw(name):通过选项名称获取选项指针。如果选项不可用,此 函数将返回nullptr而不是抛出异常。无论 fallthrough 状态如何,此方法都不会搜索 option_options 或无名称子命令的父级 🆕,此行为与get_option略有不同。.get_options(filter):获取所有已定义的选项指针列表(对于处理应用程序以生成自定义输出格式很有用)。 如果在子命令上使用,如果子命令具有 fallthrough(且不是无名称的 🆕), 也会获取父级应用程序中的选项。.parse_order(): 获取选项指针列表,顺序为其被解析的顺序(包括重复项)。.formatter(std::shared_ptr<FormatterBase> fmt): 为 help 设置自定义格式化器。.formatter_fn(fmt),签名为std::string(const App*, std::string, AppFormatMode)。有关 更多详细信息,请参阅 [formatting][]。.config_formatter(std::shared_ptr<Config> fmt): 设置自定义配置 格式化器以生成配置文件,更多详细信息请参阅 [Config files][config].description(str): 设置/更改描述。.get_description(): 访问描述。.alias(str): 为子命令设置别名,这允许子命令通过多个名称 被调用。.parsed(): 如果此子命令在命令行上给出,则为 True。.count(): 返回子命令被调用的次数。.count(option_name): 返回特定选项被 调用的次数。.count_all(): 返回特定子命令处理的参数总数,在主 App 上返回处理的命令总数。.name(name): 添加或更改名称。.callback(void() function): 为 app 设置回调。根据immediate_callback的值,设置pre_parse_callback或final_callback。有关 一些额外详细信息,请参阅 Subcommand callbacks。.parse_complete_callback(void() function): 设置解析完成时运行的回调。对于子命令,这将在单个子命令完成时执行,并且可以执行多次。请参阅 Subcommand callbacks 以获取一些额外详细信息。.final_callback(void() function): 设置在所有处理结束时运行的回调。这是在返回之前执行的最后一件事。请参阅 Subcommand callbacks 以获取一些额外详细信息。.immediate_callback(): 指定子命令的回调 是否应作为parse_complete_callback(true) 或final_callback(false) 运行。 当用于主应用程序时,如果子命令的回调未设置immediate_callback标志,则会在子命令回调之前执行主应用程序回调。如果希望控制回调的顺序和时机, 建议直接使用parse_complete_callback或final_callback,而不是使用callback和immediate_callback。 如果需要,可以使用immediate_callback来交换它们。.pre_parse_callback(void(std::size_t) function): 设置一个回调, 在应用程序的第一个参数处理完成后执行。有关更多详细信息,请参阅 子命令回调。.allow_extras(): 如果剩余额外参数,则不抛出错误。.allow_extras(CLI::ExtrasMode): 指定处理未识别 参数的方法。CLI::ExtrasMode::Error: 对未识别的参数生成错误。与.allow_extras(false)相同。CLI::ExtrasMode::ErrorImmediately: 在解析未识别的选项时立即生成错误`。CLI::ExtrasMode::Ignore: 忽略任何未识别的参数,不生成 错误。CLI::ExtrasMode::AssumeSingleArgument: 在未识别的标志或 选项参数之后,如果后续参数不是标志或选项参数, 则将其视为参数,即使它本应属于位置参数,也将其视为未识别。CLI::ExtrasMode::AssumeMultipleArguments:在未识别的标志或 选项参数之后,如果后续参数不是标志或选项 参数,则将其视为参数,即使它们本应属于位置参数,也将其视为未识别。
CLI::ExtrasMode::Capture: 捕获所有未识别的参数,与.allow_extras的true相同:.positionals_at_end(): 指定位置参数作为最后的 参数出现,如果遇到意外的位置参数则抛出错误。.prefix_command(): 类似于allow_extras,但在遇到 第一个未识别项时立即停止处理。所有后续参数都放置在 remaining_arg 列表中。它非常适合让您的应用程序或子命令成为 调用另一个应用程序的“前缀”。.prefix_command(bool): 启用或禁用前缀命令模式。prefix_command(true)等同于prefix_command(CLI::PrefixCommandMode::On),而prefix_command(false)等同于prefix_command(CLI::PrefixCommandMode::Off)。.prefix_command(CLI::PrefixCommandMode): 直接指定前缀命令模式。PrefixCommandMode::On和PrefixCommandMode::Off等同于prefix_command(true)和prefix_command(false)。使用PrefixCommandMode::SeparatorOnly调用时,仅当子命令分隔符为--时才会触发前缀命令模式;其他未识别的参数被视为 错误,除非启用了allow_extras。使用PrefixCommandMode::PositionalOnly🆕 调用时,仅当遇到第一个位置参数或分隔符--时才会触发前缀命令模式;未识别的选项 不会停止处理,而是被收集到 remaining_arg 列表中。.usage(message): 替换在帮助字符串开头 描述之后出现的文本。.usage(std::string()): 设置一个回调,用于生成将在帮助字符串开头 描述之后出现的字符串。.footer(message): 设置出现在帮助字符串底部的文本。.footer(std::string()): 设置一个回调,用于生成将在帮助字符串末尾 出现的字符串。.set_help_flag(name, message): 设置帮助标志的名称和消息,返回 指向已创建选项的指针。.set_version_flag(name, versionString or callback, help_message): 设置 版本标志名称和版本字符串或回调函数以及可选的帮助消息, 返回指向已创建选项的指针。.set_help_all_flag(name, message): 设置帮助所有标志名称和消息, 返回指向已创建选项的指针。展开子命令。.failure_message(func): 设置失败消息函数。提供两个:CLI::FailureMessage::help和CLI::FailureMessage::simple(默认)。.group(name): 设置组名称,默认为"Subcommands"。将 名称设置为空字符串将隐藏子命令。[option_name]: 获取由option_name指定的选项的 const 指针 例如app["--flag1"]将获取指向 "--flag1" 值选项的指针,app["--flag1"]->as<bool>()将获取 标志的命令行结果。如果选项 名称无效,该操作将抛出异常。
[!NOTE]
如果你有一组固定数量的必需位置选项,它们会在子命令名称之前匹配。
{}是一个空的过滤函数,任何位置参数都会在重复的子命令名称之前匹配。
回调
子命令具有三个可选的回调,它们在不同的处理阶段执行。preparse_callback 在子命令或应用程序的第一个参数处理完成后执行一次,并提供一个表示剩余待处理参数数量的参数。对于主应用程序,第一个参数被视为程序名称;对于子命令,第一个参数是子命令名称。对于 Option 组和无名子命令,第一个参数是在该组中的第一个参数或子命令处理完成之后的参数。第二个回调在解析完成后执行。这被称为
parse_complete_callback。对于子命令,这在解析后立即执行,如果子命令被多次调用,则可能执行多次。在主应用程序中,此回调在所有子命令的
parse_complete_callback 执行完毕后,但在子命令或选项组中的任何
final_callback 调用之前执行。如果主应用程序或
子命令具有配置文件,则配置文件中的数据不会反映在命名子命令的
parse_complete_callback 中。对于 option_group,
parse_complete_callback 在主应用程序的 parse_complete_callback 之前执行,但在
config_file 加载之后(如果指定了)。
final_callback 在所有处理完成后执行。在
parse_complete_callback 在主应用程序上执行后,使用的子命令
final_callback 随后执行,接着是选项组的“最终回调”。最后执行的是 final_callback 用于 main_app。
例如,假设应用程序设置如下
app.parse_complete_callback(ac1);
app.final_callback(ac2);
auto sub1=app.add_subcommand("sub1")->parse_complete_callback(c1)->preparse_callback(pc1);
auto sub2=app.add_subcommand("sub2")->final_callback(c2)->preparse_callback(pc2);
app.preparse_callback( pa);
... A bunch of other options
然后命令行被给出为
program --opt1 opt1_val sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub1opt2 sub2 --sub2opt2 val
pa将在解析任何值为 13 的参数之前被调用。pc1将在处理sub1命令后立即被调用,其值为 10。c1将在遇到sub2命令时被调用。pc2将在遇到sub2命令后以值 6 被调用。c1将在遇到第二个sub2命令后再次被调用。ac1将在处理完所有参数后被调用c2将在处理完所有参数后被调用一次。ac2将在所有较低层级的回调执行完毕后最后被调用。
当满足以下任一条件时,子命令被视为终止。
- 没有更多参数需要处理
- 遇到另一个无法放入子命令可选位置参数槽位的子命令
- 遇到
positional_mark(--) 且子命令中没有可用的位置参数槽位。 - 遇到
subcommand_terminator标记 (++)
在执行 parse_complete_callback 之前,所有包含的选项都会先于回调触发进行处理。如果再次调用带有 parse_complete_callback 的子命令,则包含的选项会被重置,并可以再次触发。
Option groups
The subcommand method
.add_option_group(name,description)
将创建一个选项组,并返回指向它的指针。description 的参数是可选的,可以省略。选项组允许创建一组选项,类似于 options 上的 groups 函数,但具有额外的控制和需求。它们允许将特定的选项集作为整体进行组合和控制。示例请参见
range example。
选项组是 App 的特化,因此所有
functions 适用于 App 或子命令的函数也适用于选项组。可以使用 add 函数在选项组中创建选项,就像子命令一样,或者可以通过添加先前创建的选项。在选项组中给出的名称不得包含换行符或空字符。
ogroup->add_option(option_pointer);
ogroup->add_options(option_pointer);
ogroup->add_options(option1,option2,option3,...);
此函数中使用的选项指针必须是选项组父应用中定义的选项,否则将生成错误。 子命令也可以通过以下方式添加
ogroup->add_subcommand(subcom_pointer);
这会导致子命令从其父级移动到选项组中。
选项组中的选项会在主应用中的任何选项之后被搜索以匹配命令行,因此主应用中的任何位置参数都会首先被匹配。因此,在使用位置参数和选项组时,必须注意顺序。选项组与 excludes 和
require_options 方法配合良好,因为应用程序会将选项组视为单个选项,用于计数和必需性检查,并且如果选项组中包含的任何选项或子命令被使用,则该选项组将被视为已使用。选项组允许指定必需性,例如要求一个组中的 3 个选项中的 1 个,以及另一个组中的 3 个选项中的 1 个。选项组也可以包含其他组。禁用选项组将关闭组内的所有选项。
CLI::TriggerOn 和 CLI::TriggerOff 方法是辅助函数,允许使用一个组中的选项/子命令来触发另一个组的开启或关闭。
CLI::TriggerOn(group1_pointer, triggered_group);
CLI::TriggerOff(group2_pointer, disabled_group);
这些函数使用了 preparse_callback、enabled_by_default() 和
disabled_by_default。触发的组可以是组指针的向量。
这些方法每个组应仅使用一次,并且会覆盖底层函数的任何先前
使用。通过使用执行更多操作的自定义 preparse_callback 函数,可以采用类似的方法
实现更复杂的安排。
额外的辅助函数 deprecate_option 和 retire_option 可用于
弃用或退役选项
CLI::deprecate_option(option *, replacement_name="");
CLI::deprecate_option(App,option_name,replacement_name="");
将指定该选项已弃用,这将在帮助中显示一条消息,并在首次使用时发出警告。已弃用的选项功能正常,但会在帮助中添加一条消息,并在首次使用时显示警告。
CLI::retire_option(App,option *);
CLI::retire_option(App,option_name);
将创建一个默认不执行任何操作的选项,并在首次使用时显示警告,表明该选项已弃用且无效。如果该选项存在, 它将被替换为一个接受相同参数的虚拟选项。
如果传入空字符串作为选项组名称,整个组将在帮助结果中隐藏。例如。
auto hidden_group=app.add_option_group("");
将创建一个组,使得该组中的任何选项都不会在帮助字符串中显示。就帮助显示而言,如果选项组名以“+”开头,则在帮助和 get_options 中会被视为未分组。例如:
auto added_group=app.add_option_group("+sub");
在这种情况下,帮助输出将不会引用选项组,且其中的选项在大多数情况下将被视为父级的一部分。
配置文件
app.set_config(option_name="",
default_file_name="",
help_string="Read an ini file",
required=false)
如果此函数不带任何参数调用,它将移除配置文件
选项(类似于 set_help_flag)。设置配置选项是特殊的。如果它
存在,它将与普通命令行参数一起被读取。如果文件
存在,它将被读取,并且除非 required 为
true,否则不会抛出错误。配置文件默认采用 [TOML][] 格式,尽管
默认读取器也可以接受 INI 格式的文件。配置读取器
可以读取 TOML 文件的大多数方面,包括字面量和可能包含
转义序列的字符串、数字分隔符以及多行字符串,并
将它们通过 CLI11 解析器处理。熟练的用户可以添加其他格式,一些
变体可以通过默认格式化程序中的自定义点获得。
TOML 文件的示例:
# Comments are supported, using a #
# The default section is [default], case-insensitive
value = 1
value2 = 123_456 # a string with separators
str = "A string"
str2 = "A string\nwith new lines"
str3 = 'A literal "string"'
vector = [1,2,3]
str_vector = ["one","two","and three"]
# Sections map to subcommands
[subcommand]
in_subcommand = Wow
sub.subcommand = true
"sub"."subcommand2" = "string_value"
或等效地采用 INI 格式
; Comments are supported, using a ;
; The default section is [default], case-insensitive
value = 1
str = "A string"
vector = 1 2 3
str_vector = "one" "two" "and three"
; Sections map to subcommands
[subcommand]
in_subcommand = Wow
sub.subcommand = true
名称和参数前后的空格将被忽略。多个参数
以空格分隔。一组引号会被移除,同时保留空格
(与命令行行为相同)。布尔选项可以是 true、on、1、
yes、enable;或 false、off、0、no、disable(不区分大小写)。
节(以及 . 分隔的名称)被视为子命令(注意:这并不
一定意味着该子命令被传递,它只是设置了“默认值”)。
你不能设置仅限位置参数。如果子命令上设置了 configurable 标志,
则可以从配置文件中触发子命令。此时
使用 [subcommand] 表示法将触发子命令,并使其表现得
如同在命令行上一样。
要从传递的参数中打印配置文件,请使用以下 重载之一:
.config_to_str():仅打印活动值。.config_to_str(bool default_also, bool write_description = false):打印 活动值,或者如果default_also为true,则包含默认参数。 此重载