Bash++
Bash++ compiler internal documentation
xgetopt.h
Go to the documentation of this file.
1
13#ifndef XGETOPT_H_
14#define XGETOPT_H_
15
16// Throw a compiler error if the C++ version is less than C++20
17#if __cplusplus < 202002L
18#error "xgetopt.h requires C++20 or later"
19#endif
20
21#include <getopt.h>
22#include <array>
23#include <vector>
24#include <string_view>
25#include <string>
26#include <optional>
27#include <cstddef>
28#include <cstdint>
29#include <type_traits>
30#include <algorithm>
31#include <concepts>
32#include <stdexcept>
33#include <utility>
34
35namespace XGetOpt {
36namespace Helpers {
37
38constexpr bool is_ws(char c) {
39 return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v';
40}
41
48template<size_t N>
50 std::array<char, N> data{};
51 size_t size = 0;
52 constexpr FixedString() = default;
53 constexpr FixedString(const char (&str)[N]) {
54 while (size + 1 < N && str[size] != '\0') {
55 data[size] = str[size];
56 size++;
57 }
58 // Ensure NUL termination when possible
59 if (size < N) data[size] = '\0';
60 }
61 constexpr ~FixedString() = default;
62
63 constexpr const char* c_str() const {
64 return data.data();
65 }
66
67 constexpr void append(const char* str) {
68 size_t index = 0;
69 while (str[index] != '\0' && size < N) {
70 data[size++] = str[index++];
71 }
72 }
73 constexpr void append(char c) {
74 if (size < N) {
75 data[size++] = c;
76 }
77 }
78 constexpr void append(char c, size_t count) {
79 for (size_t i = 0; i < count && size < N; i++) {
80 data[size++] = c;
81 }
82 }
83 constexpr void append(std::string_view sv) {
84 for (size_t i = 0; i < sv.size() && size < N; i++) {
85 data[size++] = sv[i];
86 }
87 }
88
89 constexpr std::string_view view() const {
90 return std::string_view(data.data(), size);
91 }
92
93 constexpr operator std::string_view() const {
94 return view();
95 }
96
97 constexpr size_t length() const {
98 return size;
99 }
100
101 constexpr std::optional<size_t> find(char c, size_t start = 0) const {
102 for (size_t i = start; i < size; i++) {
103 if (data[i] == c) {
104 return i;
105 }
106 }
107 return std::nullopt;
108 }
109
110 constexpr std::optional<size_t> find_first_of(const char* chars, size_t start = 0) const {
111 for (size_t i = start; i < size; i++) {
112 for (size_t j = 0; chars[j] != '\0'; j++) {
113 if (data[i] == chars[j]) {
114 return i;
115 }
116 }
117 }
118 return std::nullopt;
119 }
120
129 constexpr std::string_view get_next_word(size_t pos) const {
130 size_t next_whitespace_position = find_first_of(" \t\n", pos).value_or(size);
131 return view().substr(pos, next_whitespace_position - pos);
132 }
133};
134
135// Deduction guide for FixedString
136template<size_t N>
137FixedString(const char (&)[N]) -> FixedString<N>;
138
144struct TextView {
145 const char* data = nullptr;
146 size_t len = 0;
147
148 constexpr size_t length() const { return len; }
149
150 constexpr const char* c_str() const { return data; } // ptr must be NUL-terminated if used as C-string
151
152 constexpr std::string_view view() const {
153 return (data && len) ? std::string_view(data, len) : std::string_view{};
154 }
155
156 constexpr operator std::string_view() const { return view(); }
157
158 constexpr std::string_view get_next_word(size_t pos) const {
159 auto sv = view();
160 if (pos >= sv.size()) return {};
161 size_t end = pos;
162 while (end < sv.size()) {
163 char ch = sv[end];
164 if (ch == ' ' || ch == '\t' || ch == '\n') break;
165 ++end;
166 }
167 return sv.substr(pos, end - pos);
168 }
169};
170
171} // namespace Helpers
172
183
194template<
195 int ShortOpt,
196 Helpers::FixedString LongOpt,
197 Helpers::FixedString Description,
198 ArgumentRequirement ArgReq,
199 Helpers::FixedString ArgumentPlaceholder = "arg">
200struct Option {
201 static constexpr int shortopt = ShortOpt;
202 static constexpr auto longopt = LongOpt;
203 static constexpr auto description = Description;
204 static constexpr ArgumentRequirement argRequirement = ArgReq;
205 static constexpr auto argumentPlaceholder = ArgumentPlaceholder;
206};
207
208namespace Helpers {
209
217
218template<typename T>
219concept option_like = requires(T t) {
220 { t.shortopt } -> std::convertible_to<int>;
221 { t.argRequirement } -> std::convertible_to<ArgumentRequirement>;
222 { t.longopt.length() } -> std::convertible_to<size_t>;
223 { t.description.length() } -> std::convertible_to<size_t>;
224 { t.argumentPlaceholder.length() } -> std::convertible_to<size_t>;
225 { t.description.get_next_word(size_t{}) } -> std::same_as<std::string_view>;
226};
227
235template<option_like T>
236constexpr size_t option_label_length(const T& option) {
254 size_t length = 2; // "-x" or equivalent whitespace if no shortopt
255
256 if (option.longopt.length() > 0) {
257 length += 2; // ", " or equivalent whitespace if no shortopt
258
259 length += 2 + option.longopt.length(); // For "--longopt"
260 }
261
262 switch (option.argRequirement) {
264 length += 2 // " <"
265 + option.argumentPlaceholder.length() + 1; // "arg>"
266 break;
268 length += 1; // "["
269 if (option.longopt.length() > 0) {
270 length += 1; // "="
271 }
272 length += option.argumentPlaceholder.length() + 1; // "arg]"
273 break;
275 default:
276 break;
277 }
278
279 //length += 1; // Null terminator
280
281 return length;
282}
283
292template<size_t N, option_like T>
293constexpr size_t max_option_label_length(const std::array<T, N>& options) {
294 size_t max_length = 0;
295 for (size_t i = 0; i < N; i++) {
296 size_t length = option_label_length(options[i]);
297 if (length > max_length) {
298 max_length = length;
299 }
300 }
301 return max_length;
302}
303
312template<size_t N, option_like T>
313constexpr size_t calculate_help_string_length(const std::array<T, N>& options) {
314 size_t total_length = 0;
315 const size_t max_label_length = max_option_label_length(options);
316
317 for (size_t i = 0; i < N; i++) {
318 size_t line_length = 2;
319 total_length += 2; // indentation
320 const size_t label_length = option_label_length(options[i]);
321 const size_t padding_amount = max_label_length - label_length;
322
323 total_length += label_length + padding_amount + 1;
324 line_length += label_length + padding_amount + 1;
325
326 const size_t line_limit = 80;
327 size_t pos = line_length;
328
329 auto desc = options[i].description.view();
330 size_t idx = 0;
331
332 while (idx < desc.size()) {
333 // Skip whitespace
334 while (idx < desc.size() && is_ws(desc[idx])) ++idx;
335 if (idx >= desc.size()) break;
336
337 // Find word end
338 size_t end = idx;
339 while (end < desc.size() && !is_ws(desc[end])) ++end;
340
341 const size_t word_len = end - idx;
342
343 // Wrap if the word doesn't fit on this line (and we're not at the start of the desc column)
344 const size_t desc_col = max_label_length + 3;
345 if (pos + word_len > line_limit && pos > desc_col) {
346 total_length += 1; // '\n'
347 total_length += desc_col; // indentation to description column
348 pos = desc_col;
349 }
350
351 total_length += word_len;
352 pos += word_len;
353
354 // Look ahead: is there another word?
355 size_t next = end;
356 while (next < desc.size() && is_ws(desc[next])) ++next;
357 if (next < desc.size()) {
358 // Add a single separating space if it fits, else wrap
359 if (pos + 1 > line_limit) {
360 total_length += 1;
361 total_length += desc_col;
362 pos = desc_col;
363 } else {
364 total_length += 1;
365 pos += 1;
366 }
367 }
368
369 idx = end;
370 }
371
372 total_length += 1; // newline after each option
373 }
374
375 return total_length + 1; // trailing '\0'
376}
377
378} // namespace Helpers
379
380
387 private:
389 std::optional<std::string_view> argument;
390 public:
391 ParsedOption(int s, std::optional<std::string_view> arg)
392 : shortopt(s), argument(arg) {}
393
394 int getShortOpt() const {
395 return shortopt;
396 }
397
398 bool hasArgument() const {
399 return argument.has_value();
400 }
401
402 std::string_view getArgument() const {
403 return argument.value_or(std::string_view{});
404 }
405};
406
413 private:
414 std::vector<ParsedOption> options;
415 std::vector<std::string_view> nonOptionArguments;
416
425 void add(const OptionSequence& other) {
426 options.insert(options.end(), other.options.begin(), other.options.end());
428 other.nonOptionArguments.begin(), other.nonOptionArguments.end());
429 }
430 public:
431 void addOption(const ParsedOption& opt) {
432 options.push_back(opt);
433 }
434 void addNonOptionArgument(std::string_view arg) {
435 nonOptionArguments.push_back(arg);
436 }
437
439 add(rhs);
440 return *this;
441 }
443 lhs.add(rhs);
444 return lhs;
445 }
446
447 auto begin() const {
448 return options.begin();
449 }
450 auto end() const {
451 return options.end();
452 }
453 size_t size() const {
454 return options.size();
455 }
456 bool empty() const {
457 return options.empty();
458 }
459 const ParsedOption& operator[](size_t index) const {
460 return options[index];
461 }
462 const ParsedOption& at(size_t index) const {
463 return options.at(index);
464 }
465
473 bool hasOption(int shortopt) const {
474 return std::any_of(options.begin(), options.end(),
475 [shortopt](const ParsedOption& opt) {
476 return opt.getShortOpt() == shortopt;
477 });
478 }
479
485 const std::vector<std::string_view>& getNonOptionArguments() const {
486 return nonOptionArguments;
487 }
488};
489
519
521 int argc;
522 char** argv;
523};
524
531template<Helpers::option_like... Options>
533 private:
534 static constexpr size_t N = sizeof...(Options);
535 using OptionArray = std::array<Helpers::OptionView, N>;
536
537 static constexpr OptionArray options = OptionArray{{
539 Options::shortopt,
540 Helpers::TextView{ Options::longopt.c_str(), Options::longopt.length() },
541 Helpers::TextView{ Options::description.c_str(), Options::description.length() },
542 Options::argRequirement,
543 Helpers::TextView{ Options::argumentPlaceholder.c_str(), Options::argumentPlaceholder.length() }
544 }...
545 }};
546
547 // Compile-time guarantee: no two options have the same shortopt value
548 static_assert([]{
549 for (size_t i = 0; i < N; i++) {
550 for (size_t j = i + 1; j < N; j++) {
551 if (options[i].shortopt == options[j].shortopt) {
552 return false;
553 }
554 }
555 }
556 return true;
557 }(), "OptionParser error: Duplicate shortopt values detected in option definitions.");
558
559 // Likewise: no two options have the same (non-empty) longopt value
560 static_assert([]{
561 for (size_t i = 0; i < N; i++) {
562 if (options[i].longopt.length() == 0) continue;
563 for (size_t j = i + 1; j < N; j++) {
564 if (options[j].longopt.length() == 0) continue;
565 if (options[i].longopt.view() == options[j].longopt.view()) {
566 return false;
567 }
568 }
569 }
570 return true;
571 }(), "OptionParser error: Duplicate longopt values detected in option definitions.");
572
573 static constexpr size_t help_string_length = Helpers::calculate_help_string_length<N>(options);
574
575 static constexpr std::array<char, 3*N + 2> build_short_options_(const OptionArray& opts) {
576 size_t short_opt_index = 0;
577 std::array<char, 3*N + 2> short_opts{};
578
579 // Flag: REQUIRE_ORDER
580 // This means that option processing stops at the first non-option argument as mandated by POSIX
581 // Represented by a leading '+' in the short options string
582 // This flag is supported in the vast majority of getopt implementations
583 // Including GNU libc, every BSD variant, and musl, among others.
584 short_opts[short_opt_index++] = '+';
585
586 for (size_t i = 0; i < N; i++) {
587 if (opts[i].shortopt >= 33 && opts[i].shortopt <= 126) {
588 short_opts[short_opt_index++] = static_cast<char>(opts[i].shortopt);
589 switch (opts[i].argRequirement) {
591 short_opts[short_opt_index++] = ':';
592 break;
594 short_opts[short_opt_index++] = ':';
595 short_opts[short_opt_index++] = ':';
596 break;
598 default:
599 // No extra characters needed
600 break;
601 }
602 }
603 }
604
605 // Null-terminate the array
606 short_opts[short_opt_index] = '\0';
607 return short_opts;
608 }
609
610 static constexpr std::array<struct option, N + 1> build_long_options_(const OptionArray& opts) {
611 std::array<struct option, N + 1> long_opts{};
612
613 size_t idx = 0;
614
615 for (size_t i = 0; i < N; i++) {
616 if (opts[i].longopt.length() == 0) continue;
617 long_opts[idx].name = opts[i].longopt.c_str();
618 long_opts[idx].has_arg = static_cast<int>(opts[i].argRequirement);
619 long_opts[idx].flag = nullptr;
620 long_opts[idx].val = opts[i].shortopt;
621 idx++;
622 }
623
624 // Null-terminate the array (GNU getopt expects a sentinel)
625 long_opts[idx] = {nullptr, 0, nullptr, 0};
626 return long_opts;
627 }
628
629 static constexpr std::array<char, 3*N + 2> short_options = build_short_options_(options);
630 static constexpr std::array<struct option, N + 1> long_options = build_long_options_(options);
631
634 const size_t max_label_length = Helpers::max_option_label_length<N>(options);
635
636 for (size_t i = 0; i < N; i++) {
637 help_string.append(" ");
638
639 // First: write the '-x, --longopt <arg>' part
640 if (options[i].shortopt >= 33 && options[i].shortopt <= 126) {
641 help_string.append('-');
642 help_string.append(static_cast<char>(options[i].shortopt));
643 if (options[i].longopt.length() > 0) {
644 help_string.append(", ");
645 }
646 } else {
647 // Longopt only, add spaces for alignment
648 help_string.append(' ', 4); // The space that would've been used by "-x, "
649 }
650
651 if (options[i].longopt.length() > 0) {
652 help_string.append("--");
653 help_string.append(options[i].longopt);
654 }
655
656 switch (options[i].argRequirement) {
658 help_string.append(" <");
659 help_string.append(options[i].argumentPlaceholder);
660 help_string.append(">");
661 break;
663 // Shortopt-only? "[arg]"
664 // Longopt (with or without shortopt)? "[=arg]"
665 help_string.append("[");
666 if (options[i].longopt.length() > 0) {
667 help_string.append("=");
668 }
669 help_string.append(options[i].argumentPlaceholder);
670 help_string.append("]");
671 break;
673 default:
674 break;
675 }
676
677 // Second: write the option description
678
679 const size_t label_length = option_label_length(options[i]);
680 const size_t padding_amount = max_label_length - label_length;
681 help_string.append(' ', padding_amount + 1);
682
683 const size_t line_limit = 80;
684 const size_t desc_col = max_label_length + 3;
685 size_t pos = 2 + max_label_length + 1;
686
687 auto desc = options[i].description.view();
688 size_t idx = 0;
689
690 while (idx < desc.size()) {
691 while (idx < desc.size() && Helpers::is_ws(desc[idx])) ++idx;
692 if (idx >= desc.size()) break;
693
694 size_t end = idx;
695 while (end < desc.size() && !Helpers::is_ws(desc[end])) ++end;
696
697 std::string_view word = desc.substr(idx, end - idx);
698
699 if (pos + word.size() > line_limit && pos > desc_col) {
700 help_string.append('\n');
701 help_string.append(' ', desc_col);
702 pos = desc_col;
703 }
704
705 help_string.append(word);
706 pos += word.size();
707
708 size_t next = end;
709 while (next < desc.size() && Helpers::is_ws(desc[next])) ++next;
710 if (next < desc.size()) {
711 if (pos + 1 > line_limit) {
712 help_string.append('\n');
713 help_string.append(' ', desc_col);
714 pos = desc_col;
715 } else {
716 help_string.append(' ');
717 pos += 1;
718 }
719 }
720
721 idx = end;
722 }
723
724 help_string.append('\n');
725 }
726
727 return help_string;
728 }
729
732
733 template <StopCondition parseUntil>
734 std::pair<OptionSequence, OptionRemainder> parse_impl(int argc, char* argv[]) const {
735 OptionSequence parsed_options;
736 OptionRemainder unparsed_options{argc, argv};
737
738 // Reset in case parse() is called more than once in a process.
739 opterr = 0; // Don't let getopt print messages.
740 optind = 1;
741
742 // Platform-specific:
743 // GNU or Haiku: Set optind = 0 to reset getopt state fully
744 // Any of the BSDs or musl: Set optreset = 1 to reset getopt state fully
745 //
746 // Setting optind = 0 is UB outside of GNU/Haiku, so only do it there.
747 //
748 // GNU and Haiku have no concept of "optreset"
749 //
750 // On any unidentified platforms, we'll just have to cross our fingers.
751 //
752 // TODO(@rail5): Solaris?
753 #if defined(__GLIBC__) || defined(__HAIKU__)
754 optind = 0;
755 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__MUSL__)
756 optreset = 1;
757 #endif
758
759 int remainder_start = -1;
760
761 auto find_by_short = [&](int s) -> const auto* {
762 for (size_t i = 0; i < N; i++) {
763 if (options[i].shortopt == s) return &options[i];
764 }
765 return static_cast<const Helpers::OptionView*>(nullptr);
766 };
767
768 auto find_by_long = [&](std::string_view name) -> const auto* {
769 for (size_t i = 0; i < N; i++) {
770 if (options[i].longopt.length() > 0
771 && name == std::string_view(options[i].longopt.data, options[i].longopt.length())) {
772 return &options[i];
773 }
774 }
775 return static_cast<const Helpers::OptionView*>(nullptr);
776 };
777
778 auto token_to_long_name = [&](const char* tok) -> std::string_view {
779 if (!tok) return {};
780 std::string_view sv(tok);
781 if (sv.rfind("--", 0) != 0) return {};
782 sv.remove_prefix(2);
783 if (auto eq = sv.find('='); eq != std::string_view::npos) {
784 sv = sv.substr(0, eq);
785 }
786 return sv;
787 };
788
789 int longindex = 0;
790
791 for (;;) {
792 const int token_index = (optind == 0) ? 1 : optind; // The argv examined by this call
793 const int opt = getopt_long(argc, argv, short_options.data(), long_options.data(), &longindex);
794 if (opt == -1) {
795 if (optind >= argc) break; // All tokens processed
796
797 // Special-case:
798 // `--` ends option parsing.
799 // getopt_long consumes it, sets optind to the first token after `--`,
800 // and then returns -1.
801 // In our case, we still want to collect the remaining tokens as non-option arguments.
802 if (optind > 0 && std::string_view(argv[optind - 1]) == "--") {
803 if constexpr (parseUntil == BeforeFirstNonOptionArgument) {
804 remainder_start = token_index;
805 break;
806 } else if constexpr (parseUntil == AfterFirstNonOptionArgument) {
807 // Include the first non-option argument (which is argv[optind])
808 parsed_options.addNonOptionArgument(std::string_view(argv[optind]));
809 optind++; // Consume
810 remainder_start = optind;
811 break;
812 }
813 // Otherwise (AllOptions or BeforeFirstError), collect all remaining tokens
814 for (int i = optind; i < argc; i++) {
815 parsed_options.addNonOptionArgument(std::string_view(argv[i]));
816 }
817 optind = argc; // All tokens consumed
818 break;
819 }
820
821 // If we're here, we have a non-option argument to process
822 if constexpr (parseUntil == BeforeFirstNonOptionArgument) {
823 remainder_start = token_index;
824 break;
825 } else if constexpr (parseUntil == AfterFirstNonOptionArgument) {
826 parsed_options.addNonOptionArgument(std::string_view(argv[optind]));
827 optind++; // Consume
828 remainder_start = optind;
829 break;
830 }
831
832 // Otherwise, continue processing
833 // AllOptions or BeforeFirstError
834 parsed_options.addNonOptionArgument(std::string_view(argv[optind]));
835 optind++; // Consume
836 continue;
837 }
838
839 // Error handling
840 if (opt == '?') {
841 if constexpr (parseUntil == BeforeFirstError) {
842 remainder_start = token_index; // include offending token in remainder
843 break;
844 }
845
846 // Use token_index for the culprit token deterministically:
847 const char* culprit_tok =
848 (token_index >= 0 && token_index < argc) ? argv[token_index] : nullptr;
849
850 // For shortopts missing arg: optopt is set to the offending option character.
851 if (optopt != 0) {
852 const auto* o = find_by_short(optopt);
853 if (o && o->argRequirement == XGetOpt::ArgumentRequirement::RequiredArgument) {
854 std::string option_name;
855 if (optopt >= 33 && optopt <= 126) {
856 option_name += '-';
857 option_name += static_cast<char>(optopt);
858 } else {
859 option_name = std::string(o->longopt.length() > 0
860 ? o->longopt.data
861 : "???");
862 }
863 throw std::runtime_error(std::string("Missing required argument for option: ")
864 + option_name);
865 }
866 }
867
868 // For long options missing arg: argv[optind-1] is typically the option token.
869 const std::string_view long_name = token_to_long_name(culprit_tok);
870 if (!long_name.empty()) {
871 const auto* o = find_by_long(long_name);
872 if (o && o->argRequirement == XGetOpt::ArgumentRequirement::RequiredArgument) {
873 throw std::runtime_error(std::string("Missing required argument for option: --")
874 + std::string(long_name));
875 }
876 }
877
878 // Otherwise: unknown option
879 if (culprit_tok) {
880 throw std::runtime_error(std::string("Unknown option: ") + culprit_tok);
881 }
882
883 // Last resort:
884 throw std::runtime_error("Unknown option");
885 }
886
887 std::optional<std::string_view> argument;
888 if (optarg != nullptr) argument = std::string_view(optarg);
889 parsed_options.addOption(ParsedOption(opt, argument));
890 }
891
892 const int start = (remainder_start >= 0) ? remainder_start : optind;
893 unparsed_options.argc = argc - start;
894 unparsed_options.argv = &argv[start];
895 return {parsed_options, unparsed_options};
896 }
897 public:
903 constexpr std::string_view getHelpString() const {
904 return help_string.view();
905 }
906
907 constexpr const OptionArray& getOptions() const {
908 return options;
909 }
910
918 OptionSequence parse(int argc, char* argv[]) const {
919 return parse_impl<AllOptions>(argc, argv).first;
920 }
921
933 template<StopCondition end>
934 std::pair<OptionSequence, OptionRemainder> parse_until(int argc, char* argv[]) const {
935 return parse_impl<end>(argc, argv);
936 }
937};
938
939} // namespace XGetOpt
940
941#endif // XGETOPT_H_
Parses command-line arguments based on a set of defined options.
Definition xgetopt.h:532
static constexpr size_t help_string_length
Definition xgetopt.h:573
constexpr std::string_view getHelpString() const
Get the compile-time generated help string.
Definition xgetopt.h:903
static constexpr std::array< char, 3 *N+2 > build_short_options_(const OptionArray &opts)
Definition xgetopt.h:575
static constexpr size_t N
Definition xgetopt.h:534
std::pair< OptionSequence, OptionRemainder > parse_impl(int argc, char *argv[]) const
Definition xgetopt.h:734
static constexpr OptionArray options
Definition xgetopt.h:537
static constexpr Helpers::FixedString< help_string_length > generateHelpString(const OptionArray &options)
Definition xgetopt.h:632
std::pair< OptionSequence, OptionRemainder > parse_until(int argc, char *argv[]) const
Parse command-line arguments until a specified condition is met.
Definition xgetopt.h:934
static constexpr std::array< char, 3 *N+2 > short_options
Definition xgetopt.h:629
std::array< Helpers::OptionView, N > OptionArray
Definition xgetopt.h:535
static constexpr std::array< struct option, N+1 > long_options
Definition xgetopt.h:630
static constexpr std::array< struct option, N+1 > build_long_options_(const OptionArray &opts)
Definition xgetopt.h:610
OptionSequence parse(int argc, char *argv[]) const
Parse command-line arguments according to the defined options.
Definition xgetopt.h:918
static constexpr Helpers::FixedString< help_string_length > help_string
Definition xgetopt.h:731
constexpr const OptionArray & getOptions() const
Definition xgetopt.h:907
Represents a sequence of parsed options and non-option arguments.
Definition xgetopt.h:412
void addOption(const ParsedOption &opt)
Definition xgetopt.h:431
friend OptionSequence operator+(OptionSequence lhs, const OptionSequence &rhs)
Definition xgetopt.h:442
OptionSequence & operator+=(const OptionSequence &rhs)
Definition xgetopt.h:438
bool hasOption(int shortopt) const
Check if an option with the given shortopt was provided.
Definition xgetopt.h:473
const ParsedOption & operator[](size_t index) const
Definition xgetopt.h:459
auto end() const
Definition xgetopt.h:450
const std::vector< std::string_view > & getNonOptionArguments() const
Get a std::vector of all non-option arguments provided, in the order they were given.
Definition xgetopt.h:485
bool empty() const
Definition xgetopt.h:456
void addNonOptionArgument(std::string_view arg)
Definition xgetopt.h:434
size_t size() const
Definition xgetopt.h:453
void add(const OptionSequence &other)
Add another OptionSequence to this one.
Definition xgetopt.h:425
std::vector< std::string_view > nonOptionArguments
Definition xgetopt.h:415
auto begin() const
Definition xgetopt.h:447
std::vector< ParsedOption > options
Definition xgetopt.h:414
const ParsedOption & at(size_t index) const
Definition xgetopt.h:462
Represents a parsed command-line option and its associated argument (if any)
Definition xgetopt.h:386
std::string_view getArgument() const
Definition xgetopt.h:402
bool hasArgument() const
Definition xgetopt.h:398
ParsedOption(int s, std::optional< std::string_view > arg)
Definition xgetopt.h:391
std::optional< std::string_view > argument
Definition xgetopt.h:389
int getShortOpt() const
Definition xgetopt.h:394
int shortopt
Definition xgetopt.h:388
Definition xgetopt.h:219
constexpr size_t option_label_length(const T &option)
Calculate the length of the option label for help string formatting.
Definition xgetopt.h:236
constexpr size_t calculate_help_string_length(const std::array< T, N > &options)
Calculate the length of the help string for a set of options.
Definition xgetopt.h:313
constexpr bool is_ws(char c)
Definition xgetopt.h:38
constexpr size_t max_option_label_length(const std::array< T, N > &options)
Calculate the maximum option label length among a set of options.
Definition xgetopt.h:293
Definition xgetopt.h:35
ArgumentRequirement
Specifies whether an option requires an argument, has an optional argument, or has no argument.
Definition xgetopt.h:178
@ NoArgument
Definition xgetopt.h:179
@ RequiredArgument
Definition xgetopt.h:180
@ OptionalArgument
Definition xgetopt.h:181
StopCondition
Specifies when to stop parsing options.
Definition xgetopt.h:513
@ BeforeFirstNonOptionArgument
Definition xgetopt.h:515
@ AfterFirstNonOptionArgument
Definition xgetopt.h:516
@ BeforeFirstError
Definition xgetopt.h:517
@ AllOptions
Definition xgetopt.h:514
A fixed-size string class for compile-time string manipulation.
Definition xgetopt.h:49
constexpr FixedString()=default
constexpr void append(char c)
Definition xgetopt.h:73
constexpr void append(char c, size_t count)
Definition xgetopt.h:78
constexpr std::optional< size_t > find(char c, size_t start=0) const
Definition xgetopt.h:101
constexpr std::optional< size_t > find_first_of(const char *chars, size_t start=0) const
Definition xgetopt.h:110
std::array< char, N > data
Definition xgetopt.h:50
constexpr std::string_view get_next_word(size_t pos) const
Get the next word from the string starting at position pos.
Definition xgetopt.h:129
constexpr FixedString(const char(&str)[N])
Definition xgetopt.h:53
constexpr const char * c_str() const
Definition xgetopt.h:63
size_t size
Definition xgetopt.h:51
constexpr std::string_view view() const
Definition xgetopt.h:89
constexpr void append(std::string_view sv)
Definition xgetopt.h:83
constexpr ~FixedString()=default
constexpr void append(const char *str)
Definition xgetopt.h:67
constexpr size_t length() const
Definition xgetopt.h:97
Definition xgetopt.h:210
TextView longopt
Definition xgetopt.h:212
ArgumentRequirement argRequirement
Definition xgetopt.h:214
TextView description
Definition xgetopt.h:213
TextView argumentPlaceholder
Definition xgetopt.h:215
int shortopt
Definition xgetopt.h:211
A simple view over a text string.
Definition xgetopt.h:144
constexpr size_t length() const
Definition xgetopt.h:148
size_t len
Definition xgetopt.h:146
constexpr std::string_view view() const
Definition xgetopt.h:152
constexpr const char * c_str() const
Definition xgetopt.h:150
const char * data
Definition xgetopt.h:145
constexpr std::string_view get_next_word(size_t pos) const
Definition xgetopt.h:158
Definition xgetopt.h:520
char ** argv
Definition xgetopt.h:522
int argc
Definition xgetopt.h:521
Compile-time representation of a command-line option.
Definition xgetopt.h:200
static constexpr ArgumentRequirement argRequirement
Definition xgetopt.h:204
static constexpr auto argumentPlaceholder
Definition xgetopt.h:205
static constexpr int shortopt
Definition xgetopt.h:201
static constexpr auto longopt
Definition xgetopt.h:202
static constexpr auto description
Definition xgetopt.h:203