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
42constexpr bool is_ws_but_not_newline(char c) {
43 return c == ' ' || c == '\t' || c == '\r' || c == '\f' || c == '\v';
44}
45
52template<size_t N>
54 std::array<char, N> data{};
55 size_t size = 0;
56 constexpr FixedString() = default;
57 constexpr FixedString(const char (&str)[N]) {
58 while (size + 1 < N && str[size] != '\0') {
59 data[size] = str[size];
60 size++;
61 }
62 // Ensure NUL termination when possible
63 if (size < N) data[size] = '\0';
64 }
65 constexpr ~FixedString() = default;
66 constexpr FixedString(const FixedString& other) = default;
67 constexpr FixedString(FixedString&& other) noexcept = default;
68 constexpr FixedString& operator=(const FixedString& other) = default;
69 constexpr FixedString& operator=(FixedString&& other) = default;
70
71 constexpr const char* c_str() const {
72 return data.data();
73 }
74
75 constexpr void append(const char* str) {
76 size_t index = 0;
77 while (str[index] != '\0' && size < N) {
78 data[size++] = str[index++];
79 }
80 }
81 constexpr void append(char c) {
82 if (size < N) {
83 data[size++] = c;
84 }
85 }
86 constexpr void append(char c, size_t count) {
87 for (size_t i = 0; i < count && size < N; i++) {
88 data[size++] = c;
89 }
90 }
91 constexpr void append(std::string_view sv) {
92 for (size_t i = 0; i < sv.size() && size < N; i++) {
93 data[size++] = sv[i];
94 }
95 }
96
97 constexpr std::string_view view() const {
98 return std::string_view(data.data(), size);
99 }
100
101 constexpr operator std::string_view() const {
102 return view();
103 }
104
105 constexpr size_t length() const {
106 return size;
107 }
108
109 constexpr std::optional<size_t> find(char c, size_t start = 0) const {
110 for (size_t i = start; i < size; i++) {
111 if (data[i] == c) {
112 return i;
113 }
114 }
115 return std::nullopt;
116 }
117
118 constexpr std::optional<size_t> find_first_of(const char* chars, size_t start = 0) const {
119 for (size_t i = start; i < size; i++) {
120 for (size_t j = 0; chars[j] != '\0'; j++) {
121 if (data[i] == chars[j]) {
122 return i;
123 }
124 }
125 }
126 return std::nullopt;
127 }
128
137 constexpr std::string_view get_next_word(size_t pos) const {
138 size_t next_whitespace_position = find_first_of(" \t\n", pos).value_or(size);
139 return view().substr(pos, next_whitespace_position - pos);
140 }
141};
142
143// Deduction guide for FixedString
144template<size_t N>
145FixedString(const char (&)[N]) -> FixedString<N>;
146
152struct TextView {
153 const char* data = nullptr;
154 size_t len = 0;
155
156 constexpr size_t length() const { return len; }
157
158 constexpr const char* c_str() const { return data; } // ptr must be NUL-terminated if used as C-string
159
160 constexpr std::string_view view() const {
161 return (data && len) ? std::string_view(data, len) : std::string_view{};
162 }
163
164 constexpr operator std::string_view() const { return view(); }
165
166 constexpr std::string_view get_next_word(size_t pos) const {
167 auto sv = view();
168 if (pos >= sv.size()) return {};
169 size_t end = pos;
170 while (end < sv.size()) {
171 char ch = sv[end];
172 if (ch == ' ' || ch == '\t' || ch == '\n') break;
173 ++end;
174 }
175 return sv.substr(pos, end - pos);
176 }
177};
178
179} // namespace Helpers
180
191
202template<
203 int ShortOpt,
204 Helpers::FixedString LongOpt,
205 Helpers::FixedString Description,
206 ArgumentRequirement ArgReq,
207 Helpers::FixedString ArgumentPlaceholder = "arg">
208struct Option {
209 static constexpr int shortopt = ShortOpt;
210 static constexpr auto longopt = LongOpt;
211 static constexpr auto description = Description;
212 static constexpr ArgumentRequirement argRequirement = ArgReq;
213 static constexpr auto argumentPlaceholder = ArgumentPlaceholder;
214};
215
216namespace Helpers {
217
225
226template<typename T>
227concept option_like = requires(T t) {
228 { t.shortopt } -> std::convertible_to<int>;
229 { t.argRequirement } -> std::convertible_to<ArgumentRequirement>;
230 { t.longopt.length() } -> std::convertible_to<size_t>;
231 { t.description.length() } -> std::convertible_to<size_t>;
232 { t.argumentPlaceholder.length() } -> std::convertible_to<size_t>;
233 { t.description.get_next_word(size_t{}) } -> std::same_as<std::string_view>;
234};
235
243template<option_like T>
244constexpr size_t option_label_length(const T& option) {
262 size_t length = 2; // "-x" or equivalent whitespace if no shortopt
263
264 if (option.longopt.length() > 0) {
265 length += 2; // ", " or equivalent whitespace if no shortopt
266
267 length += 2 + option.longopt.length(); // For "--longopt"
268 }
269
270 switch (option.argRequirement) {
272 length += 2 // " <"
273 + option.argumentPlaceholder.length() + 1; // "arg>"
274 break;
276 length += 1; // "["
277 if (option.longopt.length() > 0) {
278 length += 1; // "="
279 }
280 length += option.argumentPlaceholder.length() + 1; // "arg]"
281 break;
283 default:
284 break;
285 }
286
287 return length;
288}
289
298template<size_t N, option_like T>
299constexpr size_t max_option_label_length(const std::array<T, N>& options) {
300 size_t max_length = 0;
301 for (size_t i = 0; i < N; i++) {
302 size_t length = option_label_length(options[i]);
303 if (length > max_length) {
304 max_length = length;
305 }
306 }
307 return max_length;
308}
309
318template<size_t N, option_like T>
319constexpr size_t calculate_help_string_length(const std::array<T, N>& options) {
320 size_t total_length = 0;
321 const size_t max_label_length = max_option_label_length(options);
322
323 for (size_t i = 0; i < N; i++) {
324 size_t line_length = 2;
325 total_length += 2; // indentation
326 const size_t label_length = option_label_length(options[i]);
327 const size_t padding_amount = max_label_length - label_length;
328
329 total_length += label_length + padding_amount + 1;
330 line_length += label_length + padding_amount + 1;
331
332 const size_t line_limit = 80;
333 size_t pos = line_length;
334
335 auto desc = options[i].description.view();
336 size_t idx = 0;
337
338 while (idx < desc.size()) {
339 // Skip whitespace
340 while (idx < desc.size() && is_ws_but_not_newline(desc[idx])) ++idx;
341 if (idx >= desc.size()) break;
342
343 // On newline,
344 // Add a line break, and then indent to the description column on the next line
345 if (desc[idx] == '\n') {
346 total_length += 1; // '\n'
347 total_length += max_label_length + 3; // indentation to description column
348 pos = max_label_length + 3;
349 idx++;
350 continue;
351 }
352
353 // Find word end
354 size_t end = idx;
355 while (end < desc.size() && !is_ws(desc[end])) ++end;
356
357 const size_t word_len = end - idx;
358
359 // Wrap if the word doesn't fit on this line (and we're not at the start of the desc column)
360 const size_t desc_col = max_label_length + 3;
361 if (pos + word_len > line_limit && pos > desc_col) {
362 total_length += 1; // '\n'
363 total_length += desc_col; // indentation to description column
364 pos = desc_col;
365 }
366
367 total_length += word_len;
368 pos += word_len;
369
370 // Look ahead: is there another word?
371 size_t next = end;
372 while (next < desc.size() && is_ws(desc[next])) ++next;
373 if (next < desc.size()) {
374 // Add a single separating space if it fits, else wrap
375 if (pos + 1 > line_limit) {
376 total_length += 1;
377 total_length += desc_col;
378 pos = desc_col;
379 } else {
380 total_length += 1;
381 pos += 1;
382 }
383 }
384
385 idx = end;
386 }
387
388 total_length += 1; // newline after each option
389 }
390
391 return total_length + 1; // trailing '\0'
392}
393
394} // namespace Helpers
395
396
403 private:
405 std::optional<std::string_view> argument;
406 public:
407 ParsedOption(int s, std::optional<std::string_view> arg)
408 : shortopt(s), argument(arg) {}
409
410 int getShortOpt() const {
411 return shortopt;
412 }
413
414 bool hasArgument() const {
415 return argument.has_value();
416 }
417
418 std::string_view getArgument() const {
419 return argument.value_or(std::string_view{});
420 }
421};
422
429 private:
430 std::vector<ParsedOption> options;
431 std::vector<std::string_view> nonOptionArguments;
432
441 void add(const OptionSequence& other) {
442 options.insert(options.end(), other.options.begin(), other.options.end());
444 other.nonOptionArguments.begin(), other.nonOptionArguments.end());
445 }
446 public:
447 void addOption(const ParsedOption& opt) {
448 options.push_back(opt);
449 }
450 void addNonOptionArgument(std::string_view arg) {
451 nonOptionArguments.push_back(arg);
452 }
453
455 add(rhs);
456 return *this;
457 }
459 lhs.add(rhs);
460 return lhs;
461 }
462
463 auto begin() const {
464 return options.begin();
465 }
466 auto end() const {
467 return options.end();
468 }
469 size_t size() const {
470 return options.size();
471 }
472 bool empty() const {
473 return options.empty();
474 }
475 const ParsedOption& operator[](size_t index) const {
476 return options[index];
477 }
478 const ParsedOption& at(size_t index) const {
479 return options.at(index);
480 }
481
489 bool hasOption(int shortopt) const {
490 return std::any_of(options.begin(), options.end(),
491 [shortopt](const ParsedOption& opt) {
492 return opt.getShortOpt() == shortopt;
493 });
494 }
495
501 const std::vector<std::string_view>& getNonOptionArguments() const {
502 return nonOptionArguments;
503 }
504};
505
535
537 int argc;
538 char** argv;
539};
540
547template<Helpers::option_like... Options>
549 private:
550 static constexpr size_t N = sizeof...(Options);
551 using OptionArray = std::array<Helpers::OptionView, N>;
552
553 static constexpr OptionArray options = OptionArray{{
555 Options::shortopt,
556 Helpers::TextView{ Options::longopt.c_str(), Options::longopt.length() },
557 Helpers::TextView{ Options::description.c_str(), Options::description.length() },
558 Options::argRequirement,
559 Helpers::TextView{ Options::argumentPlaceholder.c_str(), Options::argumentPlaceholder.length() }
560 }...
561 }};
562
563 // Compile-time guarantee: no two options have the same shortopt value
564 static_assert([]{
565 for (size_t i = 0; i < N; i++) {
566 for (size_t j = i + 1; j < N; j++) {
567 if (options[i].shortopt == options[j].shortopt) {
568 return false;
569 }
570 }
571 }
572 return true;
573 }(), "OptionParser error: Duplicate shortopt values detected in option definitions.");
574
575 // Likewise: no two options have the same (non-empty) longopt value
576 static_assert([]{
577 for (size_t i = 0; i < N; i++) {
578 if (options[i].longopt.length() == 0) continue;
579 for (size_t j = i + 1; j < N; j++) {
580 if (options[j].longopt.length() == 0) continue;
581 if (options[i].longopt.view() == options[j].longopt.view()) {
582 return false;
583 }
584 }
585 }
586 return true;
587 }(), "OptionParser error: Duplicate longopt values detected in option definitions.");
588
589 static constexpr size_t help_string_length = Helpers::calculate_help_string_length<N>(options);
590
591 static constexpr std::array<char, (3*N) + 2> build_short_options_(const OptionArray& opts) {
592 size_t short_opt_index = 0;
593 std::array<char, (3*N) + 2> short_opts{};
594
595 // Flag: REQUIRE_ORDER
596 // This means that option processing stops at the first non-option argument as mandated by POSIX
597 // Represented by a leading '+' in the short options string
598 // This flag is supported in the vast majority of getopt implementations
599 // Including GNU libc, every BSD variant, and musl, among others.
600 short_opts[short_opt_index++] = '+';
601
602 for (size_t i = 0; i < N; i++) {
603 if (opts[i].shortopt >= 33 && opts[i].shortopt <= 126) {
604 short_opts[short_opt_index++] = static_cast<char>(opts[i].shortopt);
605 switch (opts[i].argRequirement) {
607 short_opts[short_opt_index++] = ':';
608 break;
610 short_opts[short_opt_index++] = ':';
611 short_opts[short_opt_index++] = ':';
612 break;
614 default:
615 // No extra characters needed
616 break;
617 }
618 }
619 }
620
621 // Null-terminate the array
622 short_opts[short_opt_index] = '\0';
623 return short_opts;
624 }
625
626 static constexpr std::array<struct option, N + 1> build_long_options_(const OptionArray& opts) {
627 std::array<struct option, N + 1> long_opts{};
628
629 size_t idx = 0;
630
631 for (size_t i = 0; i < N; i++) {
632 if (opts[i].longopt.length() == 0) continue;
633 long_opts[idx].name = opts[i].longopt.c_str();
634 long_opts[idx].has_arg = static_cast<int>(opts[i].argRequirement);
635 long_opts[idx].flag = nullptr;
636 long_opts[idx].val = opts[i].shortopt;
637 idx++;
638 }
639
640 // Null-terminate the array (GNU getopt expects a sentinel)
641 long_opts[idx] = {nullptr, 0, nullptr, 0};
642 return long_opts;
643 }
644
645 static constexpr std::array<char, (3*N) + 2> short_options = build_short_options_(options);
646 static constexpr std::array<struct option, N + 1> long_options = build_long_options_(options);
647
650 const size_t max_label_length = Helpers::max_option_label_length<N>(options);
651
652 for (size_t i = 0; i < N; i++) {
653 help_string.append(" ");
654
655 // First: write the '-x, --longopt <arg>' part
656 if (options[i].shortopt >= 33 && options[i].shortopt <= 126) {
657 help_string.append('-');
658 help_string.append(static_cast<char>(options[i].shortopt));
659 if (options[i].longopt.length() > 0) {
660 help_string.append(", ");
661 }
662 } else {
663 // Longopt only, add spaces for alignment
664 help_string.append(' ', 4); // The space that would've been used by "-x, "
665 }
666
667 if (options[i].longopt.length() > 0) {
668 help_string.append("--");
669 help_string.append(options[i].longopt);
670 }
671
672 switch (options[i].argRequirement) {
674 help_string.append(" <");
675 help_string.append(options[i].argumentPlaceholder);
676 help_string.append(">");
677 break;
679 // Shortopt-only? "[arg]"
680 // Longopt (with or without shortopt)? "[=arg]"
681 help_string.append("[");
682 if (options[i].longopt.length() > 0) {
683 help_string.append("=");
684 }
685 help_string.append(options[i].argumentPlaceholder);
686 help_string.append("]");
687 break;
689 default:
690 break;
691 }
692
693 // Second: write the option description
694
695 const size_t label_length = option_label_length(options[i]);
696 const size_t padding_amount = max_label_length - label_length;
697 help_string.append(' ', padding_amount + 1);
698
699 const size_t line_limit = 80;
700 const size_t desc_col = max_label_length + 3;
701 size_t pos = 2 + max_label_length + 1;
702
703 auto desc = options[i].description.view();
704 size_t idx = 0;
705
706 while (idx < desc.size()) {
707 while (idx < desc.size() && Helpers::is_ws_but_not_newline(desc[idx])) ++idx;
708 if (idx >= desc.size()) break;
709
710 // On newline,
711 // Add a line break, and then indent to the description column on the next line
712 if (desc[idx] == '\n') {
713 help_string.append('\n');
714 help_string.append(' ', desc_col);
715 pos = desc_col;
716 idx++;
717 continue;
718 }
719
720 size_t end = idx;
721 while (end < desc.size() && !Helpers::is_ws(desc[end])) ++end;
722
723 std::string_view word = desc.substr(idx, end - idx);
724
725 if (pos + word.size() > line_limit && pos > desc_col) {
726 help_string.append('\n');
727 help_string.append(' ', desc_col);
728 pos = desc_col;
729 }
730
731 help_string.append(word);
732 pos += word.size();
733
734 size_t next = end;
735 while (next < desc.size() && Helpers::is_ws(desc[next])) ++next;
736 if (next < desc.size()) {
737 if (pos + 1 > line_limit) {
738 help_string.append('\n');
739 help_string.append(' ', desc_col);
740 pos = desc_col;
741 } else {
742 help_string.append(' ');
743 pos += 1;
744 }
745 }
746
747 idx = end;
748 }
749
750 help_string.append('\n');
751 }
752
753 return help_string;
754 }
755
758
759 template <StopCondition parseUntil>
760 std::pair<OptionSequence, OptionRemainder> parse_impl(int argc, char* argv[]) const {
761 OptionSequence parsed_options;
762 OptionRemainder unparsed_options{argc, argv};
763
764 // Reset in case parse() is called more than once in a process.
765 opterr = 0; // Don't let getopt print messages.
766 optind = 1;
767
768 // Platform-specific:
769 // GNU or Haiku: Set optind = 0 to reset getopt state fully
770 // Any of the BSDs or musl: Set optreset = 1 to reset getopt state fully
771 //
772 // Setting optind = 0 is UB outside of GNU/Haiku, so only do it there.
773 //
774 // GNU and Haiku have no concept of "optreset"
775 //
776 // On any unidentified platforms, we'll just have to cross our fingers.
777 //
778 // TODO(@rail5): Solaris?
779 #if defined(__GLIBC__) || defined(__HAIKU__)
780 optind = 0;
781 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__MUSL__)
782 optreset = 1;
783 #endif
784
785 int remainder_start = -1;
786
787 auto find_by_short = [&](int s) -> const auto* {
788 for (size_t i = 0; i < N; i++) {
789 if (options[i].shortopt == s) return &options[i];
790 }
791 return static_cast<const Helpers::OptionView*>(nullptr);
792 };
793
794 auto find_by_long = [&](std::string_view name) -> const auto* {
795 for (size_t i = 0; i < N; i++) {
796 if (options[i].longopt.length() > 0
797 && name == std::string_view(options[i].longopt.data, options[i].longopt.length())) {
798 return &options[i];
799 }
800 }
801 return static_cast<const Helpers::OptionView*>(nullptr);
802 };
803
804 auto token_to_long_name = [&](const char* tok) -> std::string_view {
805 if (!tok) return {};
806 std::string_view sv(tok);
807 if (!sv.starts_with("--")) return {};
808 sv.remove_prefix(2);
809 if (auto eq = sv.find('='); eq != std::string_view::npos) {
810 sv = sv.substr(0, eq);
811 }
812 return sv;
813 };
814
815 int longindex = 0;
816
817 for (;;) {
818 const int token_index = (optind == 0) ? 1 : optind; // The argv examined by this call
819 const int opt = getopt_long(argc, argv, short_options.data(), long_options.data(), &longindex);
820 if (opt == -1) {
821 if (optind >= argc) break; // All tokens processed
822
823 // Special-case:
824 // `--` ends option parsing.
825 // getopt_long consumes it, sets optind to the first token after `--`,
826 // and then returns -1.
827 // In our case, we still want to collect the remaining tokens as non-option arguments.
828 if (optind > 0 && std::string_view(argv[optind - 1]) == "--") {
829 if constexpr (parseUntil == BeforeFirstNonOptionArgument) {
830 remainder_start = token_index;
831 break;
832 } else if constexpr (parseUntil == AfterFirstNonOptionArgument) {
833 // Include the first non-option argument (which is argv[optind])
834 parsed_options.addNonOptionArgument(std::string_view(argv[optind]));
835 optind++; // Consume
836 remainder_start = optind;
837 break;
838 }
839 // Otherwise (AllOptions or BeforeFirstError), collect all remaining tokens
840 for (int i = optind; i < argc; i++) {
841 parsed_options.addNonOptionArgument(std::string_view(argv[i]));
842 }
843 optind = argc; // All tokens consumed
844 break;
845 }
846
847 // If we're here, we have a non-option argument to process
848 if constexpr (parseUntil == BeforeFirstNonOptionArgument) {
849 remainder_start = token_index;
850 break;
851 } else if constexpr (parseUntil == AfterFirstNonOptionArgument) {
852 parsed_options.addNonOptionArgument(std::string_view(argv[optind]));
853 optind++; // Consume
854 remainder_start = optind;
855 break;
856 }
857
858 // Otherwise, continue processing
859 // AllOptions or BeforeFirstError
860 parsed_options.addNonOptionArgument(std::string_view(argv[optind]));
861 optind++; // Consume
862 continue;
863 }
864
865 // Error handling
866 if (opt == '?') {
867 if constexpr (parseUntil == BeforeFirstError) {
868 remainder_start = token_index; // include offending token in remainder
869 break;
870 }
871
872 // Use token_index for the culprit token deterministically:
873 const char* culprit_tok =
874 (token_index >= 0 && token_index < argc) ? argv[token_index] : nullptr;
875
876 // For shortopts missing arg: optopt is set to the offending option character.
877 if (optopt != 0) {
878 const auto* o = find_by_short(optopt);
879 if (o && o->argRequirement == XGetOpt::ArgumentRequirement::RequiredArgument) {
880 std::string option_name;
881 if (optopt >= 33 && optopt <= 126) {
882 option_name += '-';
883 option_name += static_cast<char>(optopt);
884 } else {
885 option_name = std::string(o->longopt.length() > 0
886 ? o->longopt.data
887 : "???");
888 }
889 throw std::runtime_error(std::string("Missing required argument for option: ")
890 + option_name);
891 }
892 }
893
894 // For long options missing arg: argv[optind-1] is typically the option token.
895 const std::string_view long_name = token_to_long_name(culprit_tok);
896 if (!long_name.empty()) {
897 const auto* o = find_by_long(long_name);
898 if (o && o->argRequirement == XGetOpt::ArgumentRequirement::RequiredArgument) {
899 throw std::runtime_error(std::string("Missing required argument for option: --")
900 + std::string(long_name));
901 }
902 }
903
904 // Otherwise: unknown option
905 if (culprit_tok) {
906 throw std::runtime_error(std::string("Unknown option: ") + culprit_tok);
907 }
908
909 // Last resort:
910 throw std::runtime_error("Unknown option");
911 }
912
913 std::optional<std::string_view> argument;
914 if (optarg != nullptr) argument = std::string_view(optarg);
915 parsed_options.addOption(ParsedOption(opt, argument));
916 }
917
918 const int start = (remainder_start >= 0) ? remainder_start : optind;
919 unparsed_options.argc = argc - start;
920 unparsed_options.argv = &argv[start];
921 return {parsed_options, unparsed_options};
922 }
923 public:
929 constexpr std::string_view getHelpString() const {
930 return help_string.view();
931 }
932
933 constexpr const OptionArray& getOptions() const {
934 return options;
935 }
936
944 OptionSequence parse(int argc, char* argv[]) const {
945 return parse_impl<AllOptions>(argc, argv).first;
946 }
947
959 template<StopCondition end>
960 std::pair<OptionSequence, OptionRemainder> parse_until(int argc, char* argv[]) const {
961 return parse_impl<end>(argc, argv);
962 }
963};
964
965} // namespace XGetOpt
966
967#endif // XGETOPT_H_
Parses command-line arguments based on a set of defined options.
Definition xgetopt.h:548
static constexpr size_t help_string_length
Definition xgetopt.h:589
constexpr std::string_view getHelpString() const
Get the compile-time generated help string.
Definition xgetopt.h:929
static constexpr std::array< char,(3 *N)+2 > build_short_options_(const OptionArray &opts)
Definition xgetopt.h:591
static constexpr size_t N
Definition xgetopt.h:550
std::pair< OptionSequence, OptionRemainder > parse_impl(int argc, char *argv[]) const
Definition xgetopt.h:760
static constexpr OptionArray options
Definition xgetopt.h:553
static constexpr Helpers::FixedString< help_string_length > generateHelpString(const OptionArray &options)
Definition xgetopt.h:648
std::pair< OptionSequence, OptionRemainder > parse_until(int argc, char *argv[]) const
Parse command-line arguments until a specified condition is met.
Definition xgetopt.h:960
std::array< Helpers::OptionView, N > OptionArray
Definition xgetopt.h:551
static constexpr std::array< struct option, N+1 > long_options
Definition xgetopt.h:646
static constexpr std::array< char,(3 *N)+2 > short_options
Definition xgetopt.h:645
static constexpr std::array< struct option, N+1 > build_long_options_(const OptionArray &opts)
Definition xgetopt.h:626
OptionSequence parse(int argc, char *argv[]) const
Parse command-line arguments according to the defined options.
Definition xgetopt.h:944
static constexpr Helpers::FixedString< help_string_length > help_string
Definition xgetopt.h:757
constexpr const OptionArray & getOptions() const
Definition xgetopt.h:933
Represents a sequence of parsed options and non-option arguments.
Definition xgetopt.h:428
void addOption(const ParsedOption &opt)
Definition xgetopt.h:447
friend OptionSequence operator+(OptionSequence lhs, const OptionSequence &rhs)
Definition xgetopt.h:458
OptionSequence & operator+=(const OptionSequence &rhs)
Definition xgetopt.h:454
bool hasOption(int shortopt) const
Check if an option with the given shortopt was provided.
Definition xgetopt.h:489
const ParsedOption & operator[](size_t index) const
Definition xgetopt.h:475
auto end() const
Definition xgetopt.h:466
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:501
bool empty() const
Definition xgetopt.h:472
void addNonOptionArgument(std::string_view arg)
Definition xgetopt.h:450
size_t size() const
Definition xgetopt.h:469
void add(const OptionSequence &other)
Add another OptionSequence to this one.
Definition xgetopt.h:441
std::vector< std::string_view > nonOptionArguments
Definition xgetopt.h:431
auto begin() const
Definition xgetopt.h:463
std::vector< ParsedOption > options
Definition xgetopt.h:430
const ParsedOption & at(size_t index) const
Definition xgetopt.h:478
Represents a parsed command-line option and its associated argument (if any)
Definition xgetopt.h:402
std::string_view getArgument() const
Definition xgetopt.h:418
bool hasArgument() const
Definition xgetopt.h:414
ParsedOption(int s, std::optional< std::string_view > arg)
Definition xgetopt.h:407
std::optional< std::string_view > argument
Definition xgetopt.h:405
int getShortOpt() const
Definition xgetopt.h:410
int shortopt
Definition xgetopt.h:404
Definition xgetopt.h:227
constexpr size_t option_label_length(const T &option)
Calculate the length of the option label for help string formatting.
Definition xgetopt.h:244
constexpr bool is_ws_but_not_newline(char c)
Definition xgetopt.h:42
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:319
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:299
Definition xgetopt.h:35
ArgumentRequirement
Specifies whether an option requires an argument, has an optional argument, or has no argument.
Definition xgetopt.h:186
@ NoArgument
Definition xgetopt.h:187
@ RequiredArgument
Definition xgetopt.h:188
@ OptionalArgument
Definition xgetopt.h:189
StopCondition
Specifies when to stop parsing options.
Definition xgetopt.h:529
@ BeforeFirstNonOptionArgument
Definition xgetopt.h:531
@ AfterFirstNonOptionArgument
Definition xgetopt.h:532
@ BeforeFirstError
Definition xgetopt.h:533
@ AllOptions
Definition xgetopt.h:530
A fixed-size string class for compile-time string manipulation.
Definition xgetopt.h:53
constexpr FixedString & operator=(const FixedString &other)=default
constexpr FixedString(const FixedString &other)=default
constexpr FixedString()=default
constexpr void append(char c)
Definition xgetopt.h:81
constexpr FixedString & operator=(FixedString &&other)=default
constexpr void append(char c, size_t count)
Definition xgetopt.h:86
constexpr FixedString(FixedString &&other) noexcept=default
constexpr std::optional< size_t > find(char c, size_t start=0) const
Definition xgetopt.h:109
constexpr std::optional< size_t > find_first_of(const char *chars, size_t start=0) const
Definition xgetopt.h:118
std::array< char, N > data
Definition xgetopt.h:54
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:137
constexpr FixedString(const char(&str)[N])
Definition xgetopt.h:57
constexpr const char * c_str() const
Definition xgetopt.h:71
size_t size
Definition xgetopt.h:55
constexpr std::string_view view() const
Definition xgetopt.h:97
constexpr void append(std::string_view sv)
Definition xgetopt.h:91
constexpr ~FixedString()=default
constexpr void append(const char *str)
Definition xgetopt.h:75
constexpr size_t length() const
Definition xgetopt.h:105
Definition xgetopt.h:218
TextView longopt
Definition xgetopt.h:220
ArgumentRequirement argRequirement
Definition xgetopt.h:222
TextView description
Definition xgetopt.h:221
TextView argumentPlaceholder
Definition xgetopt.h:223
int shortopt
Definition xgetopt.h:219
A simple view over a text string.
Definition xgetopt.h:152
constexpr size_t length() const
Definition xgetopt.h:156
size_t len
Definition xgetopt.h:154
constexpr std::string_view view() const
Definition xgetopt.h:160
constexpr const char * c_str() const
Definition xgetopt.h:158
const char * data
Definition xgetopt.h:153
constexpr std::string_view get_next_word(size_t pos) const
Definition xgetopt.h:166
Definition xgetopt.h:536
char ** argv
Definition xgetopt.h:538
int argc
Definition xgetopt.h:537
Compile-time representation of a command-line option.
Definition xgetopt.h:208
static constexpr ArgumentRequirement argRequirement
Definition xgetopt.h:212
static constexpr auto argumentPlaceholder
Definition xgetopt.h:213
static constexpr int shortopt
Definition xgetopt.h:209
static constexpr auto longopt
Definition xgetopt.h:210
static constexpr auto description
Definition xgetopt.h:211