Skip to content

Commit 6af23e9

Browse files
committed
fix global issue & optimize single alpha issue for varname
1 parent 0324384 commit 6af23e9

File tree

4 files changed

+116
-84
lines changed

4 files changed

+116
-84
lines changed

examples/with_dispatch.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ int main(int argc, char* argv[]) {
88
App app("demo");
99
app.version("0.1.0")
1010
.description("Demo: parse + run with action")
11-
.option("yes").global().help("Auto confirm")
11+
.option("yes").short_name('y').global(true).help("Auto confirm")
1212
.subcommand("add")
1313
.description("Add a target")
1414
.arg("target").required()
1515
.arg("version").required()
16-
.action([](const ParsedArgs& a) {
17-
std::println("add: {}@{}", a.value("target").value_or(""), a.value("version").value_or(""));
16+
.action([](const ParsedArgs& args) {
17+
std::println("add: {}@{}", args.value("target").value_or(""), args.value("version").value_or(""));
1818
})
1919
.subcommand("remove")
2020
.description("Remove a target")
2121
.arg("target").required()
22-
.action([](const ParsedArgs& a) { std::println("remove: {}", a.positional(0)); });
22+
.action([](const ParsedArgs& args) { std::println("remove: {}", args.positional(0)); });
2323

2424
// 2) 解析
2525
auto result = app.parse(argc, argv);

src/cmdline.cppm

Lines changed: 82 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ public:
5656
return *this;
5757
}
5858
[[nodiscard]] ArgBuilder arg(std::string_view name);
59-
[[nodiscard]] App& option(Option o) {
60-
options_.push_back(std::move(o));
59+
[[nodiscard]] App& option(Option opt) {
60+
options_.push_back(std::move(opt));
6161
return *this;
6262
}
6363
[[nodiscard]] OptBuilder option(std::string_view name);
@@ -75,9 +75,10 @@ public:
7575
[[nodiscard]] std::expected<ParsedArgs, std::string> parse(int argc, char* argv[]) const {
7676
global_keys_t global_keys;
7777
global_opts_t global_opts;
78-
for (const auto& o : options_)
79-
if (o.global_) global_keys.insert(option_key(o));
80-
return parse_impl(argc, argv, 1, &global_keys, &global_opts);
78+
std::vector<Option> root_globals;
79+
for (const auto& opt : options_)
80+
if (opt.global_) { global_keys.insert(option_key(opt)); root_globals.push_back(opt); }
81+
return parse_impl(argc, argv, 1, &global_keys, &global_opts, &root_globals);
8182
}
8283
[[nodiscard]] std::expected<ParsedArgs, std::string> parse_from(std::span<const std::string> args) const {
8384
if (args.empty()) return std::unexpected("no program name");
@@ -87,9 +88,10 @@ public:
8788
for (auto& str : argv_storage) argv_ptrs.push_back(str.data());
8889
global_keys_t global_keys;
8990
global_opts_t global_opts;
90-
for (const auto& o : options_)
91-
if (o.global_) global_keys.insert(option_key(o));
92-
return parse_impl(static_cast<int>(argv_ptrs.size()), argv_ptrs.data(), 1, &global_keys, &global_opts);
91+
std::vector<Option> root_globals;
92+
for (const auto& opt : options_)
93+
if (opt.global_) { global_keys.insert(option_key(opt)); root_globals.push_back(opt); }
94+
return parse_impl(static_cast<int>(argv_ptrs.size()), argv_ptrs.data(), 1, &global_keys, &global_opts, &root_globals);
9395
}
9496
[[nodiscard]] std::expected<ParsedArgs, std::string> parse_from(std::string_view command_line) const {
9597
std::vector<std::string> tokens = split_command_line(command_line);
@@ -121,12 +123,12 @@ public:
121123
std::println(" {} <{}>", prog, argument.name);
122124
if (!options_.empty()) {
123125
std::println("\nOPTIONS:");
124-
for (const auto& o : options_) {
126+
for (const auto& opt : options_) {
125127
std::string opt_str;
126-
if (o.short_) opt_str += std::format("-{}, ", o.short_);
127-
if (!o.long_name.empty()) opt_str += "--" + o.long_name;
128-
if (!o.value_name_.empty()) opt_str += " <" + o.value_name_ + ">";
129-
std::println(" {:20} {}", opt_str, o.help_);
128+
if (opt.short_) opt_str += std::format("-{}, ", opt.short_);
129+
if (!opt.long_name.empty()) opt_str += "--" + opt.long_name;
130+
if (!opt.value_name_.empty()) opt_str += " <" + opt.value_name_ + ">";
131+
std::println(" {:20} {}", opt_str, opt.help_);
130132
}
131133
}
132134
if (!subcommands_.empty()) {
@@ -167,23 +169,33 @@ private:
167169

168170
[[nodiscard]] std::expected<ParsedArgs, std::string> parse_impl(
169171
int argc, char* argv[], int start_index,
170-
const global_keys_t* global_keys, global_opts_t* global_opts) const {
172+
const global_keys_t* global_keys, global_opts_t* global_opts,
173+
const std::vector<Option>* root_global_options) const {
171174

172175
ParsedArgs result;
173176
std::vector<std::string> positionals;
174177
bool only_positionals = false;
175178

176-
auto apply_option = [&](const Option* o, const std::string& key_str, std::string val, bool is_flag) {
179+
auto apply_option = [&](const Option* opt, const std::string& key_str, std::string val, bool is_flag) {
177180
if (global_keys && global_opts && global_keys->contains(key_str)) {
178181
if (is_flag) (*global_opts)[key_str].count++;
179182
else (*global_opts)[key_str] = OptionValue{1, {std::move(val)}};
180183
} else {
181184
if (is_flag) result.opts[key_str].count++;
182-
else if (o->multiple_) result.opts[key_str].values.push_back(std::move(val));
185+
else if (opt->multiple_) result.opts[key_str].values.push_back(std::move(val));
183186
else result.opts[key_str] = OptionValue{1, {std::move(val)}};
184187
}
185188
};
186189

190+
auto find_global_option = [root_global_options](std::string_view long_name, char short_name) -> const Option* {
191+
if (!root_global_options) return nullptr;
192+
for (const auto& opt : *root_global_options) {
193+
if (short_name && opt.matches_short(short_name)) return &opt;
194+
if (!long_name.empty() && opt.matches_long(long_name)) return &opt;
195+
}
196+
return nullptr;
197+
};
198+
187199
for (int index = start_index; index < argc; ++index) {
188200
std::string_view token = argv[index];
189201
if (only_positionals) {
@@ -206,7 +218,7 @@ private:
206218
for (const auto& sub : subcommands_) {
207219
if (sub.name_ == token) {
208220
result.subcommand_name_ = sub.name_;
209-
auto sub_result = sub.parse_impl(argc, argv, index + 1, global_keys, global_opts);
221+
auto sub_result = sub.parse_impl(argc, argv, index + 1, global_keys, global_opts, root_global_options);
210222
if (!sub_result) return sub_result;
211223
result.subcommand_matches = std::make_unique<ParsedArgs>(std::move(*sub_result));
212224
result.positionals = std::move(positionals);
@@ -221,34 +233,36 @@ private:
221233
auto eq_pos = rest.find('=');
222234
std::string_view key = eq_pos != std::string_view::npos ? rest.substr(0, eq_pos) : rest;
223235
std::string_view value_part = eq_pos != std::string_view::npos ? rest.substr(eq_pos + 1) : std::string_view{};
224-
const Option* o = find_option(key, '\0');
225-
if (!o) return std::unexpected(std::format("unknown option: --{}", key));
226-
std::string key_str = option_key(*o);
227-
if (o->takes_value_) {
236+
const Option* opt = find_option(key, '\0');
237+
if (!opt) opt = find_global_option(key, '\0');
238+
if (!opt) return std::unexpected(std::format("unknown option: --{}", key));
239+
std::string key_str = option_key(*opt);
240+
if (opt->takes_value_) {
228241
std::string val;
229242
if (eq_pos != std::string_view::npos)
230243
val = std::string(value_part);
231244
else if (index + 1 < argc)
232245
val = argv[++index];
233246
else
234247
return std::unexpected(std::format("option --{} requires a value", key));
235-
apply_option(o, key_str, std::move(val), false);
248+
apply_option(opt, key_str, std::move(val), false);
236249
} else {
237-
apply_option(o, key_str, "", true);
250+
apply_option(opt, key_str, "", true);
238251
}
239252
continue;
240253
}
241254
if (token.size() == 2 && token[0] == '-' && token[1] != '-') {
242255
char short_char = token[1];
243-
const Option* o = find_option("", short_char);
244-
if (!o) return std::unexpected(std::format("unknown option: -{}", short_char));
245-
std::string key_str = option_key(*o);
246-
if (o->takes_value_) {
256+
const Option* opt = find_option("", short_char);
257+
if (!opt) opt = find_global_option("", short_char);
258+
if (!opt) return std::unexpected(std::format("unknown option: -{}", short_char));
259+
std::string key_str = option_key(*opt);
260+
if (opt->takes_value_) {
247261
if (index + 1 >= argc) return std::unexpected(std::format("option -{} requires a value", short_char));
248262
std::string val = argv[++index];
249-
apply_option(o, key_str, std::move(val), false);
263+
apply_option(opt, key_str, std::move(val), false);
250264
} else {
251-
apply_option(o, key_str, "", true);
265+
apply_option(opt, key_str, "", true);
252266
}
253267
continue;
254268
}
@@ -274,14 +288,14 @@ private:
274288
}
275289

276290
const Option* find_option(std::string_view long_name, char short_name) const {
277-
for (const auto& o : options_) {
278-
if (short_name && o.matches_short(short_name)) return &o;
279-
if (!long_name.empty() && o.matches_long(long_name)) return &o;
291+
for (const auto& opt : options_) {
292+
if (short_name && opt.matches_short(short_name)) return &opt;
293+
if (!long_name.empty() && opt.matches_long(long_name)) return &opt;
280294
}
281295
return nullptr;
282296
}
283-
static std::string option_key(const Option& o) {
284-
return o.long_name.empty() ? std::string(1, o.short_) : o.long_name;
297+
static std::string option_key(const Option& opt) {
298+
return opt.long_name.empty() ? std::string(1, opt.short_) : opt.long_name;
285299
}
286300

287301
std::string name_;
@@ -297,7 +311,7 @@ private:
297311
// 链式选项构建器:.option("yes").global().help("...") 或 .option(Option(...))
298312
class OptBuilder {
299313
public:
300-
OptBuilder(App* parent, Option o) : parent_(parent), option_(std::move(o)) {}
314+
OptBuilder(App* parent, Option opt) : parent_(parent), option_(std::move(opt)) {}
301315
OptBuilder(OptBuilder&& other) noexcept : parent_(other.parent_), option_(std::move(other.option_)), committed_(other.committed_) {
302316
other.parent_ = nullptr;
303317
}
@@ -315,26 +329,26 @@ public:
315329
OptBuilder& operator=(const OptBuilder&) = delete;
316330
~OptBuilder() { commit(); }
317331

318-
OptBuilder& long_opt(std::string_view v) { (void)option_.long_opt(v); return *this; }
319-
OptBuilder& short_name(char c) { (void)option_.short_name(c); return *this; }
320-
OptBuilder& help(std::string_view v) { (void)option_.help(v); return *this; }
321-
OptBuilder& takes_value(bool b = true) { (void)option_.takes_value(b); return *this; }
322-
OptBuilder& value_name(std::string_view v) { (void)option_.value_name(v); return *this; }
323-
OptBuilder& multiple(bool b = true) { (void)option_.multiple(b); return *this; }
324-
OptBuilder& global(bool b = true) { (void)option_.global(b); return *this; }
332+
OptBuilder& long_opt(std::string_view long_opt_name) { (void)option_.long_opt(long_opt_name); return *this; }
333+
OptBuilder& short_name(char short_char) { (void)option_.short_name(short_char); return *this; }
334+
OptBuilder& help(std::string_view help_text) { (void)option_.help(help_text); return *this; }
335+
OptBuilder& takes_value(bool takes = true) { (void)option_.takes_value(takes); return *this; }
336+
OptBuilder& value_name(std::string_view value_name_str) { (void)option_.value_name(value_name_str); return *this; }
337+
OptBuilder& multiple(bool multiple_flag = true) { (void)option_.multiple(multiple_flag); return *this; }
338+
OptBuilder& global(bool global_flag = true) { (void)option_.global(global_flag); return *this; }
325339

326-
App& option(Option o) { commit(); parent_->options_.push_back(std::move(o)); return *parent_; }
340+
App& option(Option opt) { commit(); parent_->options_.push_back(std::move(opt)); return *parent_; }
327341
OptBuilder option(std::string_view name) { commit(); return parent_->option(name); }
328-
App& arg(Arg a) { commit(); parent_->args_.push_back(std::move(a)); return *parent_; }
342+
App& arg(Arg argument) { commit(); parent_->args_.push_back(std::move(argument)); return *parent_; }
329343
ArgBuilder arg(std::string_view name);
330-
App& subcommand(App sub) { commit(); parent_->subcommands_.push_back(std::move(sub)); return *parent_; }
344+
App& subcommand(App sub_app) { commit(); parent_->subcommands_.push_back(std::move(sub_app)); return *parent_; }
331345
SubcommandBuilder subcommand(std::string_view name);
332-
App& version(std::string_view v) { commit(); (void)parent_->version(v); return *parent_; }
333-
App& description(std::string_view v) { commit(); (void)parent_->description(v); return *parent_; }
334-
App& author(std::string_view v) { commit(); (void)parent_->author(v); return *parent_; }
346+
App& version(std::string_view version_str) { commit(); (void)parent_->version(version_str); return *parent_; }
347+
App& description(std::string_view desc) { commit(); (void)parent_->description(desc); return *parent_; }
348+
App& author(std::string_view author_str) { commit(); (void)parent_->author(author_str); return *parent_; }
335349
template <typename Fn>
336350
App& action(Fn&& fn) { commit(); (void)parent_->action(std::forward<Fn>(fn)); return *parent_; }
337-
App& end() { App* p = parent_; commit(); parent_ = nullptr; return *p; }
351+
App& end() { App* app_ptr = parent_; commit(); parent_ = nullptr; return *app_ptr; }
338352

339353
private:
340354
void commit() {
@@ -351,7 +365,7 @@ private:
351365
// 链式参数构建器:.arg("target").required() 或 .arg(Arg(...))
352366
class ArgBuilder {
353367
public:
354-
ArgBuilder(App* parent, Arg a) : parent_(parent), arg_(std::move(a)) {}
368+
ArgBuilder(App* parent, Arg argument) : parent_(parent), arg_(std::move(argument)) {}
355369
ArgBuilder(ArgBuilder&& other) noexcept : parent_(other.parent_), arg_(std::move(other.arg_)), committed_(other.committed_) {
356370
other.parent_ = nullptr;
357371
}
@@ -369,22 +383,22 @@ public:
369383
ArgBuilder& operator=(const ArgBuilder&) = delete;
370384
~ArgBuilder() { commit(); }
371385

372-
ArgBuilder& help(std::string_view v) { (void)arg_.help(v); return *this; }
373-
ArgBuilder& required(bool b = true) { (void)arg_.required(b); return *this; }
374-
ArgBuilder& default_value(std::string_view v) { (void)arg_.default_value(v); return *this; }
386+
ArgBuilder& help(std::string_view help_text) { (void)arg_.help(help_text); return *this; }
387+
ArgBuilder& required(bool required_flag = true) { (void)arg_.required(required_flag); return *this; }
388+
ArgBuilder& default_value(std::string_view default_val) { (void)arg_.default_value(default_val); return *this; }
375389

376-
App& option(Option o) { commit(); parent_->options_.push_back(std::move(o)); return *parent_; }
390+
App& option(Option opt) { commit(); parent_->options_.push_back(std::move(opt)); return *parent_; }
377391
OptBuilder option(std::string_view name) { commit(); return parent_->option(name); }
378-
App& arg(Arg a) { commit(); parent_->args_.push_back(std::move(a)); return *parent_; }
392+
App& arg(Arg argument) { commit(); parent_->args_.push_back(std::move(argument)); return *parent_; }
379393
ArgBuilder arg(std::string_view name) { commit(); return parent_->arg(name); }
380-
App& subcommand(App sub) { commit(); parent_->subcommands_.push_back(std::move(sub)); return *parent_; }
394+
App& subcommand(App sub_app) { commit(); parent_->subcommands_.push_back(std::move(sub_app)); return *parent_; }
381395
SubcommandBuilder subcommand(std::string_view name);
382-
App& version(std::string_view v) { commit(); (void)parent_->version(v); return *parent_; }
383-
App& description(std::string_view v) { commit(); (void)parent_->description(v); return *parent_; }
384-
App& author(std::string_view v) { commit(); (void)parent_->author(v); return *parent_; }
396+
App& version(std::string_view version_str) { commit(); (void)parent_->version(version_str); return *parent_; }
397+
App& description(std::string_view desc) { commit(); (void)parent_->description(desc); return *parent_; }
398+
App& author(std::string_view author_str) { commit(); (void)parent_->author(author_str); return *parent_; }
385399
template <typename Fn>
386400
App& action(Fn&& fn) { commit(); (void)parent_->action(std::forward<Fn>(fn)); return *parent_; }
387-
App& end() { App* p = parent_; commit(); parent_ = nullptr; return *p; }
401+
App& end() { App* app_ptr = parent_; commit(); parent_ = nullptr; return *app_ptr; }
388402

389403
private:
390404
void commit() {
@@ -401,7 +415,7 @@ private:
401415
// 链式子命令构建器:.subcommand("add").description("...").action(...) 或 .subcommand("remove")...
402416
class SubcommandBuilder {
403417
public:
404-
SubcommandBuilder(App* parent, App sub) : parent_(parent), sub_(std::move(sub)) {}
418+
SubcommandBuilder(App* parent, App sub_app) : parent_(parent), sub_(std::move(sub_app)) {}
405419
SubcommandBuilder(SubcommandBuilder&& other) noexcept
406420
: parent_(other.parent_), sub_(std::move(other.sub_)), committed_(other.committed_) {
407421
other.parent_ = nullptr;
@@ -420,12 +434,12 @@ public:
420434
SubcommandBuilder& operator=(const SubcommandBuilder&) = delete;
421435
~SubcommandBuilder() { commit(); }
422436

423-
SubcommandBuilder& version(std::string_view v) { (void)sub_.version(v); return *this; }
424-
SubcommandBuilder& author(std::string_view v) { (void)sub_.author(v); return *this; }
425-
SubcommandBuilder& description(std::string_view v) { (void)sub_.description(v); return *this; }
426-
SubcommandBuilder& arg(Arg a) { (void)sub_.arg(std::move(a)); return *this; }
437+
SubcommandBuilder& version(std::string_view version_str) { (void)sub_.version(version_str); return *this; }
438+
SubcommandBuilder& author(std::string_view author_str) { (void)sub_.author(author_str); return *this; }
439+
SubcommandBuilder& description(std::string_view desc) { (void)sub_.description(desc); return *this; }
440+
SubcommandBuilder& arg(Arg argument) { (void)sub_.arg(std::move(argument)); return *this; }
427441
ArgBuilder arg(std::string_view name);
428-
SubcommandBuilder& option(Option o) { (void)sub_.option(std::move(o)); return *this; }
442+
SubcommandBuilder& option(Option opt) { (void)sub_.option(std::move(opt)); return *this; }
429443
OptBuilder option(std::string_view name);
430444
template <typename Fn>
431445
SubcommandBuilder& action(Fn&& fn) { (void)sub_.action(std::forward<Fn>(fn)); return *this; }
@@ -437,10 +451,10 @@ public:
437451
return *this;
438452
}
439453
App& end() {
440-
App* p = parent_;
454+
App* app_ptr = parent_;
441455
commit();
442456
parent_ = nullptr;
443-
return *p;
457+
return *app_ptr;
444458
}
445459

446460
private:

src/parse.cppm

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,15 @@ export struct ParsedArgs {
3434
return std::cref(it->second);
3535
}
3636
[[nodiscard]] OptionValue option_or_empty(std::string_view name) const {
37-
auto o = option(name);
38-
return o ? o->get() : OptionValue{};
37+
auto opt = option(name);
38+
return opt ? opt->get() : OptionValue{};
3939
}
4040
[[nodiscard]] bool is_flag_set(std::string_view name) const {
4141
return option_or_empty(name).is_set();
4242
}
4343
[[nodiscard]] std::optional<std::string> value(std::string_view name) const {
44-
auto o = option(name);
45-
if (o && o->get().is_set()) return o->get().value();
44+
auto opt = option(name);
45+
if (opt && opt->get().is_set()) return opt->get().value();
4646
for (std::size_t idx = 0; idx < positional_names_.size() && idx < positionals.size(); ++idx)
4747
if (positional_names_[idx] == name) return positionals[idx];
4848
return std::nullopt;

0 commit comments

Comments
 (0)