17#if __cplusplus < 202002L
18#error "xgetopt.h requires C++20 or later"
39 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f' || c ==
'\v';
50 std::array<char, N>
data{};
54 while (
size + 1 < N && str[
size] !=
'\0') {
63 constexpr const char*
c_str()
const {
67 constexpr void append(
const char* str) {
69 while (str[index] !=
'\0' &&
size < N) {
78 constexpr void append(
char c,
size_t count) {
79 for (
size_t i = 0; i < count &&
size < N; i++) {
83 constexpr void append(std::string_view sv) {
84 for (
size_t i = 0; i < sv.size() &&
size < N; i++) {
89 constexpr std::string_view
view()
const {
90 return std::string_view(
data.data(),
size);
93 constexpr operator std::string_view()
const {
101 constexpr std::optional<size_t>
find(
char c,
size_t start = 0)
const {
102 for (
size_t i = start; i <
size; i++) {
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]) {
131 return view().substr(pos, next_whitespace_position - pos);
152 constexpr std::string_view
view()
const {
153 return (
data &&
len) ? std::string_view(
data,
len) : std::string_view{};
156 constexpr operator std::string_view()
const {
return view(); }
160 if (pos >= sv.size())
return {};
162 while (end < sv.size()) {
164 if (ch ==
' ' || ch ==
'\t' || ch ==
'\n')
break;
167 return sv.substr(pos, end - pos);
196 Helpers::FixedString LongOpt,
197 Helpers::FixedString Description,
199 Helpers::FixedString ArgumentPlaceholder =
"arg">
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>;
235template<option_like T>
256 if (option.longopt.length() > 0) {
259 length += 2 + option.longopt.length();
262 switch (option.argRequirement) {
265 + option.argumentPlaceholder.length() + 1;
269 if (option.longopt.length() > 0) {
272 length += option.argumentPlaceholder.length() + 1;
292template<
size_t N, option_like T>
294 size_t max_length = 0;
295 for (
size_t i = 0; i < N; i++) {
297 if (length > max_length) {
312template<
size_t N, option_like T>
314 size_t total_length = 0;
317 for (
size_t i = 0; i < N; i++) {
318 size_t line_length = 2;
321 const size_t padding_amount = max_label_length - label_length;
323 total_length += label_length + padding_amount + 1;
324 line_length += label_length + padding_amount + 1;
326 const size_t line_limit = 80;
327 size_t pos = line_length;
329 auto desc = options[i].description.view();
332 while (idx < desc.size()) {
334 while (idx < desc.size() &&
is_ws(desc[idx])) ++idx;
335 if (idx >= desc.size())
break;
339 while (end < desc.size() && !
is_ws(desc[end])) ++end;
341 const size_t word_len = end - idx;
344 const size_t desc_col = max_label_length + 3;
345 if (pos + word_len > line_limit && pos > desc_col) {
347 total_length += desc_col;
351 total_length += word_len;
356 while (next < desc.size() &&
is_ws(desc[next])) ++next;
357 if (next < desc.size()) {
359 if (pos + 1 > line_limit) {
361 total_length += desc_col;
375 return total_length + 1;
403 return argument.value_or(std::string_view{});
476 return opt.getShortOpt() == shortopt;
534 static constexpr size_t N =
sizeof...(Options);
542 Options::argRequirement,
549 for (
size_t i = 0; i <
N; i++) {
550 for (
size_t j = i + 1; j <
N; j++) {
557 }(),
"OptionParser error: Duplicate shortopt values detected in option definitions.");
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;
571 }(),
"OptionParser error: Duplicate longopt values detected in option definitions.");
576 size_t short_opt_index = 0;
577 std::array<char, 3*N + 2> short_opts{};
584 short_opts[short_opt_index++] =
'+';
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++] =
':';
594 short_opts[short_opt_index++] =
':';
595 short_opts[short_opt_index++] =
':';
606 short_opts[short_opt_index] =
'\0';
611 std::array<struct option, N + 1> long_opts{};
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;
625 long_opts[idx] = {
nullptr, 0,
nullptr, 0};
634 const size_t max_label_length = Helpers::max_option_label_length<N>(
options);
636 for (
size_t i = 0; i <
N; i++) {
643 if (
options[i].longopt.length() > 0) {
651 if (
options[i].longopt.length() > 0) {
656 switch (
options[i].argRequirement) {
666 if (
options[i].longopt.length() > 0) {
679 const size_t label_length = option_label_length(
options[i]);
680 const size_t padding_amount = max_label_length - label_length;
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;
687 auto desc =
options[i].description.view();
690 while (idx < desc.size()) {
692 if (idx >= desc.size())
break;
697 std::string_view word = desc.substr(idx, end - idx);
699 if (pos + word.size() > line_limit && pos > desc_col) {
710 if (next < desc.size()) {
711 if (pos + 1 > line_limit) {
733 template <StopCondition parseUntil>
734 std::pair<OptionSequence, OptionRemainder>
parse_impl(
int argc,
char* argv[])
const {
753 #if defined(__GLIBC__) || defined(__HAIKU__)
755 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__MUSL__)
759 int remainder_start = -1;
761 auto find_by_short = [&](
int s) ->
const auto* {
762 for (
size_t i = 0; i <
N; i++) {
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())) {
778 auto token_to_long_name = [&](
const char* tok) -> std::string_view {
780 std::string_view sv(tok);
781 if (sv.rfind(
"--", 0) != 0)
return {};
783 if (
auto eq = sv.find(
'='); eq != std::string_view::npos) {
784 sv = sv.substr(0, eq);
792 const int token_index = (optind == 0) ? 1 : optind;
795 if (optind >= argc)
break;
802 if (optind > 0 && std::string_view(argv[optind - 1]) ==
"--") {
804 remainder_start = token_index;
810 remainder_start = optind;
814 for (
int i = optind; i < argc; i++) {
823 remainder_start = token_index;
828 remainder_start = optind;
842 remainder_start = token_index;
847 const char* culprit_tok =
848 (token_index >= 0 && token_index < argc) ? argv[token_index] :
nullptr;
852 const auto* o = find_by_short(optopt);
854 std::string option_name;
855 if (optopt >= 33 && optopt <= 126) {
857 option_name +=
static_cast<char>(optopt);
859 option_name = std::string(o->longopt.length() > 0
863 throw std::runtime_error(std::string(
"Missing required argument for option: ")
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);
873 throw std::runtime_error(std::string(
"Missing required argument for option: --")
874 + std::string(long_name));
880 throw std::runtime_error(std::string(
"Unknown option: ") + culprit_tok);
884 throw std::runtime_error(
"Unknown option");
887 std::optional<std::string_view> argument;
888 if (optarg !=
nullptr) argument = std::string_view(optarg);
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};
919 return parse_impl<AllOptions>(argc, argv).first;
933 template<StopCondition end>
934 std::pair<OptionSequence, OptionRemainder>
parse_until(
int argc,
char* argv[])
const {
935 return parse_impl<end>(argc, argv);
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
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
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
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
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