给 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。
例如,下面几种输入都可以解析:
./demo photo.png -o result.txt -c 120 -v ./demo "my photo.png" --output=result.txt --count=120 ./demo --count=-12 ./demo -h
如何接入项目?
可以先克隆仓库:
git clone https://github.com/KAI-SHUNG/arg_parser.git cd arg_parser
然后把arg_parser.hpp复制到自己的源码目录或头文件搜索目录,在代码中包含它:
#include "arg_parser.hpp"
编译器需要支持 C++17。下面的完整示例可以直接保存为demo.cpp,与头文件放在同一目录。
一个完整可运行的例子
这个示例接收输入文件名、输出文件名、一个整数和一个布尔开关。它只打印解析结果,用来展示接口,不执行实际的文件处理。
#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的环境中,可以这样编译运行:
g++ -std=c++17 demo.cpp -o demo ./demo photo.png -o result.txt -c 120 -v
Windows 使用 MinGW 编译上述wmain版本时,需要加上-municode:
g++ -std=c++17 -municode demo.cpp -o demo.exe ./demo.exe photo.png -o result.txt -c 120 -v
输出如下:
input=photo.png output=result.txt count=120 verbose=1
没有传入参数时,会使用示例中配置的默认值:input.png、output.txt、80和false。这里没有启用std::boolalpha,因此布尔值打印为0或1。
仓库也提供了较短的 example.cpp,可以直接克隆后编译运行。
三类参数分别怎么用?
注册接口是:
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" |
带值选项支持这四种形式:
./demo --output=result.txt ./demo --output result.txt ./demo -o=result.txt ./demo -o result.txt
位置参数默认也不是必填。需要用户明确提供时,要单独设置set_required(true)。
默认值、必填参数与类型转换
默认值使用字符串配置,读取时再转换为目标类型。例如,完整示例已经为count配置了.set_default("80"),解析后可以这样读取:
int count = parser.get<int>("count");如果用户省略count,get<int>("count")会返回默认值80。如果传入--count=12abc,读取整数时会抛出异常,不会只取前面的12。字符串读取会保留空格;例如"my photo.png"会作为一个完整的文件名返回。
对于必须显式传入的选项,可以在调用parse()前注册:
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"。
两者配合,适合区分“用户主动设置”与“使用程序默认配置”:
if (parser.has("output")) { std::cout << "Output was explicitly provided\n"; } std::string output = parser.get<std::string>("output");这些读取操作都应放在parse()之后。
帮助信息不用单独维护
运行:
./demo -h
会得到上面完整示例生成的帮助信息:
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 参数,这能保留输入内容,例如:
./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 输入等测试。可以在仓库根目录编译运行:
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 许可证,完整源码、接口说明和示例都在仓库中:
GitHub 仓库
单头文件 arg_parser.hpp
README 与 API 说明
如果你正在给一个 C++ 小工具添加命令行入口,可以从这个单头文件开始试用。欢迎通过 Issues 反馈使用问题,也欢迎提交改进。