CPP-AP 3.0.1
Command-line argument parser for C++20
Loading...
Searching...
No Matches
argument.hpp
Go to the documentation of this file.
1// Copyright (c) 2023-2025 Jakub MusiaƂ
2// This file is part of the CPP-AP project (https://github.com/SpectraL519/cpp-ap).
3// Licensed under the MIT License. See the LICENSE file in the project root for full license information.
4
6
7#pragma once
8
13#include "nargs/range.hpp"
14#include "types.hpp"
15#include "util/concepts.hpp"
16#include "util/ranges.hpp"
17
18#ifdef AP_TESTING
19
20namespace ap_testing {
21struct argument_test_fixture;
22} // namespace ap_testing
23
24#endif
25
26namespace ap {
27
29enum class argument_type : bool { positional, optional };
30
57template <argument_type ArgT, util::c_argument_value_type T = std::string>
59public:
60 using value_type = T;
62
63 static constexpr argument_type type = ArgT;
64
65 argument() = delete;
66
75
87
90 [[nodiscard]] bool is_positional() const noexcept override {
92 }
93
96 [[nodiscard]] bool is_optional() const noexcept override {
98 }
99
101 [[nodiscard]] const ap::detail::argument_name& name() const noexcept override {
102 return this->_name;
103 }
104
106 [[nodiscard]] const std::optional<std::string>& help() const noexcept override {
107 return this->_help_msg;
108 }
109
111 [[nodiscard]] bool is_hidden() const noexcept override {
112 return this->_hidden;
113 }
114
116 [[nodiscard]] bool is_required() const noexcept override {
117 return this->_required;
118 }
119
121 [[nodiscard]] bool suppresses_arg_checks() const noexcept override {
122 return this->_suppress_arg_checks;
123 }
124
126 [[nodiscard]] bool suppresses_group_checks() const noexcept override {
127 return this->_suppress_group_checks;
128 }
129
131 [[nodiscard]] bool is_greedy() const noexcept override {
132 return this->_greedy;
133 }
134
135 // attribute setters
136
142 argument& help(std::string_view help_msg) noexcept {
143 this->_help_msg = help_msg;
144 return *this;
145 }
146
152 argument& hidden(const bool h = true) noexcept {
153 this->_hidden = h;
154 return *this;
155 }
156
163 argument& required(const bool value = true) {
164 if (value and (this->_suppress_arg_checks or this->_suppress_group_checks))
166 std::format("A suppressing argument [{}] cannot be required!", this->_name.str())
167 );
168
169 this->_required = value;
170 return *this;
171 }
172
179 argument& suppress_arg_checks(const bool value = true) {
180 if (value and this->_required)
181 throw invalid_configuration(std::format(
182 "A required argument [{}] cannot suppress argument checks!", this->_name.str()
183 ));
184
186 return *this;
187 }
188
196 if (value and this->_required)
197 throw invalid_configuration(std::format(
198 "A required argument [{}] cannot suppress argument group checks!", this->_name.str()
199 ));
200
202 return *this;
203 }
204
211 argument& greedy(const bool value = true) noexcept
212 requires(not util::c_is_none<value_type>)
213 {
214 this->_greedy = value;
215 return *this;
216 }
217
224 argument& nargs(const nargs::range& range) noexcept
225 requires(not util::c_is_none<value_type>)
226 {
227 this->_nargs_range = range;
228 return *this;
229 }
230
237 argument& nargs(const count_type n) noexcept
238 requires(not util::c_is_none<value_type>)
239 {
240 return this->nargs(nargs::range(n));
241 }
242
250 argument& nargs(const count_type lower, const count_type upper) noexcept
251 requires(not util::c_is_none<value_type>)
252 {
253 return this->nargs(nargs::range(lower, upper));
254 }
255
266 template <action::util::c_value_action_specifier AS, typename F>
267 argument& action(F&& action) noexcept
268 requires(not util::c_is_none<value_type>)
269 {
271 this->_value_actions.emplace_back(std::forward<callable_type>(action));
272 return *this;
273 }
274
283 template <action::util::c_flag_action_specifier AS, typename F>
284 argument& action(F&& action) noexcept
285 requires(type == argument_type::optional)
286 {
287 this->_flag_actions.emplace_back(std::forward<flag_action_type>(action));
288 return *this;
289 }
290
300 template <util::c_range_of<value_type, util::type_validator::convertible> CR>
301 argument& choices(const CR& choices) noexcept
302 requires(not util::c_is_none<value_type> and std::equality_comparable<value_type>)
303 {
304 for (const auto& choice : choices)
305 this->_choices.emplace_back(choice);
306 return *this;
307 }
308
315 argument& choices(std::initializer_list<value_type> choices) noexcept
316 requires(not util::c_is_none<value_type> and std::equality_comparable<value_type>)
317 {
318 return this->choices<>(choices);
319 }
320
328 argument& choices(const std::convertible_to<value_type> auto&... choices) noexcept
329 requires(not util::c_is_none<value_type> and std::equality_comparable<value_type>)
330 {
331 (this->_choices.emplace_back(choices), ...);
332 return *this;
333 }
334
342 template <util::c_range_of<value_type, util::type_validator::convertible> CR>
343 argument& default_values(const CR& values) noexcept
344 requires(not util::c_is_none<value_type> and std::equality_comparable<value_type>)
345 {
346 for (const auto& value : values)
347 this->_default_values.emplace_back(std::make_any<value_type>(value));
348 this->_required = false;
349 return *this;
350 }
351
359 argument& default_values(std::initializer_list<value_type> values) noexcept
360 requires(not util::c_is_none<value_type> and std::equality_comparable<value_type>)
361 {
362 return this->default_values<>(values);
363 }
364
372 argument& default_values(const std::convertible_to<value_type> auto&... values) noexcept
373 requires(not util::c_is_none<value_type>)
374 {
375 (this->_default_values.emplace_back(std::make_any<value_type>(values)), ...);
376 this->_required = false;
377 return *this;
378 }
379
387 template <util::c_range_of<value_type, util::type_validator::convertible> CR>
388 argument& implicit_values(const CR& values) noexcept
390 {
391 for (const auto& value : values)
392 this->_implicit_values.emplace_back(std::make_any<value_type>(value));
393 return *this;
394 }
395
402 argument& implicit_values(std::initializer_list<value_type> values) noexcept
404 {
405 return this->implicit_values<>(values);
406 }
407
414 argument& implicit_values(const std::convertible_to<value_type> auto&... values) noexcept
416 {
417 (this->_implicit_values.emplace_back(std::make_any<value_type>(values)), ...);
418 return *this;
419 }
420
421#ifdef AP_TESTING
422 friend struct ::ap_testing::argument_test_fixture;
423#endif
424
425private:
428
431
434 template <typename _T>
435 using value_arg_specific_type = std::conditional_t<
437 none_type,
438 _T>;
439
442 template <typename _T>
444 std::conditional_t<type == argument_type::positional, _T, none_type>;
445
448 template <typename _T>
450 std::conditional_t<type == argument_type::optional, _T, none_type>;
451
458 [[nodiscard]] detail::help_builder help_builder(const bool verbose) const noexcept override {
459 detail::help_builder bld(this->_name.str(), this->_help_msg);
460
461 if (not verbose)
462 return bld;
463
464 bld.params.reserve(6ull);
465 if (this->_required != _default_required)
466 bld.add_param("required", std::format("{}", this->_required));
467 if (this->_suppress_arg_checks)
468 bld.add_param("suppress arg checks", "true");
469 if (this->_suppress_group_checks)
470 bld.add_param("suppress group checks", "true");
472 bld.add_param("nargs", this->_nargs_range);
473 if constexpr (util::c_writable<value_type>) {
474 if (not this->_choices.empty())
475 bld.add_range_param("choices", this->_choices);
476 if (not this->_default_values.empty())
477 bld.add_range_param(
478 "default value(s)", util::any_range_cast_view<value_type>(this->_default_values)
479 );
480 if constexpr (type == argument_type::optional) {
481 if (not this->_implicit_values.empty())
482 bld.add_range_param(
483 "implicit value(s)",
484 util::any_range_cast_view<value_type>(this->_implicit_values)
485 );
486 }
487 }
488
489 return bld;
490 }
491
494 bool mark_used() override {
495 if constexpr (type == argument_type::optional) {
496 ++this->_count;
497 for (const auto& action : this->_flag_actions)
498 action();
499 }
500
501 return this->_accepts_further_values();
502 }
503
505 [[nodiscard]] bool is_used() const noexcept override {
506 return this->count() > 0ull;
507 }
508
514 [[nodiscard]] std::size_t count() const noexcept override {
515 if constexpr (type == argument_type::optional)
516 return this->_count;
517 else
518 return static_cast<std::size_t>(this->has_parsed_values());
519 }
520
527 bool set_value(const std::string& str_value) override {
528 return this->_set_value_impl(str_value);
529 }
530
533 [[nodiscard]] bool has_value() const noexcept override {
534 return this->has_parsed_values() or this->_has_predefined_values_impl();
535 }
536
538 [[nodiscard]] bool has_parsed_values() const noexcept override {
539 return not this->_values.empty();
540 }
541
543 [[nodiscard]] bool has_predefined_values() const noexcept override {
544 return this->_has_predefined_values_impl();
545 }
546
548 [[nodiscard]] std::weak_ordering nvalues_ordering() const noexcept override {
549 if (this->_values.empty() and this->_has_predefined_values_impl())
550 return std::weak_ordering::equivalent;
551
552 return this->_values.size() <=> this->_nargs_range;
553 }
554
560 [[nodiscard]] const std::any& value() const override {
561 if (this->has_parsed_values())
562 return this->_values.front();
563
564 if constexpr (util::c_is_none<value_type>)
565 throw std::logic_error(
566 std::format("No values parsed for argument '{}'.", this->_name.str())
567 );
568 else
569 return this->_predefined_values().front();
570 }
571
573 [[nodiscard]] const std::vector<std::any>& values() const override {
574 return this->_values_impl();
575 }
576
579 [[nodiscard]] const std::vector<std::any>& _values_impl() const noexcept
580 requires(util::c_is_none<value_type>)
581 {
582 return this->_values;
583 }
584
590 [[nodiscard]] const std::vector<std::any>& _values_impl() const noexcept
591 requires(not util::c_is_none<value_type>)
592 {
593 if (this->has_parsed_values())
594 return this->_values;
595
596 try {
597 return this->_predefined_values();
598 }
599 catch (const std::logic_error&) {
600 return this->_values; // fallback: empty vector
601 }
602 }
603
605 [[nodiscard]] bool _has_predefined_values_impl() const noexcept
606 requires(util::c_is_none<value_type>)
607 {
608 return false;
609 }
610
617 [[nodiscard]] bool _has_predefined_values_impl() const noexcept
618 requires(not util::c_is_none<value_type>)
619 {
620 if constexpr (type == argument_type::positional)
621 return not this->_default_values.empty();
622 else
623 return not this->_default_values.empty()
624 or (this->is_used() and not this->_implicit_values.empty());
625 }
626
634 [[nodiscard]] const std::vector<std::any>& _predefined_values() const
635 requires(not util::c_is_none<value_type>)
636 {
637 if constexpr (type == argument_type::optional) {
638 if (this->is_used()) {
639 if (this->_implicit_values.empty())
640 throw(std::logic_error(std::format(
641 "No implicit values specified for argument '{}'.", this->_name.str()
642 )));
643
644 return this->_implicit_values;
645 }
646 }
647
648 if (this->_default_values.empty())
649 throw(std::logic_error(
650 std::format("No default values specified for argument '{}'.", this->_name.str())
651 ));
652
653 return this->_default_values;
654 }
655
657 [[nodiscard]] bool _accepts_further_values() const noexcept {
658 return not std::is_gt(this->_values.size() + 1ull <=> this->_nargs_range);
659 }
660
663 [[nodiscard]] bool _is_valid_choice(const value_type& value) const noexcept
664 requires(not util::c_is_none<value_type>)
665 {
666 return this->_choices.empty()
667 or std::ranges::find(this->_choices, value) != this->_choices.end();
668 }
669
676 bool _set_value_impl(const std::string& str_value)
678 {
679 throw parsing_failure(std::format(
680 "Cannot set values for a none-type argument '{}' (value: '{}')",
681 this->_name.str(),
682 str_value
683 ));
684 }
685
696 bool _set_value_impl(const std::string& str_value)
697 requires(not util::c_is_none<value_type>)
698 {
699 if (not this->_accepts_further_values())
700 throw parsing_failure::invalid_nvalues(this->_name, std::weak_ordering::greater);
701
704 value = value_type(str_value);
705 }
706 else {
707 if (not (std::istringstream(str_value) >> value))
708 throw parsing_failure(std::format(
709 "Cannot parse value `{}` for argument [{}].", str_value, this->_name.str()
710 ));
711 }
712
713 if (not this->_is_valid_choice(value))
714 throw parsing_failure(std::format(
715 "Value `{}` is not a valid choice for argument [{}].", str_value, this->_name.str()
716 ));
717
718 const auto apply_visitor = action::util::apply_visitor<value_type>{value};
719 for (const auto& action : this->_value_actions)
720 std::visit(apply_visitor, action);
721
722 this->_values.emplace_back(std::move(value));
723 return this->_accepts_further_values();
724 }
725
726 // attributes
728 std::optional<std::string> _help_msg;
740
741 bool _required : 1;
743 false;
745 false;
746 bool _greedy : 1 = false;
747 bool _hidden : 1 = false;
748
749 // parsing result
750 [[no_unique_address]] optional_specific_type<std::size_t>
752 std::vector<std::any> _values;
753
754 // default attribute values
760};
761
767template <util::c_argument_value_type T = std::string>
769
775template <util::c_argument_value_type T = std::string>
777
778} // namespace ap
Defines the base argument class and common utility.
Represents a command-line argument, either positional or optional.
Definition argument.hpp:58
argument & action(F &&action) noexcept
Set the on-flag action for the argument.
Definition argument.hpp:284
std::vector< std::any > _values
The argument's parsed values.
Definition argument.hpp:752
std::conditional_t< type==argument_type::positional, _T, none_type > positional_specific_type
The argument's positional-argument-specific type alias.
Definition argument.hpp:444
bool suppresses_group_checks() const noexcept override
Definition argument.hpp:126
static constexpr argument_type type
The argument's type discriminator.
Definition argument.hpp:63
argument & action(F &&action) noexcept
Set the value action for the argument.
Definition argument.hpp:267
bool _greedy
The argument's greedy attribute value.
Definition argument.hpp:746
action::util::value_action_variant_type< T > value_action_type
The argument's value action type alias.
Definition argument.hpp:427
argument & implicit_values(const CR &values) noexcept
Add implicit values for the optional argument.
Definition argument.hpp:388
const std::vector< std::any > & _predefined_values() const
Definition argument.hpp:634
T value_type
The argument's value type alias.
Definition argument.hpp:60
argument(const detail::argument_name &name)
Optional argument constructor.
Definition argument.hpp:81
const std::vector< std::any > & _values_impl() const noexcept
Definition argument.hpp:579
bool is_greedy() const noexcept override
Definition argument.hpp:131
bool mark_used() override
Mark the optional argument as used.
Definition argument.hpp:494
const std::any & value() const override
Definition argument.hpp:560
argument & default_values(const CR &values) noexcept
Add default values for the argument.
Definition argument.hpp:343
bool _is_valid_choice(const value_type &value) const noexcept
Definition argument.hpp:663
value_arg_specific_type< optional_specific_type< std::vector< std::any > > > _implicit_values
The optional argument's implicit value list.
Definition argument.hpp:733
bool is_required() const noexcept override
Definition argument.hpp:116
bool has_value() const noexcept override
Definition argument.hpp:533
bool _required
The argument's required attribute value.
Definition argument.hpp:741
const ap::detail::argument_name _name
The argument's name.
Definition argument.hpp:727
std::weak_ordering nvalues_ordering() const noexcept override
Definition argument.hpp:548
argument & nargs(const count_type n) noexcept
Set the nargs range for the argument.
Definition argument.hpp:237
bool _set_value_impl(const std::string &str_value)
The implementation of the set_value method for non-none-type arguments.
Definition argument.hpp:696
bool is_positional() const noexcept override
Checks if the argument is positional.
Definition argument.hpp:90
argument & suppress_arg_checks(const bool value=true)
Enable/disable suppressing argument checks for other arguments.
Definition argument.hpp:179
argument & nargs(const count_type lower, const count_type upper) noexcept
Set the nargs range for the optional argument.
Definition argument.hpp:250
bool has_parsed_values() const noexcept override
Definition argument.hpp:538
bool _accepts_further_values() const noexcept
Definition argument.hpp:657
optional_specific_type< std::vector< flag_action_type > > _flag_actions
The optional argument's flag actions collection.
Definition argument.hpp:737
nargs::count_type count_type
The argument's count type alias.
Definition argument.hpp:61
nargs::range _nargs_range
The argument's nargs range attribute value.
Definition argument.hpp:729
bool is_used() const noexcept override
Definition argument.hpp:505
argument & default_values(std::initializer_list< value_type > values) noexcept
Add default values for the argument.
Definition argument.hpp:359
argument & implicit_values(std::initializer_list< value_type > values) noexcept
Add implicit values for the optional argument.
Definition argument.hpp:402
bool _has_predefined_values_impl() const noexcept
Definition argument.hpp:605
bool suppresses_arg_checks() const noexcept override
Definition argument.hpp:121
bool is_optional() const noexcept override
Checks if the argument is optional.
Definition argument.hpp:96
const std::optional< std::string > & help() const noexcept override
Definition argument.hpp:106
std::optional< std::string > _help_msg
The argument's help message.
Definition argument.hpp:728
argument & required(const bool value=true)
Set the required attribute of the argument.
Definition argument.hpp:163
value_arg_specific_type< std::vector< value_action_type > > _value_actions
The argument's value actions collection.
Definition argument.hpp:739
argument & greedy(const bool value=true) noexcept
Set the greedy attribute of the argument.
Definition argument.hpp:211
argument & choices(const std::convertible_to< value_type > auto &... choices) noexcept
Add the choices for the argument.
Definition argument.hpp:328
bool _has_predefined_values_impl() const noexcept
Definition argument.hpp:617
bool _hidden
The argument's hidden attribute value.
Definition argument.hpp:747
value_arg_specific_type< std::vector< std::any > > _default_values
The argument's default value list.
Definition argument.hpp:731
typename action_type::on_flag::type flag_action_type
The argument's flag action type alias.
Definition argument.hpp:430
argument & choices(std::initializer_list< value_type > choices) noexcept
Add the choices for the argument.
Definition argument.hpp:315
const std::vector< std::any > & values() const override
Definition argument.hpp:573
argument & suppress_group_checks(const bool value=true)
Enable/disable suppressing argument group checks.
Definition argument.hpp:195
bool is_hidden() const noexcept override
Definition argument.hpp:111
std::size_t count() const noexcept override
Definition argument.hpp:514
std::conditional_t< type==argument_type::optional, _T, none_type > optional_specific_type
The argument's optional-argument-specific type alias.
Definition argument.hpp:450
static constexpr nargs::range _default_nargs_range
Definition argument.hpp:756
bool has_predefined_values() const noexcept override
Definition argument.hpp:543
static constexpr nargs::range _default_nargs_range_actual
Definition argument.hpp:758
argument & nargs(const nargs::range &range) noexcept
Set the nargs range for the argument.
Definition argument.hpp:224
bool _suppress_group_checks
The argument's suppress_group_checks attribute value.
Definition argument.hpp:745
argument & help(std::string_view help_msg) noexcept
Set the help message for the argument.
Definition argument.hpp:142
argument()=delete
bool _suppress_arg_checks
The argument's suppress_arg_checks attribute value.
Definition argument.hpp:743
static constexpr bool _default_required
Definition argument.hpp:755
optional_specific_type< std::size_t > _count
The argument's value count.
Definition argument.hpp:751
detail::help_builder help_builder(const bool verbose) const noexcept override
Creates a help message builder object for the argument.
Definition argument.hpp:458
value_arg_specific_type< std::vector< value_type > > _choices
The argument's valid choices collection.
Definition argument.hpp:735
const std::vector< std::any > & _values_impl() const noexcept
Definition argument.hpp:590
bool set_value(const std::string &str_value) override
Set the value for the optional argument.
Definition argument.hpp:527
argument(const detail::argument_name &name)
Positional argument constructor.
Definition argument.hpp:72
const ap::detail::argument_name & name() const noexcept override
Definition argument.hpp:101
argument & implicit_values(const std::convertible_to< value_type > auto &... values) noexcept
Add a implicit values for the optional argument.
Definition argument.hpp:414
argument & default_values(const std::convertible_to< value_type > auto &... values) noexcept
Add default values for the argument.
Definition argument.hpp:372
std::conditional_t< util::c_is_none< value_type >, none_type, _T > value_arg_specific_type
Type alias for value-argument-specific types.
Definition argument.hpp:438
argument & hidden(const bool h=true) noexcept
Set the hidden attribute for the argument.
Definition argument.hpp:152
bool _set_value_impl(const std::string &str_value)
The implementation of the set_value method for none-type arguments.
Definition argument.hpp:676
argument & choices(const CR &choices) noexcept
Add the choices for the argument.
Definition argument.hpp:301
Argument class interface.
A help message builder class.
void add_range_param(const std::string &param_name, const R &range, const std::string_view delimiter=default_delimiter)
Adds a range parameter descriptor with the given value.
std::vector< parameter_descriptor > params
void add_param(const std::string &param_name, const std::string &value)
Adds a parameter descriptor with the given string value.
Argument's number of values managing class.
Definition range.hpp:27
The concept is satisfied when T is ap::none_type.
Definition concepts.hpp:25
The concept is satisfied when T can be constructed from const std::string&.
Definition concepts.hpp:41
The concept is satisfied when T overloads the std::ostream operator <<.
Definition concepts.hpp:49
typename AS::template type< T > callable_type
Template argument action callable type alias.
Definition helpers.hpp:22
Defines structures for creating and formatting help messages.
Defines general action-related helper utility.
std::variant< callable_type< action_type::observe, T >, callable_type< action_type::transform, T >, callable_type< action_type::modify, T > > value_action_variant_type
Template argument action callabla variant type alias.
Definition helpers.hpp:29
constexpr range any() noexcept
range class builder function. Creates a range [0, inf].
Definition range.hpp:163
std::size_t count_type
Definition range.hpp:18
argument_type
A discriminator type used to specify the type of an argument within the ap::argument class.
Definition argument.hpp:29
Defines the nargs::range class and it's builder functions.
Provides common ranges utility functions.
A visitor structure used to apply value actions.
Definition helpers.hpp:37
std::function< void()> type
Definition types.hpp:60
Structure holding the argument's name.
std::string str() const noexcept
Get a string representation of the argument_name.
Exception type used for invalid configuration of an argument parser or its arguments.
A type representing the absence of a value. This type is used for arguments that should not store any...
Definition types.hpp:20
Exception type used for errors encountered during the argument parsing operation.
static parsing_failure invalid_nvalues(const detail::argument_name &arg_name, const std::weak_ordering ordering) noexcept