CLI11:命令行参数解析库
CLI11 是一个支持 C++11 标准的命令行参数解析器。除此之外,比较流行的命令行参数解析库有 Boost Program Options 和 Cxxopts 等。CLI11 的 GitHub 库的 README 中对几种常见的库进行了简单的比较,再次不赘述。选择这个库的原因是其支持像 git 命令那样的子命令。
接下来用一个完整的 CMake 项目,以实现 git 命令的 init 子命令作为示例说明这个库的用法。项目代码可以在 GitHub 仓库中查看。
使用
首先一定要包含这三个头文件:
<CLI/App.hpp>
<CLI/Config.hpp>
<CLI/Formatter.hpp>
否则编译时在链接阶段会报错。
CLI11 库使用 CLI::App
对象表示一个解析器。我们用一个 application
类来封装代码:
namespace demoapp {
class application {
public:
application();
~application();
int run(int argc, char **argv);
private:
CLI::App app;
};
}; // namespace demoapp
application::run
方法中通过 CLI11_PARSE
宏对命令行参数进行解析:
int application::run(int argc, char **argv)
{
CLI11_PARSE(app, argc, argv);
return 0;
}
CLI11_PARSE
宏的定义为:
try {
app.parse(argc, argv);
} catch (const CLI::ParseError &e) {
return app.exit(e);
}
访问非选项参数
默认情况下 CLI::App
对象不允许非选项参数,如果指定了非选项参数会抛出异常。在需要非选项参数的时候可调用 CLI::App
的 allow_extras()
方法允许非选项参数。
可通过 CLI::App
的 remaining()
方法访问所有的非选项参数。remaining_size()
返回非选项参数的个数。
添加选项参数
可通过 CLI::App
的 add_flag()
和 add_option()
方法添加选项参数。flag 表示不需要值的参数,或者说值类型为 bool 的参数, option 表示需要值的参数。二者通常区别不大。
两个方法支持传入变量,用来将参数值保存在变量中。但是像 git 这样有很多子命令和选项参数的命令,这种做法不是很好。因此可以通过 CLI::App
的 get_option()
方法获取指定选项参数的 CLI::Option
对象,然后使用 CLI::Option
对象的 results()
方法或者 as()
方法获取参数值。我们就是用这种方法获取 init 子命令的选项参数:
const CLI::Option *option = app->get_option("--initial-branch");
option->results(branch);
option = app->get_option("--quiet");
option->results(quiet);
添加子命令
可通过 CLI::App
的 add_subcommand()
方法添加子命令。该方法返回一个 CLI:App
对象,因此可以用上面提到的方法为子命令添加选项参数。
可使用 require_subcommand()
方法要求命令行参数中必须至少使用几个子命令。
application::application()
{
app.description("Demo git command to show the usage of CLI11 library");
app.name("git");
auto init_subcmd = app.add_subcommand("init", "Create an empty Git repository or reinitialize an existing one");
init_subcmd->add_option("-b,--initial-branch",
"Use the specified name for the initial branch in the newly created repository");
init_subcmd->add_flag("-q,--quiet", "Only print warning and error messages.");
init_subcmd->allow_extras();
app.require_subcommand(1);
}
然后为 init 子命令定义一个类:
class init {
public:
init(const CLI::App *app) { this->app = app; }
~init() {}
int run();
private:
const CLI::App *app;
bool quiet;
std::string dir{"."};
std::string branch;
int check_args();
};
私有方法 check_args()
检查参数是否合规,并设置相关变量。具体的业务逻辑放在 run()
方法中。