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';
43 return c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\f' || c ==
'\v';
54 std::array<char, N>
data{};
58 while (
size + 1 < N && str[
size] !=
'\0') {
71 constexpr const char*
c_str()
const {
75 constexpr void append(
const char* str) {
77 while (str[index] !=
'\0' &&
size < N) {
86 constexpr void append(
char c,
size_t count) {
87 for (
size_t i = 0; i < count &&
size < N; i++) {
91 constexpr void append(std::string_view sv) {
92 for (
size_t i = 0; i < sv.size() &&
size < N; i++) {
97 constexpr std::string_view
view()
const {
98 return std::string_view(
data.data(),
size);
101 constexpr operator std::string_view()
const {
109 constexpr std::optional<size_t>
find(
char c,
size_t start = 0)
const {
110 for (
size_t i = start; i <
size; i++) {
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]) {
139 return view().substr(pos, next_whitespace_position - pos);
160 constexpr std::string_view
view()
const {
161 return (
data &&
len) ? std::string_view(
data,
len) : std::string_view{};
164 constexpr operator std::string_view()
const {
return view(); }
168 if (pos >= sv.size())
return {};
170 while (end < sv.size()) {
172 if (ch ==
' ' || ch ==
'\t' || ch ==
'\n')
break;
175 return sv.substr(pos, end - pos);
204 Helpers::FixedString LongOpt,
205 Helpers::FixedString Description,
207 Helpers::FixedString ArgumentPlaceholder =
"arg">
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>;
243template<option_like T>
264 if (option.longopt.length() > 0) {
267 length += 2 + option.longopt.length();
270 switch (option.argRequirement) {
273 + option.argumentPlaceholder.length() + 1;
277 if (option.longopt.length() > 0) {
280 length += option.argumentPlaceholder.length() + 1;
298template<
size_t N, option_like T>
300 size_t max_length = 0;
301 for (
size_t i = 0; i < N; i++) {
303 if (length > max_length) {
318template<
size_t N, option_like T>
320 size_t total_length = 0;
323 for (
size_t i = 0; i < N; i++) {
324 size_t line_length = 2;
327 const size_t padding_amount = max_label_length - label_length;
329 total_length += label_length + padding_amount + 1;
330 line_length += label_length + padding_amount + 1;
332 const size_t line_limit = 80;
333 size_t pos = line_length;
335 auto desc = options[i].description.view();
338 while (idx < desc.size()) {
341 if (idx >= desc.size())
break;
345 if (desc[idx] ==
'\n') {
347 total_length += max_label_length + 3;
348 pos = max_label_length + 3;
355 while (end < desc.size() && !
is_ws(desc[end])) ++end;
357 const size_t word_len = end - idx;
360 const size_t desc_col = max_label_length + 3;
361 if (pos + word_len > line_limit && pos > desc_col) {
363 total_length += desc_col;
367 total_length += word_len;
372 while (next < desc.size() &&
is_ws(desc[next])) ++next;
373 if (next < desc.size()) {
375 if (pos + 1 > line_limit) {
377 total_length += desc_col;
391 return total_length + 1;
419 return argument.value_or(std::string_view{});
492 return opt.getShortOpt() == shortopt;
550 static constexpr size_t N =
sizeof...(Options);
558 Options::argRequirement,
565 for (
size_t i = 0; i <
N; i++) {
566 for (
size_t j = i + 1; j <
N; j++) {
573 }(),
"OptionParser error: Duplicate shortopt values detected in option definitions.");
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;
587 }(),
"OptionParser error: Duplicate longopt values detected in option definitions.");
592 size_t short_opt_index = 0;
593 std::array<char, (3*
N) + 2> short_opts{};
600 short_opts[short_opt_index++] =
'+';
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++] =
':';
610 short_opts[short_opt_index++] =
':';
611 short_opts[short_opt_index++] =
':';
622 short_opts[short_opt_index] =
'\0';
627 std::array<struct option, N + 1> long_opts{};
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;
641 long_opts[idx] = {
nullptr, 0,
nullptr, 0};
650 const size_t max_label_length = Helpers::max_option_label_length<N>(
options);
652 for (
size_t i = 0; i <
N; i++) {
659 if (
options[i].longopt.length() > 0) {
667 if (
options[i].longopt.length() > 0) {
672 switch (
options[i].argRequirement) {
682 if (
options[i].longopt.length() > 0) {
695 const size_t label_length = option_label_length(
options[i]);
696 const size_t padding_amount = max_label_length - label_length;
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;
703 auto desc =
options[i].description.view();
706 while (idx < desc.size()) {
708 if (idx >= desc.size())
break;
712 if (desc[idx] ==
'\n') {
723 std::string_view word = desc.substr(idx, end - idx);
725 if (pos + word.size() > line_limit && pos > desc_col) {
736 if (next < desc.size()) {
737 if (pos + 1 > line_limit) {
759 template <StopCondition parseUntil>
760 std::pair<OptionSequence, OptionRemainder>
parse_impl(
int argc,
char* argv[])
const {
779 #if defined(__GLIBC__) || defined(__HAIKU__)
781 #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__MUSL__)
785 int remainder_start = -1;
787 auto find_by_short = [&](
int s) ->
const auto* {
788 for (
size_t i = 0; i <
N; i++) {
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())) {
804 auto token_to_long_name = [&](
const char* tok) -> std::string_view {
806 std::string_view sv(tok);
807 if (!sv.starts_with(
"--"))
return {};
809 if (
auto eq = sv.find(
'='); eq != std::string_view::npos) {
810 sv = sv.substr(0, eq);
818 const int token_index = (optind == 0) ? 1 : optind;
821 if (optind >= argc)
break;
828 if (optind > 0 && std::string_view(argv[optind - 1]) ==
"--") {
830 remainder_start = token_index;
836 remainder_start = optind;
840 for (
int i = optind; i < argc; i++) {
849 remainder_start = token_index;
854 remainder_start = optind;
868 remainder_start = token_index;
873 const char* culprit_tok =
874 (token_index >= 0 && token_index < argc) ? argv[token_index] :
nullptr;
878 const auto* o = find_by_short(optopt);
880 std::string option_name;
881 if (optopt >= 33 && optopt <= 126) {
883 option_name +=
static_cast<char>(optopt);
885 option_name = std::string(o->longopt.length() > 0
889 throw std::runtime_error(std::string(
"Missing required argument for option: ")
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);
899 throw std::runtime_error(std::string(
"Missing required argument for option: --")
900 + std::string(long_name));
906 throw std::runtime_error(std::string(
"Unknown option: ") + culprit_tok);
910 throw std::runtime_error(
"Unknown option");
913 std::optional<std::string_view> argument;
914 if (optarg !=
nullptr) argument = std::string_view(optarg);
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};
945 return parse_impl<AllOptions>(argc, argv).first;
959 template<StopCondition end>
960 std::pair<OptionSequence, OptionRemainder>
parse_until(
int argc,
char* argv[])
const {
961 return parse_impl<end>(argc, argv);
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
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
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
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
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