Logo ROOT  
Reference Guide
Loading...
Searching...
No Matches
optparse.hxx
Go to the documentation of this file.
1// \file optparse.hxx
2///
3/// Small utility to parse cmdline options.
4///
5/// Usage:
6/// ~~~{.cpp}
7/// MyAppOpts ParseArgs(const char **args, int nArgs) {
8/// ROOT::RCmdLineOpts opts;
9/// // will parse '-c VAL', '--compress VAL' or '--compress=VAL'
10/// opts.AddFlag({"-c", "--compress"}, RCmdLineOpts::EFlagType::kWithArg);
11/// // will toggle a switch '--recreate' (no args).
12/// opts.AddFlag({"--recreate"});
13/// opts.AddFlag({"-o"}, RCmdLineOpts::EFlagType::kWithArg);
14///
15/// // NOTE: `args` should not contain the program name! It should usually be `argc + 1`.
16/// // For example `main(char **argv, int argc)` might call this function as:
17/// // `ParseArgs(const_cast<const char**>(argv) + 1, argc - 1);`
18/// opts.Parse(args, nArgs);
19///
20/// // Check for errors:
21/// for (const auto &err : opts.GetErrors()) { /* print errors ... */ }
22/// if (!opts.GetErrors().empty()) return {};
23///
24/// // Convert the parsed options from string if necessary:
25/// MyAppOpts myOpts;
26/// // switch (boolean flag):
27/// myOpts.fRecreate = opts.GetSwitch("recreate");
28/// // string flag:
29/// myOpts.fOutput = opts.GetFlagValue("o");
30/// // integer flag:
31/// myOpts.fCompression = opts.GetFlagValueAs<int>("compress"); // (could also have used "c" instead of "compress")
32/// // positional arguments:
33/// myOpts.fArgs = opts.GetArgs();
34///
35/// return myOpts;
36/// }
37/// ~~~
38///
39/// ## Additional Notes
40///
41/// ### Flag grouping
42/// By default, if all the short flags you pass (those starting with a single `-`) are 1 character long, the parser will
43/// accept grouped flags like "-abc" as equivalent to "-a -b -c". The last flag in the group may also accept an
44/// argument, in which case "-abc foo" will count as "-a -b -c foo" where "foo" is the argument to "-c". If you want to
45/// disable flag grouping, use:
46///
47/// ~~~{.cpp}
48/// ROOT::RCmdLineOpts opts({ EFlagTreatment::kSimple });
49/// ~~~
50///
51/// ### Repeated flags
52/// Multiple repeated flags, like `-vvv`, are supported but must explicitly be marked as such on a per-flag basis:
53/// ~~~{.cpp}
54/// opts.AddFlag({"-v"}, RCmdLineOpts::EFlagType::kSwitch, "", RCmdLineOpts::kFlagAllowMultiple);
55/// ~~~
56/// This works both for switches and flags with arguments. `GetSwitch` returns the number of times a specific flag
57/// appeared; for flags with arguments `GetFlagValues` and `GetFlagValuesAs<T>` can be used to access the values as
58/// vectors.
59///
60/// ### Positional argument separator
61/// The string "--" is treated as the positional argument separator: all strings after it will be treated as positional
62/// arguments even if they start with "-".
63///
64/// ## Prefix flags (aka no space between flag and argument)
65/// If you need your flags to support the syntax "-fXYZ" where "-f" is your flag and "XYZ" its argument, you can enable
66/// that per-flag by using `RCmdLineOpts::kFlagPrefixArg`:
67///
68/// ~~~{.cpp}
69/// opts.AddFlag({"-I", "--include"}, RCmdLineOpts::EFlagType::kWithArg, RCmdLineOpts::kFlagPrefixArg });
70/// ~~~
71///
72/// (see EFlagTreatment for more details). This will **disable** flag grouping globally, but allows the parser to
73/// interpret flags and arguments that are not separated by spaces. If prefix arg is used, the equal sign will *not*
74/// be considered as the key-value separator (`-Dfoo=bar` if `-D` is a prefix arg flag will be parsed as `"-D",
75/// "foo=bar"`). Note that this option only makes sense for flags with arguments.
76///
77/// \author Giacomo Parolini <giacomo.parolini@cern.ch>
78/// \date 2025-10-09
79
80#ifndef ROOT_OptParse
81#define ROOT_OptParse
82
83#include <algorithm>
84#include <cassert>
85#include <charconv>
86#include <cstring>
87#include <cstdint>
88#include <iostream>
89#include <limits>
90#include <optional>
91#include <sstream>
92#include <stdexcept>
93#include <string>
94#include <string_view>
95#include <vector>
96
97namespace ROOT {
98
100public:
101 enum class EFlagTreatment {
102 /// Will result to kGrouped if you don't define any short flag longer than 1 character, otherwise kSimple.
104 /// `-abc` will always be treated as the single flag "-abc"
106 /// `-abc` will be treated as "-a -b -c". This is only valid for short flags.
107 /// With this setting you cannot define short flags that are more than 1 character long nor ones that are marked
108 /// with kFlagPrefixArg.
110 };
111
112 struct RSettings {
113 /// Affects how flags are parsed (\see EFlagTreatment).
115 };
116
117 enum class EFlagType {
120 };
121
122 struct RFlag {
123 std::string fName;
124 std::string fValue;
125 std::string fHelp;
126 };
127
128 // Technically these are bit flags, but EFlagFlag is confusing, so let's call them opts.
129 enum EFlagOpt {
130 /// Flag is allowed to appear multiple times (default: it's an error to see the same flag twice)
132 /// Flag argument can appear right after this flag without a space or equal sign in between.
133 /// Note that marking any short flag with this disables flag grouping!
135 };
136
137private:
139 std::vector<RFlag> fFlags;
140 std::vector<std::string> fArgs;
141
144 std::string fName;
145 std::string fHelp;
146 // If >= 0, this flag is an alias of the RExpectedFlag at index fAlias.
147 int fAlias = -1;
148 bool fShort = false;
149 std::uint32_t fOpts = 0;
150
151 std::string AsStr() const { return std::string(fShort ? "-" : "--") + fName; }
152 };
153 std::vector<RExpectedFlag> fExpectedFlags;
154 std::vector<std::string> fErrors;
155
156 const RExpectedFlag *GetExpectedFlag(std::string_view name) const
157 {
158 const auto StartsWith = [](std::string_view string, std::string_view prefix) {
159 return string.size() >= prefix.size() && string.substr(0, prefix.size()) == prefix;
160 };
161
162 for (const auto &flag : fExpectedFlags) {
163 if (flag.fOpts & kFlagPrefixArg) {
164 if (StartsWith(name, flag.fName)) {
165 // NOTE: we can't have ambiguities here because we make sure that no flags share a common prefix in
166 // AddFlag().
167 return &flag;
168 }
169 } else if (flag.fName == name) {
170 return &flag;
171 }
172 }
173 return nullptr;
174 }
175
176public:
177 explicit RCmdLineOpts(RSettings settings = {EFlagTreatment::kDefault}) : fSettings(settings) {}
178
179 /// Returns all parsing errors
180 const std::vector<std::string> &GetErrors() const { return fErrors; }
181 /// Retrieves all positional arguments
182 const std::vector<std::string> &GetArgs() const { return fArgs; }
183 /// Retrieves all parsed flags
184 const std::vector<RFlag> &GetFlags() const { return fFlags; }
185
186 /// Conveniency method to print any errors to `stream`.
187 /// \return true if any error was printed
188 bool ReportErrors(std::ostream &stream = std::cerr) const
189 {
190 for (const auto &err : fErrors)
191 stream << err << "\n";
192 return !fErrors.empty();
193 }
194
195 /// Defines a new flag (either a switch or a flag with argument).
196 /// The flag may be referred to as any of the values inside `aliases` (e.g. { "-h", "--help" }).
197 /// You must pass at least 1 string inside `aliases`.
198 /// All strings inside `aliases` must start with `-` or `--` and be at least 1 character long (aside the dashes).
199 /// Flags starting with a single `-` are considered "short", regardless of their actual length.
200 /// If all short flags are 1 character long, they may be collapsed into one and parsed as individual flags
201 /// (meaning a string like "-fgk" will be parsed as "-f -g -k") and the final flag may have a following argument.
202 /// This does NOT happen if any short flag is longer than 1 character, to avoid ambiguity.
203 ///
204 /// \param aliases All the equivalent names of this flag
205 /// \param type What kind of flag is this: with arguments or not
206 /// \param help Help string for the flag
207 /// \param flagOpts Bitmask of EFlagOpt for additional options
208 void AddFlag(std::initializer_list<std::string_view> aliases, EFlagType type = EFlagType::kSwitch,
209 std::string_view help = "", std::uint32_t flagOpts = 0)
210 {
211 const auto IsPrefixOf = [](std::string_view a, std::string_view b) {
212 return a.size() < b.size() && std::equal(a.begin(), a.end(), b.begin());
213 };
214
215 if (aliases.size() == 0)
216 throw std::invalid_argument("AddFlag must receive at least 1 name for the flag!");
217
218 if ((flagOpts & kFlagPrefixArg) && type != EFlagType::kWithArg)
219 throw std::invalid_argument("Flag `" + std::string(*aliases.begin()) +
220 "` has option kFlagPrefixArg but it's a Switch, so the option makes no sense.");
221
222 std::vector<RExpectedFlag> flagsToBeAdded;
223 flagsToBeAdded.reserve(aliases.size());
224
225 const auto nCurrentFlags = fExpectedFlags.size();
226 int aliasIdx = -1;
227 for (auto f : aliases) {
228 auto prefixLen = f.find_first_not_of('-');
229 if (prefixLen != 1 && prefixLen != 2)
230 throw std::invalid_argument(std::string("Invalid flag `") + std::string(f) +
231 "`: flags must start with '-' or '--'");
232 if (f.size() == prefixLen)
233 throw std::invalid_argument("Flag name cannot be empty");
234
235 auto flagName = f.substr(prefixLen);
236
237 // Check that we're not introducing ambiguities with prefix flags.
238 // While we're at it, also check that none of the given aliases were already added. Note that it is valid to
239 // add the same flag name as both a long and short flag (like "-foo" and "--foo") but only if they are aliases
240 // of the same flag.
241 for (const auto &expFlag : fExpectedFlags) {
242 if (expFlag.fName == flagName) {
243 throw std::invalid_argument(
244 "Flag `" + std::string(flagName) +
245 "` was added multiple times. Note that adding flags with the same name but different number of `-` "
246 "can only be done as aliases of the same call to AddFlag().");
247 }
248
249 if (!(flagOpts & kFlagPrefixArg) && !(expFlag.fOpts & kFlagPrefixArg))
250 continue;
251
252 // At least one of expFlag and f is a prefix flag: this means that they must not share a common prefix.
253 if (((expFlag.fOpts & kFlagPrefixArg) && IsPrefixOf(expFlag.fName, flagName)) ||
254 ((flagOpts & kFlagPrefixArg) && IsPrefixOf(flagName, expFlag.fName))) {
255 throw std::invalid_argument("Flags `" + expFlag.AsStr() + "` and `" + std::string(f) +
256 "` have a common prefix. This causes ambiguity because at least one of them "
257 "is marked with kFlagPrefixArg.");
258 }
259 }
260
261 // Check that the user didn't pass the very same flag multiple times in the same AddFlag invocation, as that's
262 // likely a mistake. This is different from the check done in the previous loop as that one only concerns
263 // flags that were already added before this AddFlag invocation.
264 for (const auto &expFlag : flagsToBeAdded) {
265 if (expFlag.AsStr() == f)
266 throw std::invalid_argument("The same flag `" + expFlag.AsStr() +
267 "` was passed multiple times to AddFlag().");
268 }
269
270 bool disallowsGrouping = (prefixLen == 1 && (f.size() > 2 || (flagOpts & kFlagPrefixArg)));
271 if (disallowsGrouping) {
272 if (fSettings.fFlagTreatment == EFlagTreatment::kDefault) {
273 fSettings.fFlagTreatment = EFlagTreatment::kSimple;
274 } else if (fSettings.fFlagTreatment == EFlagTreatment::kGrouped) {
275 throw std::invalid_argument(
276 std::string("Flags starting with a single dash must be 1 character long when `FlagTreatment == "
277 "EFlagTreatment::kGrouped'! Cannot accept given flag `") +
278 std::string(f) + "`");
279 }
280 }
281
282 RExpectedFlag expected;
283 expected.fFlagType = type;
284 expected.fName = flagName;
285 expected.fHelp = help;
286 expected.fAlias = aliasIdx;
287 expected.fShort = prefixLen == 1;
288 expected.fOpts = flagOpts;
289 flagsToBeAdded.push_back(expected);
290 if (aliasIdx < 0)
291 aliasIdx = nCurrentFlags + flagsToBeAdded.size() - 1;
292 }
293
294 fExpectedFlags.insert(fExpectedFlags.end(), flagsToBeAdded.begin(), flagsToBeAdded.end());
295 }
296
297 /// If `name` refers to a previously-defined switch (i.e. a boolean flag), returns how many times
298 /// the flag appeared (this will never be more than 1 unless the flag is allowed to appear multiple times).
299 /// \throws std::invalid_argument if the flag was undefined or defined as a flag with arguments
300 int GetSwitch(std::string_view name) const
301 {
302 const auto *exp = GetExpectedFlag(name);
303 if (!exp)
304 throw std::invalid_argument(std::string("Flag `") + std::string(name) + "` is not expected");
305 if (exp->fFlagType != EFlagType::kSwitch)
306 throw std::invalid_argument(std::string("Flag `") + std::string(name) + "` is not a switch");
307
308 std::string_view lookedUpName = name;
309 if (exp->fAlias >= 0)
310 lookedUpName = fExpectedFlags[exp->fAlias].fName;
311
312 int n = 0;
313 for (const auto &f : fFlags) {
314 n += f.fName == lookedUpName;
315 }
316 return n;
317 }
318
319 /// If `name` refers to a previously-defined non-switch flag, gets its value.
320 /// \throws std::invalid_argument if the flag was undefined or defined as a switch flag
321 std::string_view GetFlagValue(std::string_view name) const
322 {
323 auto values = GetFlagValues(name);
324 return values.empty() ? "" : values[0];
325 }
326
327 /// If `name` refers to a previously-defined non-switch flag, gets its values.
328 /// This will never return more than 1 value unless the flag is allowed to appear multiple times.
329 /// \throws std::invalid_argument if the flag was undefined or defined as a switch flag
330 std::vector<std::string_view> GetFlagValues(std::string_view name) const
331 {
332 const auto *exp = GetExpectedFlag(name);
333 if (!exp)
334 throw std::invalid_argument(std::string("Flag `") + std::string(name) + "` is not expected");
335 if (exp->fFlagType != EFlagType::kWithArg)
336 throw std::invalid_argument(std::string("Flag `") + std::string(name) + "` is a switch, use GetSwitch()");
337
338 std::string_view lookedUpName = name;
339 if (exp->fAlias >= 0)
340 lookedUpName = fExpectedFlags[exp->fAlias].fName;
341
342 std::vector<std::string_view> values;
343 for (const auto &f : fFlags) {
344 if (f.fName == lookedUpName)
345 values.push_back(f.fValue);
346 }
347 return values;
348 }
349
350 // Tries to retrieve the flag value as a type T.
351 // The only supported types are integral and floating point types.
352 // \return A value of type T if the flag is present and convertible
353 // \return nullopt if the flag is not there
354 // \throws std::invalid_argument if the flag is there but not convertible.
355 template <typename T>
356 std::optional<T> GetFlagValueAs(std::string_view name) const
357 {
359 return values.empty() ? std::nullopt : std::make_optional(values[0]);
360 }
361
362 // Tries to retrieve the flag values as a type T.
363 // The only supported types are integral and floating point types.
364 // \return An array of values of type T if the flag is present and all its values are convertible to T
365 // \throws std::invalid_argument if the flag has values but any of them are not convertible.
366 template <typename T>
367 std::vector<T> GetFlagValuesAs(std::string_view name) const
368 {
369 static_assert(std::is_integral_v<T> || std::is_floating_point_v<T>);
370
371 std::vector<T> values;
372 for (auto val : GetFlagValues(name)) {
373 // NOTE: on paper std::from_chars is supported since C++17, however some compilers don't properly support it
374 // (e.g. it's not available at all on MacOS < 26 and only the integer overload is available in AlmaLinux 8).
375 // There is also no compiler define that we can use to determine the availability, so we just use it only
376 // from C++20 and hope for the best.
377#if __cplusplus >= 202002L && !defined(__APPLE__)
378 T converted;
379 auto res = std::from_chars(val.data(), val.data() + val.size(), converted);
380 if (res.ptr == val.data() + val.size() && res.ec == std::errc{}) {
381 values.push_back(converted);
382 } else {
383 std::stringstream err;
384 err << "Failed to parse flag `" << name << "` with value `" << val << "`";
385 if constexpr (std::is_integral_v<T>)
386 err << " as an integer.\n";
387 else
388 err << " as a floating point number.\n";
389
390 if (res.ec == std::errc::result_out_of_range)
391 throw std::out_of_range(err.str());
392 else
393 throw std::invalid_argument(err.str());
394 }
395#else
396 std::conditional_t<std::is_integral_v<T>, long long, long double> converted;
397 std::size_t unconvertedPos;
398 if constexpr (std::is_integral_v<T>) {
399 converted = std::stoll(std::string(val), &unconvertedPos);
400 } else {
401 converted = std::stold(std::string(val), &unconvertedPos);
402 }
403
404 const bool isOor = converted > std::numeric_limits<T>::max();
405 if (unconvertedPos != val.size() || isOor) {
406 std::stringstream err;
407 err << "Failed to parse flag `" << name << "` with value `" << val << "`";
408 if constexpr (std::is_integral_v<T>)
409 err << " as an integer.\n";
410 else
411 err << " as a floating point number.\n";
412
413 if (isOor)
414 throw std::out_of_range(err.str());
415 else
416 throw std::invalid_argument(err.str());
417 }
418
419 values.push_back(converted);
420#endif
421 }
422 return values;
423 }
424
425 void Parse(const char **args, std::size_t nArgs)
426 {
427 bool forcePositional = false;
428
429 // If flag treatment is still Default by now it means we can safely group short flags (otherwise we'd have
430 // already changed it to Simple).
431 if (fSettings.fFlagTreatment == EFlagTreatment::kDefault)
432 fSettings.fFlagTreatment = EFlagTreatment::kGrouped;
433
434 // Contains one or more flags coming from one of the arguments (e.g. "-abc" may be split
435 // into flags "a", "b", and "c", which will be stored in `argStr`).
436 std::vector<std::string_view> argStr;
437
438 for (std::size_t i = 0; i < nArgs && fErrors.empty(); ++i) {
439 const char *arg = args[i];
440 const char *const argOrig = arg;
441
442 if (strcmp(arg, "--") == 0) {
443 forcePositional = true;
444 continue;
445 }
446
447 bool isFlag = !forcePositional && arg[0] == '-';
448 if (!isFlag) {
449 // positional argument
450 fArgs.push_back(arg);
451 continue;
452 }
453
454 ++arg; // eat first `-`.
455
456 // Parse long or short flag and its argument into `argStr` / `nxtArgStr`.
457 // Note that `argStr` may contain multiple flags in case of grouped short flags (in which case nxtArgStr
458 // refers only to the last one).
459 argStr.clear();
460 std::string_view nxtArgStr;
461 // If this is false `nxtArgStr` *must* refer to the next arg, otherwise it might or might not be.
462 bool nxtArgIsTentative = true;
463 if (arg[0] == '-') {
464 // long flag
465 ++arg;
466
467 // if we have prefix flags we need to check them first, as in that case the `=` sign should not be treated
468 // as the key-value separator.
469 const auto argLen = strlen(arg);
470 for (const auto &flag : fExpectedFlags) {
471 if ((flag.fOpts & kFlagPrefixArg) && flag.fName.size() < argLen &&
472 strncmp(arg, flag.fName.c_str(), flag.fName.size()) == 0) {
473 argStr.push_back(std::string_view(arg, flag.fName.size()));
474 nxtArgStr = std::string_view(arg + flag.fName.size(), argLen - flag.fName.size());
475 break;
476 }
477 }
478
479 if (argStr.empty()) {
480 const char *eq = strchr(arg, '=');
481 if (eq) {
482 argStr.push_back(std::string_view(arg, eq - arg));
483 nxtArgStr = std::string_view(eq + 1);
484 nxtArgIsTentative = false;
485 } else {
486 argStr.push_back(std::string_view(arg));
487 if (i < nArgs - 1 && args[i + 1][0] != '-') {
488 nxtArgStr = args[i + 1];
489 ++i;
490 }
491 }
492 }
493 } else {
494 // short flag.
495 // If flag grouping is active, all flags except the last one will have an implicitly empty argument.
496 auto argLen = strlen(arg);
497 while (fSettings.fFlagTreatment == EFlagTreatment::kGrouped && argLen > 1) {
498 argStr.push_back(std::string_view{arg, 1});
499 ++arg, --argLen;
500 }
501
502 argStr.push_back(std::string_view(arg));
503 if (i < nArgs - 1 && args[i + 1][0] != '-') {
504 nxtArgStr = args[i + 1];
505 ++i;
506 }
507 }
508
509 assert(argStr.size() < 2 || fSettings.fFlagTreatment == EFlagTreatment::kGrouped);
510
511 for (auto j = 0u; j < argStr.size(); ++j) {
512 std::string_view argS = argStr[j];
513
514 const auto *exp = GetExpectedFlag(argS);
515 if (!exp) {
516 fErrors.push_back(std::string("Unknown flag: ") + argOrig);
517 break;
518 }
519
520 // In Prefix mode, check if the returned expected flag is shorter than `argS`. This can mean two things:
521 // - if `nxtArgIsTentative == false` then this flag was followed by an equal sign, and in that case
522 // the intention is interpreted as "I want this flag's argument to be whatever follows the equal sign",
523 // which means we treat this as an unknown flag;
524 // - otherwise, we use the rest of `argS` as the argument to the flag.
525 // More concretely: if the user added flag "-D" and argS is "-Dfoo=bar", we parse it as
526 // {flag: "-Dfoo", arg: "bar"}, rather than {flag: "-D", arg: "foo=bar"}.
527 if ((exp->fOpts & kFlagPrefixArg) && argS.size() > exp->fName.size()) {
528 if (nxtArgIsTentative) {
529 i -= !nxtArgStr.empty(); // if we had already picked a candidate next arg, undo that.
530 nxtArgStr = argS.substr(exp->fName.size());
531 nxtArgIsTentative = false;
532 } else {
533 fErrors.push_back(std::string("Unknown flag: ") + argOrig);
534 break;
535 }
536 } else {
537 assert(exp->fName.size() == argS.size());
538 }
539
540 std::string_view nxtArg = (j == argStr.size() - 1) ? nxtArgStr : "";
541
543 flag.fHelp = exp->fHelp;
544 // If the flag is an alias (e.g. long version of a short one), save its name as the aliased one, so we
545 // can fetch the value later by using any of the aliases.
546 if (exp->fAlias < 0)
547 flag.fName = exp->fName;
548 else
549 flag.fName = fExpectedFlags[exp->fAlias].fName;
550
551 // Check for duplicate flags
552 if (!(exp->fOpts & kFlagAllowMultiple)) {
553 auto existingIt =
554 std::find_if(fFlags.begin(), fFlags.end(), [&flag](const auto &f) { return f.fName == flag.fName; });
555 if (existingIt != fFlags.end()) {
556 std::string err = std::string("Flag ") + exp->AsStr() + " appeared more than once";
557 if (exp->fFlagType == RCmdLineOpts::EFlagType::kWithArg)
558 err += " with the value: " + existingIt->fValue;
559 fErrors.push_back(err);
560 break;
561 }
562 }
563
564 // Check that arguments are what we expect.
565 if (exp->fFlagType == RCmdLineOpts::EFlagType::kWithArg) {
566 if (!nxtArg.empty()) {
567 flag.fValue = nxtArg;
568 } else {
569 fErrors.push_back("Missing argument for flag " + exp->AsStr());
570 }
571 } else {
572 if (!nxtArg.empty()) {
573 if (nxtArgIsTentative)
574 --i;
575 else
576 fErrors.push_back("Flag " + exp->AsStr() + " does not expect an argument");
577 }
578 }
579
580 if (!fErrors.empty())
581 break;
582
583 fFlags.push_back(flag);
584 }
585
586 if (!fErrors.empty())
587 break;
588 }
589 }
590};
591
592} // namespace ROOT
593
594#endif
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define a(i)
Definition RSha256.hxx:99
char name[80]
Definition TGX11.cxx:148
Double_t err
@ kDefault
Definition TSystem.h:243
std::vector< double > values
std::vector< RFlag > fFlags
Definition optparse.hxx:139
@ kSimple
-abc will always be treated as the single flag "-abc"
Definition optparse.hxx:105
@ kDefault
Will result to kGrouped if you don't define any short flag longer than 1 character,...
Definition optparse.hxx:103
@ kGrouped
-abc will be treated as "-a -b -c".
Definition optparse.hxx:109
const RExpectedFlag * GetExpectedFlag(std::string_view name) const
Definition optparse.hxx:156
void AddFlag(std::initializer_list< std::string_view > aliases, EFlagType type=EFlagType::kSwitch, std::string_view help="", std::uint32_t flagOpts=0)
Defines a new flag (either a switch or a flag with argument).
Definition optparse.hxx:208
const std::vector< std::string > & GetErrors() const
Returns all parsing errors.
Definition optparse.hxx:180
int GetSwitch(std::string_view name) const
If name refers to a previously-defined switch (i.e.
Definition optparse.hxx:300
const std::vector< std::string > & GetArgs() const
Retrieves all positional arguments.
Definition optparse.hxx:182
std::vector< T > GetFlagValuesAs(std::string_view name) const
Definition optparse.hxx:367
std::string_view GetFlagValue(std::string_view name) const
If name refers to a previously-defined non-switch flag, gets its value.
Definition optparse.hxx:321
std::vector< RExpectedFlag > fExpectedFlags
Definition optparse.hxx:153
RCmdLineOpts(RSettings settings={EFlagTreatment::kDefault})
Definition optparse.hxx:177
const std::vector< RFlag > & GetFlags() const
Retrieves all parsed flags.
Definition optparse.hxx:184
void Parse(const char **args, std::size_t nArgs)
Definition optparse.hxx:425
std::vector< std::string > fArgs
Definition optparse.hxx:140
std::vector< std::string_view > GetFlagValues(std::string_view name) const
If name refers to a previously-defined non-switch flag, gets its values.
Definition optparse.hxx:330
RSettings fSettings
Definition optparse.hxx:138
bool ReportErrors(std::ostream &stream=std::cerr) const
Conveniency method to print any errors to stream.
Definition optparse.hxx:188
std::optional< T > GetFlagValueAs(std::string_view name) const
Definition optparse.hxx:356
@ kFlagPrefixArg
Flag argument can appear right after this flag without a space or equal sign in between.
Definition optparse.hxx:134
@ kFlagAllowMultiple
Flag is allowed to appear multiple times (default: it's an error to see the same flag twice).
Definition optparse.hxx:131
std::vector< std::string > fErrors
Definition optparse.hxx:154
void help()
Definition geodemo.C:168
const Int_t n
Definition legend1.C:16
Small utility to parse cmdline options.
Definition RExports.h:71
bool StartsWith(std::string_view string, std::string_view prefix)
EFlagTreatment fFlagTreatment
Affects how flags are parsed (.
Definition optparse.hxx:114