Lector v1.0.0
C++ library for parsing command line arguments.
arguments.hpp
1// Copyright © 2026, Alexandre Coderre-Chabot.
2
3// This file is part of Lector (https://github.com/acodcha/lector), a C++ library for parsing
4// command line arguments. Lector is licensed under the MIT License (https://mit-license.org).
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
7// associated documentation files (the "Software"), to deal in the Software without restriction,
8// including without limitation the rights to use, copy, modify, merge, publish, distribute,
9// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11// - The above copyright notice and this permission notice shall be included in all copies or
12// substantial portions of the Software.
13// - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
14// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM
17// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
18
19#ifndef LECTOR_ARGUMENTS_HPP
20#define LECTOR_ARGUMENTS_HPP
21
22#include <algorithm>
23#include <array>
24#include <cstddef>
25#include <cstdint>
26#include <filesystem>
27#include <initializer_list>
28#include <iostream>
29#include <limits>
30#include <optional>
31#include <sstream>
32#include <stdexcept>
33#include <string>
34#include <string_view>
35#include <type_traits>
36#include <unordered_set>
37#include <utility>
38#include <vector>
39
40#include "lector/parse.hpp"
41#include "lector/print.hpp"
42#include "lector/text.hpp"
43
44/// @brief The Lector library's namespace.
45namespace lector {
46
47/// @brief Form of a command line argument.
48enum class Form : std::int8_t {
49 /// @brief Unknown, unspecified, or invalid command line argument form.
50 Unknown = 0,
51
52 /// @brief The command line argument is a positional argument; it does not define any keys and
53 /// must be specified in a specific order on the command line.
54 Positional = 1,
55
56 /// @brief The command line argument is a named argument; it defines one or more keys and is
57 /// specified on the command line by one of its keys. Named arguments can be specified in any
58 /// order on the command line.
59 Named = 2,
60};
61
62/// @brief Specialization of the lector::Names constant for the lector::Form enumeration.
63template <>
64inline constexpr std::array<lector::Name<lector::Form>, 3> Names<lector::Form>{
65 {
66 {lector::Form::Unknown, "Unknown"},
67 {lector::Form::Positional, "Positional"},
68 {lector::Form::Named, "Named"},
69 }
70};
71
72/// @brief Specialization of the lector::Spellings constant for the lector::Form enumeration.
73template <>
74inline constexpr std::array<lector::Spelling<lector::Form>, 9> Spellings<lector::Form>{
75 {
76 {"Unknown", lector::Form::Unknown},
77 {"Positional", lector::Form::Positional},
78 {"Named", lector::Form::Named},
79 {"unknown", lector::Form::Unknown},
80 {"positional", lector::Form::Positional},
81 {"named", lector::Form::Named},
82 {"UNKNOWN", lector::Form::Unknown},
83 {"POSITIONAL", lector::Form::Positional},
84 {"NAMED", lector::Form::Named},
85 }
86};
87
88/// @brief Importance of a command line argument.
89enum class Importance : std::int8_t {
90 /// @brief Unknown, unspecified, or invalid command line argument importance.
91 Unknown = 0,
92
93 /// @brief The command line argument is optional; it may or may not be provided by the user.
94 Optional = 1,
95
96 /// @brief The command line argument is required; it must be provided by the user.
97 Required = 2,
98};
99
100/// @brief Specialization of the lector::Names constant for the lector::Importance enumeration.
101template <>
102inline constexpr std::array<lector::Name<lector::Importance>, 3> Names<lector::Importance>{
103 {
104 {lector::Importance::Unknown, "Unknown"},
105 {lector::Importance::Optional, "Optional"},
106 {lector::Importance::Required, "Required"},
107 }
108};
109
110/// @brief Specialization of the lector::Spellings constant for the lector::Importance enumeration.
111template <>
112inline constexpr std::array<lector::Spelling<lector::Importance>, 9> Spellings<lector::Importance>{
113 {
114 {"Unknown", lector::Importance::Unknown},
115 {"Optional", lector::Importance::Optional},
116 {"Required", lector::Importance::Required},
117 {"unknown", lector::Importance::Unknown},
118 {"optional", lector::Importance::Optional},
119 {"required", lector::Importance::Required},
120 {"UNKNOWN", lector::Importance::Unknown},
121 {"OPTIONAL", lector::Importance::Optional},
122 {"REQUIRED", lector::Importance::Required},
123 }
124};
125
126/// @brief A command line argument, including its label, value type, keys, description, importance,
127/// default value, and parsed value.
128/// @tparam LabelValue Value of this command line argument's label. The label is used to uniquely
129/// identify this command line argument in a collection of command line arguments.
130/// @tparam Type The type of the value stored in this command line argument.
131template <auto LabelValue, typename Type>
132class Argument final {
133public:
134 using ValueType = Type;
135
136 /// @brief Default constructor. Initializes the command line argument with no keys, an empty
137 /// description, required importance, and no default value.
138 Argument() noexcept = default;
139
140 /// @brief Constructor for a required command line argument or a boolean command line argument. No
141 /// default value is needed. Boolean command line arguments are always optional and always default
142 /// to false.
143 /// @param[in] keys The keys that can be used to specify the command line argument.
144 /// @param[in] description The description of the command line argument.
145 /// @throws std::invalid_argument if any of the parameters are invalid.
146 Argument(const std::initializer_list<std::string>& keys, const std::string& description)
147 : keys_{keys}, description_{description},
148 importance_{
149 std::is_same_v<Type, bool> ? lector::Importance::Optional : lector::Importance::Required} {
150 set_boolean_default();
151 validate_keys();
152 validate_description();
153 }
154
155 /// @brief Constructor for an optional non-boolean command line argument. A default value must be
156 /// provided.
157 /// @param[in] keys The keys that can be used to specify the command line argument.
158 /// @param[in] description The description of the command line argument.
159 /// @param[in] default_value The default value of the command line argument.
160 /// @throws std::invalid_argument if any of the parameters are invalid.
161 Argument(const std::initializer_list<std::string>& keys, const std::string& description,
162 const Type& default_value)
163 : keys_{keys}, description_{description}, default_value_{default_value},
164 importance_{lector::Importance::Optional} {
165 validate_keys();
166 validate_description();
167 validate_default_value();
168 }
169
170 /// @brief Destructor. Destroys this command line argument.
171 ~Argument() noexcept = default;
172
173 /// @brief Copy constructor. Constructs a command line argument by copying another one.
174 Argument(const lector::Argument<LabelValue, Type>&) = default;
175
176 /// @brief Copy assignment operator. Assigns this command line argument by copying another one.
177 /// @return This command line argument after the assignment.
178 lector::Argument<LabelValue, Type>& operator=(
179 const lector::Argument<LabelValue, Type>&) = default;
180
181 /// @brief Move constructor. Constructs a command line argument by moving another one.
182 Argument(lector::Argument<LabelValue, Type>&&) noexcept = default;
183
184 /// @brief Move assignment operator. Assigns this command line argument by moving another one.
185 /// @return This command line argument after the assignment.
186 lector::Argument<LabelValue, Type>& operator=(
187 lector::Argument<LabelValue, Type>&&) noexcept = default;
188
189 /// @brief Label of this command line argument. Used to uniquely identify this command line
190 /// argument. Set at construction.
191 /// @return The label of this command line argument.
192 [[nodiscard]] static constexpr auto label() noexcept {
193 return LabelValue;
194 }
195
196 /// @brief Keys that can be used to specify this command line argument. Set at construction.
197 /// @return The keys that can be used to specify this command line argument.
198 [[nodiscard]] const std::vector<std::string>& keys() const noexcept {
199 return keys_;
200 }
201
202 /// @brief Description of this command line argument. Set at construction.
203 /// @return The description of this command line argument.
204 [[nodiscard]] std::string_view description() const noexcept {
205 return description_;
206 }
207
208 /// @brief Default value of this command line argument. Only relevant for optional non-boolean
209 /// arguments. Set at construction.
210 /// @return The default value of this command line argument.
211 [[nodiscard]] const std::optional<Type>& default_value() const noexcept {
212 return default_value_;
213 }
214
215 /// @brief Parsed value of this command line argument. Set when this command line argument is
216 /// parsed.
217 /// @return The parsed value of this command line argument.
218 [[nodiscard]] const std::optional<Type>& parsed_value() const noexcept {
219 return parsed_value_;
220 }
221
222 /// @brief Importance of this command line argument. Required arguments must be provided by the
223 /// user, while optional arguments may or may not be provided by the user. Set at construction.
224 /// @return The importance of this command line argument.
225 [[nodiscard]] lector::Importance importance() const noexcept {
226 return importance_;
227 }
228
229 /// @brief Value of this command line argument. Returns the parsed value if it exists; otherwise,
230 /// returns the default value.
231 /// @return The value of this command line argument.
232 /// @throws std::logic_error if this command line argument is missing both its parsed value and
233 /// its default value.
234 [[nodiscard]] const Type& parsed_or_default_value() const {
235 if (parsed_value_.has_value()) {
236 return parsed_value_.value();
237 }
238 if (default_value_.has_value()) {
239 return default_value_.value();
240 }
241 throw std::logic_error(
242 "No parsed or default value for argument '" + longest_key_with_value_type() + "'.");
243 }
244
245 /// @brief Sets the parsed value of this command line argument.
246 /// @param[in] value The parsed value to set.
247 void set_parsed_value(const Type& value) {
248 parsed_value_ = value;
249 }
250
251 /// @brief Prints the longest key of this command line argument with its associated value type as
252 /// a string.
253 /// @return The string that contains the longest key and its associated value type.
254 [[nodiscard]] std::string longest_key_with_value_type() const {
255 std::string result{longest_key()};
256 const std::string type{value_type()};
257 if (!type.empty()) {
258 result.push_back(' ');
259 result.append(type);
260 }
261 return result;
262 }
263
264 /// @brief Prints the keys and value type of this command line argument as a string of text.
265 /// @return The string of text that contains the keys and value type of this command line
266 /// argument.
267 [[nodiscard]] std::string keys_with_value_type() const {
268 std::string result;
269 for (std::size_t index{0UL}; index < keys_.size(); ++index) {
270 const std::string type{value_type()};
271 result.append(keys_.at(index));
272 if (!type.empty()) {
273 result.push_back(' ');
274 result.append(type);
275 }
276 if (index + 1 < keys_.size()) {
277 result.append(", ");
278 }
279 }
280 return result;
281 }
282
283 /// @brief Prints the usage information of this command line argument as a string of text. The
284 /// usage information consists of this command line argument's longest key and value type,
285 /// enclosed in square braces if this command line argument is optional.
286 /// @return The string of text that contains the usage information of this command line argument.
287 [[nodiscard]] std::string usage() const {
288 if (keys_.empty()) {
289 return std::string{};
290 }
292 return "[" + longest_key_with_value_type() + "]";
293 }
295 }
296
297 /// @brief Prints the options information of this command line argument as a string of text. The
298 /// options information consist of this command line argument's keys, value type, and description.
299 /// @return The string of text that contains the options information of this command line
300 /// argument.
301 [[nodiscard]] std::string options() const {
302 if (keys_.empty() && description_.empty()) {
303 return std::string{};
304 }
305 return keys_with_value_type() + " " + description_;
306 }
307
308 /// @brief Prints the execution of this command line argument as a string of text. The execution
309 /// consists of this command line argument's longest key and parsed value, if any.
310 /// @return The string of text that contains the execution of this command line argument.
311 [[nodiscard]] std::string execution() const {
312 if constexpr (std::is_same_v<Type, bool>) {
313 if (parsed_value_.has_value() && parsed_value_.value()) {
314 return longest_key();
315 }
316 return "";
317 } else {
318 if (parsed_value_.has_value()) {
319 return longest_key() + " " + lector::print<Type>(parsed_value_.value());
320 }
321 return "";
322 }
323 }
324
325private:
326 /// @brief If this command line argument is a boolean argument, sets its default value to false.
327 /// Called by the constructor that does not specify a default value. Boolean arguments are always
328 /// optional and always default to false.
329 void set_boolean_default() {
330 if constexpr (std::is_same_v<Type, bool>) {
331 default_value_ = false;
332 }
333 }
334
335 /// @brief Validates the keys of this command line argument. Called by both constructors.
336 /// @throws std::logic_error if this command line argument has no keys or if any of its keys are
337 /// invalid.
338 void validate_keys() const {
339 if (keys_.empty()) {
340 throw std::logic_error("All arguments must each have at least one key.");
341 }
342 for (const std::string& key : keys_) {
343 if (key.empty()) {
344 if (longest_key().empty()) {
345 throw std::logic_error("Arguments cannot have empty keys.");
346 }
347 throw std::logic_error("Empty key in argument '" + longest_key_with_value_type()
348 + "'. Arguments cannot have empty keys.");
349 }
350 }
351 const std::size_t keys_size{keys_.size()};
352 for (std::size_t first{0UL}; first < keys_size; ++first) {
353 for (std::size_t second{first + static_cast<std::size_t>(1UL)}; second < keys_size;
354 ++second) {
355 if (keys_[first] == keys_[second]) {
356 throw std::logic_error(
357 "Duplicated key '" + keys_[first] + "' in argument '" + longest_key_with_value_type()
358 + "'. Arguments cannot have duplicate keys.");
359 }
360 }
361 }
362 };
363
364 /// @brief Validates the description of this command line argument. Called by both constructors.
365 /// @throws std::logic_error if this command line argument's description is empty.
366 void validate_description() const {
367 if (description_.empty()) {
368 throw std::logic_error("Empty description in argument '" + longest_key_with_value_type()
369 + "'. All arguments must have descriptions.");
370 }
371 };
372
373 /// @brief Validates the default value of this command line argument. Called by the constructor
374 /// that specifies a default value.
375 /// @throws std::logic_error if this command line argument is boolean but specifies a default
376 /// value.
377 constexpr void validate_default_value() const {
378 if constexpr (std::is_same_v<Type, bool>) {
379 throw std::logic_error(
380 "Specified default value for boolean argument '" + longest_key_with_value_type()
381 + "'. Boolean arguments are always false by default and cannot specify default values.");
382 }
383 }
384
385 /// @brief Prints the value type of this command line argument as a string of text.
386 /// @return The string of text that contains the value type.
387 [[nodiscard]] constexpr std::string_view value_type() const {
388 if constexpr (std::is_same_v<Type, bool>) {
389 return "";
390 } else if constexpr (std::numeric_limits<Type>::is_integer) {
391 return "<number>";
392 } else if constexpr (std::is_floating_point_v<Type>) {
393 return "<value>";
394 } else if constexpr (
395 std::is_same_v<Type, std::string> || std::is_same_v<Type, std::string_view>) {
396 return "<text>";
397 } else if constexpr (std::is_same_v<Type, std::filesystem::path>) {
398 return "<path>";
399 } else {
400 return "<value>";
401 }
402 }
403
404 /// @brief Returns the longest key of this command line argument.
405 /// @return The longest key of this command line argument.
406 /// @throws std::logic_error if this command line argument has no keys.
407 [[nodiscard]] const std::string& longest_key() const {
408 if (keys_.empty()) {
409 throw std::logic_error("All arguments must each have at least one key.");
410 }
411 std::size_t longest_key_index{0UL};
412 for (std::size_t index{1UL}; index < keys_.size(); ++index) {
413 if (keys_[index].size() > keys_[longest_key_index].size()) {
414 longest_key_index = index;
415 }
416 }
417 return keys_[longest_key_index];
418 }
419
420 /// @brief Keys that can be used to specify this command line argument. Set at construction.
421 std::vector<std::string> keys_;
422
423 /// @brief Description of this command line argument. Set at construction.
424 std::string description_;
425
426 /// @brief Default value of this command line argument. Only relevant for optional non-boolean
427 /// arguments. Set at construction.
428 std::optional<Type> default_value_;
429
430 /// @brief Parsed value of this command line argument. Set when this command line argument is
431 /// parsed.
432 std::optional<Type> parsed_value_;
433
434 /// @brief Importance of this command line argument. Required arguments must be provided by the
435 /// user, while optional arguments may or may not be provided by the user. Set at construction.
437};
438
439/// @brief Configuration of the help information of a collection of command line arguments.
440struct Configuration final {
441public:
442 /// @brief Title of the application whose command line arguments are to be parsed. When the
443 /// collection of command line arguments' help information is printed, this title appears first,
444 /// before its usage information. Optional and empty by default, in which case no title is
445 /// printed.
446 std::optional<std::string> title{std::nullopt};
447
448 /// @brief Description of the application whose command line arguments are to be parsed. When the
449 /// collection of command line arguments' help information is printed, this description appears
450 /// between its usage information and its options information. Optional and empty by default, in
451 /// which case no description is printed.
452 std::optional<std::string> description{std::nullopt};
453
454 /// @brief Additional notes pertaining to the application whose command line arguments are to be
455 /// parsed. When the collection of command line arguments' help information is printed, these
456 /// notes appear last, after its options information. Optional and empty by default, in which case
457 /// no notes are printed.
458 std::optional<std::string> notes{std::nullopt};
459};
460
461/// @brief Type trait used to extract a command line argument from a collection of command line
462/// arguments, using only its Label.
463/// @tparam Label The label of the command line argument to extract.
464/// @tparam ...ArgumentTypes The variadic list of argument types in the collection of command line
465/// arguments.
466template <auto Label, typename... ArgumentTypes>
467struct FindArgumentByLabel;
468
469/// @brief Type trait specialization used to extract a command line argument from a collection of
470/// command line arguments, using its Label, its type, and the types of the remaining command line
471/// arguments in the collection.
472/// @tparam Label The label of the command line argument to extract.
473/// @tparam Type The type of the command line argument to extract.
474/// @tparam ...OtherArgumentTypes The variadic list of argument types in the collection of command
475/// line arguments, excluding the command line argument to extract.
476template <auto Label, typename Type, typename... OtherArgumentTypes>
477struct FindArgumentByLabel<Label, lector::Argument<Label, Type>, OtherArgumentTypes...> {
479};
480
481/// @brief Type trait specialization used to extract a command line argument from a collection of
482/// command line arguments, using its Label and the types of the remaining command line arguments in
483/// the collection.
484/// @tparam Label The label of the command line argument to extract.
485/// @tparam OtherLabel The label of the command line argument to compare against.
486/// @tparam OtherType The type of the command line argument to compare against.
487/// @tparam ...RemainingArgumentTypes The variadic list of argument types in the collection of
488/// command line arguments, excluding the command line argument to extract.
489template <auto Label, auto OtherLabel, typename OtherType, typename... RemainingArgumentTypes>
490struct FindArgumentByLabel<Label, lector::Argument<OtherLabel, OtherType>,
491 RemainingArgumentTypes...>
492 final {
493 using type = typename lector::FindArgumentByLabel<Label, RemainingArgumentTypes...>::type;
494};
495
496/// @brief Data structure that validates at compilation time that a specified variadic list of types
497/// are unique. Base data structure that contains an empty list of types and returns true.
498/// @tparam ...Types Variadic list of types to check for uniqueness.
499template <auto... Types>
500struct AreUnique : std::true_type {};
501
502/// @brief Data structure that validates at compilation time that a specified variadic list of types
503/// are unique. Recursively compares a first type against the remaining variadic list of types.
504/// @tparam FirstType The first type to compare.
505/// @tparam ...RemainingTypes The remaining types in the variadic list of types to compare.
506template <auto FirstType, auto... RemainingTypes>
507struct AreUnique<FirstType, RemainingTypes...>
508 : std::bool_constant<((FirstType != RemainingTypes) && ...)
509 && AreUnique<RemainingTypes...>::value> {};
510
511/// @brief A collection of command line arguments that can be parsed from argc and argv.
512/// @tparam ...ArgumentTypes Variadic list of the types of the command line arguments in this
513/// collection.
514template <typename... ArgumentTypes>
515class Arguments final {
516public:
517 /// @brief Compile-time check that all arguments have unique labels.
518 static_assert(AreUnique<ArgumentTypes::label()...>::value,
519 "Duplicate argument labels detected. Each argument must have a unique label.");
520
521 /// @brief Constructor. Constructs a collection of command line arguments from a configuration
522 /// data structure and a variadic list of command line arguments.
523 /// @param[in] ...arguments The variadic list of command line arguments.
524 explicit Arguments(lector::Configuration&& configuration, ArgumentTypes... arguments)
525 : configuration_{std::move(configuration)}, arguments_{std::move(arguments)...} {
526 validate_keys();
527 }
528
529 /// @brief Constructor. Constructs a collection of command line arguments from a variadic list of
530 /// command line arguments.
531 /// @param[in] ...arguments The variadic list of command line arguments.
532 explicit Arguments(ArgumentTypes... arguments) : arguments_{std::move(arguments)...} {
533 validate_keys();
534 }
535
536 /// @brief Destructor. Destroys this collection of command line arguments.
537 ~Arguments() noexcept = default;
538
539 /// @brief Copy constructor. Constructs a collection of command line argument by copying another
540 /// one.
541 Arguments(const lector::Arguments<ArgumentTypes...>&) = default;
542
543 /// @brief Copy assignment operator. Assigns this collection of command line argument by copying
544 /// another one.
545 /// @return This collection of command line argument after the assignment.
546 lector::Arguments<ArgumentTypes...>& operator=(
547 const lector::Arguments<ArgumentTypes...>&) = default;
548
549 /// @brief Move constructor. Constructs a collection of command line argument by moving another
550 /// one.
551 Arguments(lector::Arguments<ArgumentTypes...>&&) noexcept = default;
552
553 /// @brief Move assignment operator. Assigns this collection of command line argument by moving
554 /// another one.
555 /// @return This collection of command line argument after the assignment.
556 lector::Arguments<ArgumentTypes...>& operator=(
557 lector::Arguments<ArgumentTypes...>&&) noexcept = default;
558
559 /// @brief Parses argc and argv to populate the parsed values of the command line arguments in
560 /// this collection.
561 /// @param[in] argc The number of command line arguments, including the executable path.
562 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
563 /// with the executable path.
564 /// @throws std::invalid_argument if an invalid, unknown, duplicated, or missing argument is
565 /// encountered.
566 void parse(const int argc, char* argv[]) {
567 parse_executable_path(argc, argv);
568 parse_arguments(argc, argv);
569 validate_all_required_arguments_have_parsed_values();
570 }
571
572 [[nodiscard]] const lector::Configuration& configuration() const {
573 return configuration_;
574 }
575
576 /// @brief Returns the executable path of this collection of command line arguments. If the
577 /// command line arguments have not yet been parsed from argc and argv, this path is empty.
578 /// @return The executable path of this collection of command line arguments.
579 [[nodiscard]] const std::filesystem::path& executable_path() const {
580 return executable_path_;
581 }
582
583 /// @brief Returns a specified command line argument from this collection.
584 /// @tparam Label The label of the command line argument to return.
585 /// @return The specified command line argument.
586 template <auto Label>
587 [[nodiscard]] const auto& get() const {
588 using Type = typename lector::FindArgumentByLabel<Label, ArgumentTypes...>::type;
589 return std::get<Type>(arguments_);
590 }
591
592 /// @brief Prints the usage information of this collection of command line arguments as a string
593 /// of text. The usage information consists of each argument's longest key and value type,
594 /// enclosed in square braces for optional command line arguments.
595 /// @return The string of text that contains the usage information of this collection of command
596 /// line arguments.
597 [[nodiscard]] std::string usage() const {
598 return usage(std::numeric_limits<std::size_t>::max());
599 }
600
601 /// @brief Prints the usage information of this collection of command line arguments as a string
602 /// of text. The usage information consists of each argument's longest key and value type,
603 /// enclosed in square braces for optional command line arguments. Lines are wrapped.
604 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
605 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
606 /// text contains very long words whose lengths exceed the desired line length.
607 /// @return The string of text that contains the usage information of this collection of command
608 /// line arguments.
609 /// @throws std::invalid_argument if the desired line length is zero.
610 [[nodiscard]] std::string usage(const std::size_t line_length) const {
611 validate_line_length(line_length);
612 std::string result;
613 result.append(executable_path_.filename().string());
614 std::apply(
615 [&](const auto&... argument) {
616 (..., [&]() {
617 result.push_back(' ');
618 result.append(argument.usage());
619 }());
620 },
621 arguments_);
622 return result;
623 }
624
625 /// @brief Prints the options information of this collection of command line argument as a string
626 /// of text. The options information consists of the list of each command line argument's keys,
627 /// value type, and description.
628 /// @return The string of text that contains the options information of this collection of command
629 /// line arguments.
630 [[nodiscard]] std::string options() const {
631 return options(std::numeric_limits<std::size_t>::max());
632 }
633
634 /// @brief Prints the options information of this collection of command line argument as a string
635 /// of text. The options information consists of the list of each command line argument's keys,
636 /// value type, and description. Lines are wrapped.
637 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
638 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
639 /// text contains very long words whose lengths exceed the desired line length.
640 /// @return The string of text that contains the options information of this collection of command
641 /// line arguments.
642 /// @throws std::invalid_argument if the desired line length is zero.
643 [[nodiscard]] std::string options(const std::size_t line_length) const {
644 validate_line_length(line_length);
645 // Compute the text formatting dimensions.
646 constexpr std::size_t gutter_width{2UL};
647 const std::size_t maximum_first_column_width{
648 (line_length - gutter_width) / static_cast<std::size_t>(2UL)};
649 const std::size_t maximum_length_of_keys_with_value_types_{
650 maximum_length_of_keys_with_value_type()};
651 const std::size_t first_column_width{
652 std::min(maximum_length_of_keys_with_value_types_, maximum_first_column_width)};
653 const std::size_t second_column_width{line_length - gutter_width - first_column_width};
654 // Obtain the number of command line arguments.
655 constexpr std::size_t argument_count{std::tuple_size_v<decltype(arguments_)>};
656 // Iterate through the command line arguments and update the resulting string of text.
657 std::string result;
658 std::size_t argument_index{0UL};
659 std::apply(
660 [&](const auto&... argument) {
661 (..., [&]() {
662 result.append(lector::combine_and_left_align(
663 argument.keys_with_value_type(), first_column_width, argument.description(),
664 second_column_width));
665 ++argument_index;
666 if (argument_index < argument_count) {
667 result.push_back('\n');
668 }
669 }());
670 },
671 arguments_);
672 return result;
673 }
674
675 /// @brief Prints the help information of this collection of command line arguments as a string of
676 /// text. The help information consists of this collection of command line arguments' title, usage
677 /// information, description, options information, and notes.
678 /// @return The string of text that contains the help information of this collection of command
679 /// line arguments.
680 [[nodiscard]] std::string help() const {
681 return help(std::numeric_limits<std::size_t>::max());
682 }
683
684 /// @brief Prints the help information of this collection of command line arguments as a string of
685 /// text. The help information consists of this collection of command line arguments' title, usage
686 /// information, description, options information, and notes. Lines are wrapped.
687 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
688 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
689 /// text contains very long words whose lengths exceed the desired line length.
690 /// @return The string of text that contains the help information of this collection of command
691 /// line arguments.
692 /// @throws std::invalid_argument if the desired line length is zero.
693 [[nodiscard]] std::string help(const std::size_t line_length) const {
694 validate_line_length(line_length);
695 std::string result;
696 if (configuration_.title.has_value() && !configuration_.title.value().empty()) {
697 result.append(lector::wrap_and_left_align(configuration_.title.value(), line_length));
698 }
699 const std::string usage_{lector::wrap_and_left_align(usage(), line_length)};
700 if (!usage_.empty()) {
701 if (!result.empty()) {
702 result.push_back('\n');
703 result.push_back('\n');
704 }
705 result.append("Usage:\n");
706 result.append(usage_);
707 }
708 if (configuration_.description.has_value() && !configuration_.description.value().empty()) {
709 if (!result.empty()) {
710 result.push_back('\n');
711 result.push_back('\n');
712 }
713 result.append(lector::wrap_and_left_align(configuration_.description.value(), line_length));
714 }
715 const std::string options_{options(line_length)};
716 if (!options_.empty()) {
717 if (!result.empty()) {
718 result.push_back('\n');
719 result.push_back('\n');
720 }
721 result.append("Options:\n");
722 result.append(options_);
723 }
724 if (configuration_.notes.has_value() && !configuration_.notes.value().empty()) {
725 if (!result.empty()) {
726 result.push_back('\n');
727 result.push_back('\n');
728 }
729 result.append(lector::wrap_and_left_align(configuration_.notes.value(), line_length));
730 }
731 return result;
732 }
733
734 /// @brief Prints the execution of this collection of command line argument as a string of text.
735 /// The execution consists of the executable path followed by each argument's longest key and
736 /// corresponding parsed value, if any.
737 /// @return The string of text that contains the execution of this collection of command line
738 /// argument.
739 [[nodiscard]] std::string execution() const {
740 return execution(std::numeric_limits<std::size_t>::max());
741 }
742
743 /// @brief Prints the execution of this collection of command line argument as a string of text.
744 /// The execution consists of the executable path followed by each argument's longest key and
745 /// corresponding parsed value, if any. Lines are wrapped.
746 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
747 /// than zero. The recommended value is 80. The actual line length may be longer if the string of
748 /// text contains very long words whose lengths exceed the desired line length.
749 /// @return The string of text that contains the execution of this collection of command line
750 /// argument.
751 /// @throws std::invalid_argument if the desired line length is zero.
752 [[nodiscard]] std::string execution(const std::size_t line_length) const {
753 validate_line_length(line_length);
754 std::string printed_execution_arguments;
755 std::apply(
756 [&](const auto&... argument) {
757 (..., [&]() {
758 const std::string argument_execution{argument.execution()};
759 if (!printed_execution_arguments.empty() && !argument_execution.empty()) {
760 printed_execution_arguments.push_back(' ');
761 }
762 printed_execution_arguments.append(argument_execution);
763 }());
764 },
765 arguments_);
766 if (printed_execution_arguments.empty()) {
767 return executable_path_.string();
768 }
770 executable_path_.string() + " " + printed_execution_arguments, line_length);
771 }
772
773private:
774 /// @brief Default length to use when wrapping lines when printing usage information, options
775 /// information, or help information as a string of text. The actual line length may be longer if
776 /// the string of text contains very long words whose lengths exceed the desired line length.
777 static constexpr std::size_t default_line_length_{80UL};
778
779 /// @brief The best matching argument for a command line argument token during parsing. The best
780 /// matching argument is the argument with the longest matching key, and if there are multiple
781 /// arguments with keys of the same length that match, then the best matching argument is the one
782 /// that is matched by a non-inline key rather than an inline key. Used to avoid shadowing when
783 /// multiple arguments have keys that are prefixes of each other, and to prefer non-inline matches
784 /// over inline matches when the key lengths are equal.
785 struct BestArgument final {
786 /// @brief Index of this argument in the tuple of arguments. Used to identify this argument
787 /// during parsing.
788 std::size_t index{0UL};
789
790 /// @brief Length of the longest matching key for this argument. Used to avoid shadowing when
791 /// multiple arguments have keys that are prefixes of each other. For example, if one argument
792 /// has the key "key" and another argument has the key "key_long", then the argument with the
793 /// key "key_long" should be matched for the command line argument "key_long=value", not the
794 /// argument with the key "key".
795 std::size_t key_length{0UL};
796
797 /// @brief Whether this argument was matched by an inline key of the form "key=value" rather
798 /// than a whitespace-separated key-value pair of the form "key value". Used to prefer
799 /// non-inline matches over inline matches when the key lengths are equal. For example, if one
800 /// argument has the key "key" and another argument has the key "key_long", then the argument
801 /// with the key "key" should be matched for the command line argument "key=value", not the
802 /// argument with the key "key_long".
803 bool is_inline{false};
804 };
805
806 /// @brief Validates that a specified line length is strictly greater than zero.
807 /// @param[in] line_length The desired line length to use when wrapping. Must be strictly greater
808 /// than zero.
809 /// @throws std::invalid_argument if the desired line length is zero.
810 static void validate_line_length(const std::size_t line_length) {
811 if (line_length <= static_cast<std::size_t>(0UL)) {
812 throw std::invalid_argument("Invalid line length. Must be strictly greater than zero.");
813 }
814 }
815
816 /// @brief Parses the executable path from argc and argv. Called by the lector::Arguments::parse
817 /// method.
818 /// @param[in] argc The number of command line arguments, including the executable path.
819 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
820 /// with the executable path.
821 void parse_executable_path(const int argc, char* argv[]) {
822 if (argc > 0) {
823 executable_path_ = argv[0];
824 }
825 }
826
827 /// @brief Parses the command line arguments from argc and argv, except for the executable path.
828 /// Starts at the second argument in argv. Called by the lector::Arguments::parse method.
829 /// @param[in] argc The number of command line arguments, including the executable path.
830 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
831 /// with the executable path.
832 /// @throws std::invalid_argument if an invalid, unknown, duplicated, or missing argument is
833 /// encountered.
834 void parse_arguments(const int argc, char* argv[]) {
835 const std::size_t count{static_cast<std::size_t>(argc)};
836 for (std::size_t argv_index{1UL}; argv_index < count; ++argv_index) {
837 const std::string_view token{argv[argv_index]};
838 const BestArgument best_argument{find_best_argument(token)};
839 std::size_t current_index{0UL};
840 std::apply(
841 [&](auto&... argument) {
842 (..., [&]() {
843 if (current_index == best_argument.index) {
844 populate_argument(argument, best_argument, argc, argv, argv_index);
845 }
846 ++current_index;
847 }());
848 },
849 arguments_);
850 }
851 }
852
853 /// @brief Finds the best matching argument for a command line argument token.
854 /// @param[in] token The command line argument token to match.
855 /// @return The best matching argument.
856 /// @throws std::invalid_argument if an unknown argument is encountered.
857 [[nodiscard]] BestArgument find_best_argument(const std::string_view token) const {
858 std::optional<BestArgument> best;
859 std::size_t argument_index{0UL};
860 std::apply(
861 [&](const auto&... argument) {
862 (..., [&]() {
863 for (const std::string& argument_key : argument.keys()) {
864 const std::optional<BestArgument> exact_match{
865 try_exact_match(token, argument_index, argument_key)};
866 if (exact_match.has_value()) {
867 if (!best.has_value() || exact_match->key_length > best->key_length
868 || (exact_match->key_length == best->key_length && best->is_inline)) {
869 best = exact_match;
870 }
871 continue;
872 }
873 const std::optional<BestArgument> inline_match{
874 try_inline_match<decltype(argument)>(token, argument_index, argument_key)};
875 if (inline_match.has_value()
876 && (!best.has_value() || inline_match->key_length > best->key_length)) {
877 best = inline_match;
878 }
879 }
880 ++argument_index;
881 }());
882 },
883 arguments_);
884 if (!best.has_value()) {
885 throw std::invalid_argument("Unknown argument '" + std::string{token} + "'.");
886 }
887 return best.value();
888 }
889
890 /// @brief Checks whether a token is the exact match of an argument's key. Called by
891 /// lector::Arguments::find_best_argument().
892 /// @param[in] token The token to check.
893 /// @param[in] argument_index The index of the argument that has the key.
894 /// @param[in] argument_key The argument key against which to compare.
895 /// @return A populated lector::Arguments::BestArgument data structure if the specified token is
896 /// an exact match for the key, or std::nullopt otherwise.
897 [[nodiscard]] static std::optional<BestArgument> try_exact_match(
898 const std::string_view token, const std::size_t argument_index,
899 const std::string_view argument_key) noexcept {
900 if (token == argument_key) {
901 return BestArgument{argument_index, argument_key.size(), false};
902 }
903 return std::nullopt;
904 }
905
906 /// @brief Checks whether a token contains an inline match of an argument's key of the form
907 /// "key=value". Called by lector::Arguments::find_best_argument().
908 /// @tparam Argument The type of the argument that has the key to be used in the comparison.
909 /// @param[in] token The token to check.
910 /// @param[in] argument_index The index of the argument that has the key to be used in the
911 /// comparison.
912 /// @param[in] argument_key The argument key against which to compare.
913 /// @return A populated lector::Arguments::BestArgument data structure if the specified token
914 /// contains an inline match for the key, or std::nullopt otherwise.
915 template <typename Argument>
916 [[nodiscard]] static std::optional<BestArgument> try_inline_match(
917 const std::string_view token, const std::size_t argument_index,
918 const std::string_view argument_key) noexcept {
919 using ArgumentType = typename std::decay_t<Argument>::ValueType;
920 // Inline matching is strictly disabled for boolean arguments because they are key-only flags
921 // that do not have values.
922 if constexpr (!std::is_same_v<ArgumentType, bool>) {
923 if (token.size() > argument_key.size()
924 && token.compare(0, argument_key.size(), argument_key) == 0
925 && token[argument_key.size()] == '=') {
926 return BestArgument{argument_index, argument_key.size(), true};
927 }
928 }
929 return std::nullopt;
930 }
931
932 /// @brief Populates an argument with its parsed value. Called by
933 /// lector::Arguments::parse_arguments().
934 /// @tparam Argument The type of the argument to be populated.
935 /// @param[in,out] argument The argument to be populated.
936 /// @param[in] best_argument The lector::Arguments::BestArgument data structure that corresponds
937 /// to the argument to be populated.
938 /// @param[in] argc The number of command line arguments, including the executable path.
939 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
940 /// with the executable path.
941 /// @param[in,out] argv_index The index of the argument in the array of C-string command line
942 /// arguments whose value is to be extracted and used to populate the argument.
943 /// @throws std::invalid_argument if the parsed value is invalid for this argument type.
944 template <typename Argument>
945 static void populate_argument(Argument& argument, const BestArgument& best_argument,
946 const int argc, char* argv[], std::size_t& argv_index) {
947 if (argument.parsed_value().has_value()) {
948 throw std::invalid_argument(
949 "Duplicated argument '" + argument.longest_key_with_value_type() + "'.");
950 }
951 using Type = typename std::decay_t<Argument>::ValueType;
952 if constexpr (std::is_same_v<Type, bool>) {
953 // Boolean arguments are key-only flags; their presence implies true.
954 argument.set_parsed_value(true);
955 } else {
956 // This is a non-boolean argument. Extract and parse its value.
957 const std::string raw_value{extract_raw_value(
958 best_argument, argument.longest_key_with_value_type(), argc, argv, argv_index)};
959 const std::optional<Type> parsed_value{lector::parse<Type>(raw_value)};
960 if (parsed_value.has_value()) {
961 argument.set_parsed_value(parsed_value.value());
962 } else {
963 throw std::invalid_argument("Invalid value '" + raw_value + "' for argument '"
964 + argument.longest_key_with_value_type() + "'.");
965 }
966 }
967 }
968
969 /// @brief Extracts the raw string value from an argv token for a non-boolean best argument.
970 /// Called by lector::Arguments::populate_argument().
971 /// @param[in] best_argument The best argument.
972 /// @param[in] best_argument_longest_key_with_value_type The longest key with value type of the
973 /// best argument.
974 /// @param[in] argc The number of command line arguments, including the executable path.
975 /// @param[in] argv The array of C-strings that represents the command line arguments, starting
976 /// with the executable path.
977 /// @param[in,out] argv_index The index of the argument in the array of C-string command line
978 /// arguments whose raw string value is to be extracted.
979 /// @return The extracted raw string value.
980 /// @throws std::invalid_argument if the command line arguments are missing the value for this
981 /// best argument.
982 [[nodiscard]] static std::string extract_raw_value(
983 const BestArgument& best_argument,
984 const std::string& best_argument_longest_key_with_value_type, const int argc, char* argv[],
985 std::size_t& argv_index) {
986 const std::string_view token{argv[argv_index]};
987 if (best_argument.is_inline) {
988 // The token contains an inline value of the form "key=value". Extract the last portion of the
989 // token.
990 return std::string{token.substr(best_argument.key_length + 1)};
991 }
992 if (argv_index + 1 < static_cast<std::size_t>(argc)) {
993 // The token is a standalone value of the form "key", and the next token is of the form
994 // "value". Advance the argv index to consume the next argv element that contains the value.
995 ++argv_index;
996 return std::string{argv[argv_index]};
997 }
998 throw std::invalid_argument(
999 "Missing value for argument '" + best_argument_longest_key_with_value_type + "'.");
1000 }
1001
1002 /// @brief Validates that the same key is never duplicated across two or more arguments. Called by
1003 /// the constructor.
1004 /// @throws std::logic_error if the same key is duplicated across two or more arguments.
1005 void validate_keys() const {
1006 std::unordered_set<std::string> unique_keys;
1007 std::apply(
1008 [&](const auto&... argument) {
1009 (..., [&]() {
1010 for (const std::string& key : argument.keys()) {
1011 const std::pair<std::unordered_set<std::string>::const_iterator, bool> result{
1012 unique_keys.insert(key)};
1013 if (!result.second) {
1014 throw std::logic_error("Duplicate key '" + key + "' across two arguments.");
1015 }
1016 }
1017 }());
1018 },
1019 arguments_);
1020 }
1021
1022 /// @brief Validates that all required arguments have each successfully parsed a value from argc
1023 /// and argv. Called by the lector::Arguments::parse method.
1024 /// @throws std::invalid_argument if one or more required arguments are missing a parsed value.
1025 void validate_all_required_arguments_have_parsed_values() const {
1026 std::apply(
1027 [&](const auto&... argument) {
1028 (..., [&]() {
1029 if (argument.importance() == lector::Importance::Required
1030 && !argument.parsed_value().has_value()) {
1031 throw std::invalid_argument(
1032 "Missing required argument '" + argument.longest_key_with_value_type() + "'.");
1033 }
1034 }());
1035 },
1036 arguments_);
1037 }
1038
1039 /// @brief Computes and returns the maximum length of the printed keys and value type across all
1040 /// arguments in this collection of command line arguments.
1041 /// @return The maximum length of the printed keys and value type across all arguments in this
1042 /// collection.
1043 [[nodiscard]] std::size_t maximum_length_of_keys_with_value_type() const {
1044 std::size_t maximum_length{0UL};
1045 std::apply(
1046 [&](const auto&... argument) {
1047 (..., [&]() {
1048 const std::size_t length{lector::code_points(argument.keys_with_value_type())};
1049 maximum_length = std::max(maximum_length, length);
1050 }());
1051 },
1052 arguments_);
1053 return maximum_length;
1054 }
1055
1056 /// @brief Configuration of the help information of this collection of command line arguments.
1057 lector::Configuration configuration_{};
1058
1059 /// @brief Variadic collection of command line arguments.
1060 std::tuple<ArgumentTypes...> arguments_;
1061
1062 /// @brief Executable path of this collection of command line arguments. If the command line
1063 /// arguments have not yet been parsed from argc and argv, this path is empty.
1064 std::filesystem::path executable_path_;
1065};
1066
1067} // namespace lector
1068
1069#endif // LECTOR_ARGUMENTS_HPP
A command line argument, including its label, value type, keys, description, importance,...
Definition arguments.hpp:132
const std::optional< Type > & default_value() const noexcept
Default value of this command line argument. Only relevant for optional non-boolean arguments....
Definition arguments.hpp:211
const std::vector< std::string > & keys() const noexcept
Keys that can be used to specify this command line argument. Set at construction.
Definition arguments.hpp:198
std::string_view description() const noexcept
Description of this command line argument. Set at construction.
Definition arguments.hpp:204
void set_parsed_value(const Type &value)
Sets the parsed value of this command line argument.
Definition arguments.hpp:247
lector::Importance importance() const noexcept
Importance of this command line argument. Required arguments must be provided by the user,...
Definition arguments.hpp:225
std::string usage() const
Prints the usage information of this command line argument as a string of text. The usage information...
Definition arguments.hpp:287
~Argument() noexcept=default
Destructor. Destroys this command line argument.
std::string options() const
Prints the options information of this command line argument as a string of text. The options informa...
Definition arguments.hpp:301
const std::optional< Type > & parsed_value() const noexcept
Parsed value of this command line argument. Set when this command line argument is parsed.
Definition arguments.hpp:218
std::string longest_key_with_value_type() const
Prints the longest key of this command line argument with its associated value type as a string.
Definition arguments.hpp:254
Argument(const std::initializer_list< std::string > &keys, const std::string &description, const Type &default_value)
Constructor for an optional non-boolean command line argument. A default value must be provided.
Definition arguments.hpp:161
static constexpr auto label() noexcept
Label of this command line argument. Used to uniquely identify this command line argument....
Definition arguments.hpp:192
std::string execution() const
Prints the execution of this command line argument as a string of text. The execution consists of thi...
Definition arguments.hpp:311
std::string keys_with_value_type() const
Prints the keys and value type of this command line argument as a string of text.
Definition arguments.hpp:267
Argument() noexcept=default
Default constructor. Initializes the command line argument with no keys, an empty description,...
const Type & parsed_or_default_value() const
Value of this command line argument. Returns the parsed value if it exists; otherwise,...
Definition arguments.hpp:234
A collection of command line arguments that can be parsed from argc and argv.
Definition arguments.hpp:515
std::string usage(const std::size_t line_length) const
Prints the usage information of this collection of command line arguments as a string of text....
Definition arguments.hpp:610
std::string options() const
Prints the options information of this collection of command line argument as a string of text....
Definition arguments.hpp:630
const std::filesystem::path & executable_path() const
Returns the executable path of this collection of command line arguments. If the command line argumen...
Definition arguments.hpp:579
std::string execution(const std::size_t line_length) const
Prints the execution of this collection of command line argument as a string of text....
Definition arguments.hpp:752
Arguments(ArgumentTypes... arguments)
Constructor. Constructs a collection of command line arguments from a variadic list of command line a...
Definition arguments.hpp:532
std::string help() const
Prints the help information of this collection of command line arguments as a string of text....
Definition arguments.hpp:680
std::string execution() const
Prints the execution of this collection of command line argument as a string of text....
Definition arguments.hpp:739
~Arguments() noexcept=default
Destructor. Destroys this collection of command line arguments.
const auto & get() const
Returns a specified command line argument from this collection.
Definition arguments.hpp:587
void parse(const int argc, char *argv[])
Parses argc and argv to populate the parsed values of the command line arguments in this collection.
Definition arguments.hpp:566
std::string usage() const
Prints the usage information of this collection of command line arguments as a string of text....
Definition arguments.hpp:597
std::string help(const std::size_t line_length) const
Prints the help information of this collection of command line arguments as a string of text....
Definition arguments.hpp:693
Arguments(lector::Configuration &&configuration, ArgumentTypes... arguments)
Compile-time check that all arguments have unique labels.
Definition arguments.hpp:524
std::string options(const std::size_t line_length) const
Prints the options information of this collection of command line argument as a string of text....
Definition arguments.hpp:643
The Lector library's namespace.
Definition arguments.hpp:45
std::string combine_and_left_align(const std::string_view first_column_text, const std::size_t first_column_width, const std::string_view second_column_text, const std::size_t second_column_width)
Combines two strings of text, each representing a column, into a single vector of strings that contai...
Definition text.hpp:311
std::string wrap_and_left_align(const std::string_view text, const std::size_t line_length)
Left-aligns and wraps a string of text to a line length.
Definition text.hpp:296
std::size_t code_points(const std::string_view text)
Counts and returns the number of UTF-8 code points in a string of text. The number of UTF-8 code poin...
Definition text.hpp:69
Form
Form of a command line argument.
Definition arguments.hpp:48
@ Named
The command line argument is a named argument; it defines one or more keys and is specified on the co...
@ Unknown
Unknown, unspecified, or invalid command line argument form.
@ Positional
The command line argument is a positional argument; it does not define any keys and must be specified...
Importance
Importance of a command line argument.
Definition arguments.hpp:89
@ Unknown
Unknown, unspecified, or invalid command line argument importance.
@ Required
The command line argument is required; it must be provided by the user.
@ Optional
The command line argument is optional; it may or may not be provided by the user.
Configuration of the help information of a collection of command line arguments.
Definition arguments.hpp:440
std::optional< std::string > title
Title of the application whose command line arguments are to be parsed. When the collection of comman...
Definition arguments.hpp:446
std::optional< std::string > description
Description of the application whose command line arguments are to be parsed. When the collection of ...
Definition arguments.hpp:452
std::optional< std::string > notes
Additional notes pertaining to the application whose command line arguments are to be parsed....
Definition arguments.hpp:458