Featured image of post arg_parser,一个轻量的 C++17 单头文件命令行参数解析库

arg_parser,一个轻量的 C++17 单头文件命令行参数解析库

arg_parser 的接入方式、参数类型、默认值与必填检查、类型转换,以及 Windows Unicode 输入。

给 C++ 小工具添加命令行参数时,很容易从几个 argv 判断开始,逐渐写出一串处理短参数、默认值、类型转换和错误提示的代码。参数多起来以后,帮助信息也要跟着手动维护。

arg_parser 把这些常用操作整理成一个小型库:注册参数,调用 parse(),再通过 get<T>() 读取结果。项目只需要一个头文件,适合给文件处理工具、命令行程序和个人项目添加基础的参数解析能力。

项目地址:KAI-SHUNG/arg_parser。

有哪些功能?

  • 单头文件、无第三方依赖:使用 C++17,把 arg_parser.hpp 放进项目即可,不需要额外编译库文件。
  • 三类参数:支持无值开关、带值选项和位置参数,也支持短别名。
  • 参数配置与类型获取:支持默认值、必填参数、描述,以及 get<std::string>()、get<int>() 等读取方式。
  • 内置帮助输出:根据注册信息生成用法和参数说明,应用可以用它处理 -h、--help。
  • Windows Unicode 输入:支持从 wmain 接收 UTF-16 参数,并转换为 UTF-8。

例如,下面几种输入都可以解析:

1
2
3
4
./demo photo.png -o result.txt -c 120 -v
./demo "my photo.png" --output=result.txt --count=120
./demo --count=-12
./demo -h

如何接入项目?

可以先克隆仓库:

1
2
git clone https://github.com/KAI-SHUNG/arg_parser.git
cd arg_parser

然后把 arg_parser.hpp 复制到自己的源码目录或头文件搜索目录,在代码中包含它:

1
#include "arg_parser.hpp"

编译器需要支持 C++17。下面的完整示例可以直接保存为 demo.cpp,与头文件放在同一目录。

一个完整可运行的例子

这个示例接收输入文件名、输出文件名、一个整数和一个布尔开关。它只打印解析结果,用来展示接口,不执行实际的文件处理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include "arg_parser.hpp"
#include <iostream>

#ifdef _WIN32
int wmain(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
    using arg_parser::ArgType;
    arg_parser::ArgParser parser;
    parser.set_program_name("demo");

    parser.add_argument("input")
        .set_default("input.png").set_description("Input file");
    parser.add_argument("output", "o", ArgType::Option)
        .set_default("output.txt").set_description("Output file");
    parser.add_argument("count", "c", ArgType::Option)
        .set_default("80").set_description("Count value");
    parser.add_argument("verbose", "v", ArgType::Flag)
        .set_default("false").set_description("Enable verbose mode");
    parser.add_argument("help", "h", ArgType::Flag)
        .set_description("Show help");

    try {
        parser.parse(argc, argv);
        if (parser.has("help")) {
            parser.help();
            return 0;
        }

        std::cout << "input=" << parser.get<std::string>("input") << '\n'
                  << "output=" << parser.get<std::string>("output") << '\n'
                  << "count=" << parser.get<int>("count") << '\n'
                  << "verbose=" << parser.get<bool>("verbose") << '\n';
    } catch (const std::exception& error) {
        std::cerr << error.what() << '\n';
        parser.help();
        return 1;
    }
    return 0;
}

在 Linux、macOS 等使用 main 的环境中,可以这样编译运行:

1
2
g++ -std=c++17 demo.cpp -o demo
./demo photo.png -o result.txt -c 120 -v

Windows 使用 MinGW 编译上述 wmain 版本时,需要加上 -municode:

1
2
g++ -std=c++17 -municode demo.cpp -o demo.exe
./demo.exe photo.png -o result.txt -c 120 -v

输出如下:

1
2
3
4
input=photo.png
output=result.txt
count=120
verbose=1

没有传入参数时,会使用示例中配置的默认值:input.png、output.txt、80 和 false。这里没有启用 std::boolalpha,因此布尔值打印为 0 或 1。

仓库也提供了较短的 example.cpp,可以直接克隆后编译运行。

三类参数分别怎么用?

注册接口是:

1
add_argument(name, alias = std::nullopt, type = ArgType::Positional)

name 是正式名称,alias 是可选别名,两者都不带前导 -。名称和别名不能与已经注册的参数冲突。默认类型是位置参数,返回的 Argument& 可以继续链式调用配置函数。

类型 注册示例 命令行输入 用途
Positional add_argument("input") photo.png 按注册顺序接收位置参数
Option add_argument("output", "o", ArgType::Option) --output result.txt 或 -o=result.txt 接收一个值
Flag add_argument("verbose", "v", ArgType::Flag) --verbose 或 -v 表示开关,提供时保存为 "true"

带值选项支持这四种形式:

1
2
3
4
./demo --output=result.txt
./demo --output result.txt
./demo -o=result.txt
./demo -o result.txt

位置参数默认也不是必填。需要用户明确提供时,要单独设置 set_required(true)。

默认值、必填参数与类型转换

默认值使用字符串配置,读取时再转换为目标类型。例如,完整示例已经为 count 配置了 .set_default("80"),解析后可以这样读取:

1
int count = parser.get<int>("count");

如果用户省略 count,get<int>("count") 会返回默认值 80。如果传入 --count=12abc,读取整数时会抛出异常,不会只取前面的 12。字符串读取会保留空格;例如 "my photo.png" 会作为一个完整的文件名返回。

对于必须显式传入的选项,可以在调用 parse() 前注册:

1
2
3
parser.add_argument("config", std::nullopt, ArgType::Option)
    .set_required(true)
    .set_description("Configuration file");

此时需要提供 --config settings.json。即使这个参数设置了默认值,也不能代替用户的显式输入。

get<bool>() 当前按字符串是否等于 "true" 返回结果。因此,开关参数通常搭配 .set_default("false") 使用;不要把它当作支持任意布尔字面量的转换器。

has() 和 get<T>() 有什么区别?

has() 判断的是“用户是否明确提供了这个参数”,默认值不算明确提供。它支持正式名称和别名,例如 has("output") 与 has("o")。

get<T>() 则读取参数值:用户提供了就读取输入,没提供就尝试默认值。读取时应使用正式名称,例如 get<std::string>("output"),不要使用别名 "o"。

两者配合,适合区分“用户主动设置”与“使用程序默认配置”:

1
2
3
4
if (parser.has("output")) {
    std::cout << "Output was explicitly provided\n";
}
std::string output = parser.get<std::string>("output");

这些读取操作都应放在 parse() 之后。

帮助信息不用单独维护

运行:

1
./demo -h

会得到上面完整示例生成的帮助信息:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Usage: demo [options] <input>

Options:
  --input
      Input file
  --output, -o <value>
      Output file
  --count, -c <value>
      Count value
  --verbose, -v
      Enable verbose mode
  --help, -h
      Show help

参数名称、别名和描述都来自注册信息。set_program_name() 可以设置用法中的程序名,set_note() 可以在帮助末尾追加说明。

注意:注册一个叫 help 的参数不会自动显示帮助。应用需要在解析后检查它,再调用 help(),完整示例已经展示了这一流程。

Windows 中文参数怎么处理?

头文件在 Windows 上提供 parse(argc, wchar_t** argv) 重载,可以与 wmain 配合,把 UTF-16 参数转换为 UTF-8。对于中文文件名等非 ASCII 参数,这能保留输入内容,例如:

1
./demo.exe "图片.png" --output="结果.txt"

get<std::string>() 得到的是 UTF-8 字符串。后续如果把它交给文件操作接口,还需要确认该接口接受的编码;终端显示效果也取决于终端配置。

一些需要了解的边界

这个项目的接口集中在基础参数解析。使用时需要注意以下约定:

  • 负数和以 - 开头的选项值使用 =:例如 --count=-12、--output=-result.txt。--count -12 会被视为缺少值。
  • 以 - 开头的位置参数使用 -- 分隔:例如 ./demo -- -photo.png,分隔符后的内容按位置参数处理。
  • 不支持短开关合并:用 -v -h,不要写成 -vh。
  • 位置参数仍按顺序传入:帮助输出目前会把 input 列在 Options 中,但实际应写 ./demo photo.png,不能用 --input=photo.png。
  • 同一个开关或选项不能重复提供:例如 --output=a.txt -o=b.txt 会被拒绝。
  • 每个解析器实例只调用一次 parse():需要解析另一组输入时,新建实例。
  • 必填检查先于应用的帮助处理:如果添加了必填参数,仅传 -h 仍可能在 parse() 中触发缺少必填参数的异常。需要自行安排错误与帮助的展示流程;完整示例会在捕获异常后显示帮助。

未知参数、缺少选项值、重复输入和必填参数缺失会在解析时报告;数值转换错误会在调用 get<T>() 时报告。实际应用中,建议像完整示例一样捕获异常并给出错误提示。

如何运行项目测试?

仓库包含参数解析、别名、默认值、必填检查、类型转换、帮助输出和 Unicode 输入等测试。可以在仓库根目录编译运行:

1
2
g++ -std=c++17 -I. tests/test_arg_parser.cpp -o test_arg_parser
./test_arg_parser

Windows 下把输出文件名改成 test_arg_parser.exe,并运行 ./test_arg_parser.exe。测试程序使用普通 main,不需要 -municode。

项目地址与许可证

项目采用 MIT 许可证,完整源码、接口说明和示例都在仓库中:

如果你正在给一个 C++ 小工具添加命令行入口,可以从这个单头文件开始试用。欢迎通过 Issues 反馈使用问题,也欢迎提交改进。

页面浏览量Loading
网站总访客数:Loading
网站总访问量:Loading
使用 Hugo 构建
主题 Stack 由 Jimmy 设计