C++库 CLI11 介绍和使用
CLI11 是一个 single-header-only 库, 用于命令行解析。 用户只需要包含一个头文件即可使用:
#include <CLI11.hpp>
CLI11 的官方仓库是: https://github.com/CLIUtils/CLI11.git
官方 README.md 提供的最基本用法:
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;
}
显示帮助
对于 CLI11 写的应用程序, 万能的查看命令行参数的方式, 是指定 --help
. 例如前一节给出的代码,编译出的可执行文件 cli_test:
./cli_test --help
输入:
My App
Usage: ./cli_test [OPTIONS]Options:-h,--help Print this help message and exit-f,--file TEXT A help string
可以看到, -f,--file TEXT
被打印出来, 很直观.
命名扫盲:flag 和 option
- flag 指的是开关, 用
-
开头, 例如-a
. - option 指的是选项,有 name 和 value, 例如
-f filename
. - positional option 指的是位置参数, 不以
-
开头,用户提供 value, 程序内部为它维护 name, 例如./program value1 value2
.
稍微复杂的形式:
--long
: long flag (长flag)--long_flag=true
: 带等号的长 flag, 目的是覆盖默认值--file filename
: option--file=filename
: option-a -b
: 多个 flag-ffilename
: option 的 name 和 value 之间可以省略空格
更复杂的:
-ab
: flag 可以合并-abf filename
: flag 和 option 可以组合
如果调用了 allow_windows_style_options()
, 还可以传入 windows 风格的 flag 或 option, 也就是 /
替代 -
, 例如:
/a
: 这是一个 flag/f filename
: 这是一个 option/long
: 长 flag/file filename
: option, 空格分隔 name 和 value/file:filename
: option, 冒号(:
)分隔 name 和 value/long_flag:false
: 长 flag 使用:
覆盖默认值
ensure_utf8(argv)
CLI11 从 v2.4.0 开始, 在 App
类提供 ensure_utf8()
成员函数.
argv = app.ensure_utf8(argv);
是在做什么?
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;
}
在 Windows 上, 使用 unicode 作为参数时你的 main()
函数可能已经接收到损坏的 Unicode。 这种情况下解析 argv
会导致得到不正确的字符串。 推荐的解决方法, 是在所有参数被解析之前调用 app.ensure_utf8(argv)
: 如果 argv 已经是 UTF-8(比如 Linux 和 macOS 系统)则返回为了原本的 argv, 如果是 Windows 则会申请的内存, 并将原本的 argv 转换到新的 argv 中, 这个转换指的是 decode, 是基于 win32 API 实现的:
int main(int argc, char** argv)
{CLI::App app;printf("argv = %p\n", argv); // ...A62000argv = app.ensure_utf8(argv); // 申请新的内存printf("argv = %p\n", argv); // ...A88120CLI11_PARSE(app, argc, argv);return 0;
}
对应的源码如下:
- 数据成员
normalized_argv_
存放被正确解析后的参数 - 数据成员
normalized_argv_view_
则是normalized_argv_
的 view, 它的 .data() 是返还给用户的 “新的 argv"
namespace CLI {
class App {...
#ifdef _WIN32/// When normalizing argv to UTF-8 on Windows, this is the storage for normalized args.std::vector<std::string> normalized_argv_{};/// When normalizing argv to UTF-8 on Windows, this is the `char**` value returned to the user.std::vector<char *> normalized_argv_view_{};
#endif/// Convert the contents of argv to UTF-8. Only does something on Windows, does nothing elsewhere.CLI11_NODISCARD char **ensure_utf8(char **argv);
};CLI11_NODISCARD CLI11_INLINE char **App::ensure_utf8(char **argv) {
#ifdef _WIN32(void)argv;normalized_argv_ = detail::compute_win32_argv();if(!normalized_argv_view_.empty()) {normalized_argv_view_.clear();}normalized_argv_view_.reserve(normalized_argv_.size());for(auto &arg : normalized_argv_) {// using const_cast is well-defined, string is known to not be const.normalized_argv_view_.push_back(const_cast<char *>(arg.data()));}return normalized_argv_view_.data();
#elsereturn argv;
#endif
}
API: 添加 flag 和 option
CLI::App
类提供两个成员函数,分别用于添加 flag 和 option
add_option()
add_option()
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"); // 关注这里app.add_option(option_name, help_string=""); // 关注这里CLI11_PARSE(app, argc, argv);return 0;
}
其他
本篇算是 CLI11 库的小白扫盲贴, 谈不上平均水平的用法, 主要还是个人简单记录。
CLI11 添加 option 和 flag 时, 有多种写法, 这里没提及。
CLI11 支持子命令, 这里没提及。
没有提及的内容, 可以参照官方 README.md ,也可以从 CLI11.hpp 源码研读。 没大段贴这两个来源的内容的原因, 是细节太多, 使用 CLI11 的目的并不是陷入细节而是掌握基本的框架感, 用到的时候再查也来得及。
2025.01.01 21:20:00