|
| 1 | +# cmdline |
| 2 | + |
| 3 | +C++23 命令行解析库,风格参考 clap。模块化设计,单 import,无裸指针 API。 |
| 4 | + |
| 5 | +- **C++23** 模块 |
| 6 | +- **类型命名**:`ParsedArgs`(解析结果)、`OptionValue`(选项取值) |
| 7 | +- **解析 API**:`parse(argc, argv)`、`parse_from(span<string>)`、`parse_from(string_view)`,支持多种输入 |
| 8 | +- **子命令分发**:`on_run(lambda)` + `dispatch(parsed)`,也可分离构建后自行根据 `subcommand_name()` 分发 |
| 9 | +- **全局选项**、`get_flag()` / `get_one()`、`std::expected` 解析结果 |
| 10 | + |
| 11 | +## 快速开始 |
| 12 | + |
| 13 | +```cpp |
| 14 | +import std; |
| 15 | +import cmdline; |
| 16 | + |
| 17 | +int main(int argc, char* argv[]) { |
| 18 | + using namespace cmdline; |
| 19 | + |
| 20 | + auto app = App("myapp") |
| 21 | + .version("1.0.0") |
| 22 | + .about("My CLI") |
| 23 | + .arg(Arg("input").help("Input file").required()) |
| 24 | + .opt(Opt("verbose").short_name('v').long_opt("verbose").help("Verbose")) |
| 25 | + .opt(Opt("config").long_opt("config").takes_value().value_name("FILE")); |
| 26 | + |
| 27 | + auto result = app.parse(argc, argv); |
| 28 | + if (!result) { |
| 29 | + if (result.error() == "help requested" || result.error() == "version requested") |
| 30 | + return 0; |
| 31 | + std::println("Error: {}", result.error()); |
| 32 | + return 1; |
| 33 | + } |
| 34 | + const ParsedArgs& parsed = *result; |
| 35 | + if (parsed.get_flag("verbose")) { /* ... */ } |
| 36 | + if (auto c = parsed.get_one("config")) std::println("Config: {}", *c); |
| 37 | + std::println("Input: {}", parsed.arg(0)); |
| 38 | + return 0; |
| 39 | +} |
| 40 | +``` |
| 41 | +
|
| 42 | +## 多种输入 |
| 43 | +
|
| 44 | +```cpp |
| 45 | +// C 风格 |
| 46 | +auto result = app.parse(argc, argv); |
| 47 | +
|
| 48 | +// 字符串列表 |
| 49 | +std::vector<std::string> args = {"myapp", "add", "python", "3.12"}; |
| 50 | +auto result = app.parse_from(args); |
| 51 | +
|
| 52 | +// 单条命令行字符串(按空格/引号拆分) |
| 53 | +auto result = app.parse_from("myapp add python 3.12 --yes"); |
| 54 | +``` |
| 55 | + |
| 56 | +## 绑定分发(on_run + dispatch) |
| 57 | + |
| 58 | +子命令推荐用字符串链式:`.subcommand("name").about("...").arg(...).on_run(...)`;也可传入完整 `App` 对象。 |
| 59 | + |
| 60 | +```cpp |
| 61 | +App app("demo"); |
| 62 | +app.opt(Opt("yes").long_opt("yes").global().help("Auto confirm")) |
| 63 | + .subcommand("add") |
| 64 | + .about("Add a target") |
| 65 | + .arg(Arg("target").required()) |
| 66 | + .arg(Arg("version").required()) |
| 67 | + .on_run([](const ParsedArgs& args) { |
| 68 | + std::println("add: {}@{}", args.get_one("target").value_or(""), args.get_one("version").value_or("")); |
| 69 | + }) |
| 70 | + .subcommand("remove") |
| 71 | + .about("Remove") |
| 72 | + .arg(Arg("target").required()) |
| 73 | + .on_run([](const ParsedArgs& args) { std::println("remove: {}", args.arg(0)); }); |
| 74 | + |
| 75 | +auto result = app.parse(argc, argv); |
| 76 | +if (result) app.dispatch(*result); |
| 77 | +``` |
| 78 | +
|
| 79 | +## 分离模式(自行分发) |
| 80 | +
|
| 81 | +```cpp |
| 82 | +auto result = app.parse(argc, argv); |
| 83 | +if (!result) return 1; |
| 84 | +const ParsedArgs& parsed = *result; |
| 85 | +if (parsed.has_subcommand()) { |
| 86 | + auto sub = parsed.subcommand(); |
| 87 | + if (sub && parsed.subcommand_name() == "add") { |
| 88 | + const ParsedArgs& sub_args = sub->get(); |
| 89 | + // ... |
| 90 | + } |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +## 构建 |
| 95 | + |
| 96 | +```bash |
| 97 | +cd cmdline && xmake -P . |
| 98 | +xmake run basic |
| 99 | +xmake run with_dispatch -- add python 3.12 |
| 100 | +xmake run parse_from_string |
| 101 | +``` |
| 102 | + |
| 103 | +测试需 Google Test,使用 **`xmake -y`** 可自动安装依赖(如 gtest)并编译: |
| 104 | + |
| 105 | +```bash |
| 106 | +xmake -y # 自动确认并安装 gtest,编译全部(含测试) |
| 107 | +xmake -y run cmdline_test # 编译并运行测试 |
| 108 | +``` |
| 109 | + |
| 110 | +## CI |
| 111 | + |
| 112 | +GitHub Actions 在 `push` / `pull_request` 到 `main` 或 `master` 时自动构建并测试(见 [.github/workflows/ci.yml](.github/workflows/ci.yml)): |
| 113 | + |
| 114 | +- 安装 xmake(package 缓存)、build-essential |
| 115 | +- 通过 [Xlings](https://d2learn.org/xlings-install.sh) 安装 GCC 15.1(C++23 模块) |
| 116 | +- `xmake -y` 构建(含 gtest) |
| 117 | +- `xmake run cmdline_test` 跑测试 |
| 118 | +- 跑示例 `with_dispatch` 做冒烟检查 |
| 119 | + |
| 120 | +## API 概要 |
| 121 | + |
| 122 | +| 类型/方法 | 说明 | |
| 123 | +|-----------|------| |
| 124 | +| `App` | 根或子命令;`.version()`, `.author()`, `.about()`, `.arg()`, `.opt()`, `.subcommand(name\|App)`, `.on_run()`, `.parse()`, `.parse_from()`, `.dispatch()`, `.print_help()` | |
| 125 | +| `Arg` | 位置参数;`.help()`, `.required()`, `.default_value()` | |
| 126 | +| `Opt` | 选项;`.short_name()`, `.long_opt()`, `.help()`, `.takes_value()`, `.value_name()`, `.multiple()`, `.global()` | |
| 127 | +| `ParsedArgs` | 解析结果;`.get_flag()`, `.get_one()`, `.opt()`, `.opt_or_empty()`, `.arg()`, `.arg_count()`, `.has_subcommand()`, `.subcommand_name()`, `.subcommand()` | |
| 128 | +| `OptionValue` | 单个选项取值;`.value()`, `.value_or()`, `.is_set()` | |
| 129 | + |
| 130 | +解析返回 `std::expected<ParsedArgs, std::string>`。`-h`/`--help` 或 `--version` 时 error 为 `"help requested"` / `"version requested"`,可据此退出 0。 |
0 commit comments