@@ -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 (" \n OPTIONS:" );
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(...))
298312class OptBuilder {
299313public:
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
339353private:
340354 void commit () {
@@ -351,7 +365,7 @@ private:
351365// 链式参数构建器:.arg("target").required() 或 .arg(Arg(...))
352366class ArgBuilder {
353367public:
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
389403private:
390404 void commit () {
@@ -401,7 +415,7 @@ private:
401415// 链式子命令构建器:.subcommand("add").description("...").action(...) 或 .subcommand("remove")...
402416class SubcommandBuilder {
403417public:
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
446460private:
0 commit comments