CPP-AP 2.7.0
Command-line argument parser for C++20
Loading...
Searching...
No Matches
optional.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
14#include "ap/nargs/range.hpp"
15
16#ifdef AP_TESTING
17
18namespace ap_testing {
19struct optional_argument_test_fixture;
20} // namespace ap_testing
21
22#endif
23
24namespace ap::argument {
25
30template <detail::c_argument_value_type T = std::string>
32public:
33 using value_type = T;
35
36 optional() = delete;
37
43
44 ~optional() = default;
45
51 bool operator==(const optional& other) const noexcept {
52 return this->_name == other._name;
53 }
54
60 optional& help(std::string_view help_msg) noexcept {
61 this->_help_msg = help_msg;
62 return *this;
63 }
64
70 optional& hidden(const bool h = true) noexcept {
71 this->_hidden = h;
72 return *this;
73 }
74
81 optional& required(const bool r = true) noexcept {
82 this->_required = r;
83 if (this->_required)
84 this->_bypass_required = false;
85 return *this;
86 }
87
94 optional& bypass_required(const bool br = true) noexcept {
95 this->_bypass_required = br;
96 if (this->_bypass_required)
97 this->_required = false;
98 return *this;
99 }
100
106 optional& nargs(const nargs::range& range) noexcept {
107 this->_nargs_range = range;
108 return *this;
109 }
110
116 optional& nargs(const count_type n) noexcept {
117 this->_nargs_range = nargs::range(n);
118 return *this;
119 }
120
127 optional& nargs(const count_type lower_bound, const count_type upper_bound) noexcept {
128 this->_nargs_range = nargs::range(lower_bound, upper_bound);
129 return *this;
130 }
131
139 template <action::detail::c_action_specifier AS, typename F>
140 optional& action(F&& action) noexcept {
143 this->_value_actions.emplace_back(std::forward<callable_type>(action));
144 }
145 else {
146 this->_flag_actions.emplace_back(std::forward<flag_action_type>(action));
147 }
148
149 return *this;
150 }
151
160 template <detail::c_range_of<value_type, detail::type_validator::convertible> CR>
161 optional& choices(const CR& choices) noexcept
162 requires(std::equality_comparable<value_type>)
163 {
164 for (const auto& choice : choices)
165 this->_choices.emplace_back(choice);
166 return *this;
167 }
168
175 optional& choices(std::initializer_list<value_type> choices) noexcept
176 requires(std::equality_comparable<value_type>)
177 {
178 return this->choices<>(choices);
179 }
180
187 optional& default_value(const std::convertible_to<value_type> auto& default_value) noexcept {
188 this->_default_value = std::make_any<value_type>(default_value);
189 this->_required = false;
190 return *this;
191 }
192
198 optional& implicit_value(const std::convertible_to<value_type> auto& implicit_value) noexcept {
199 this->_implicit_value = std::make_any<value_type>(implicit_value);
200 return *this;
201 }
202
204 friend class ::ap::argument_parser;
205
206#ifdef AP_TESTING
210 friend struct ::ap_testing::optional_argument_test_fixture;
211#endif
212
213private:
217
222 [[nodiscard]] detail::argument_descriptor desc(const bool verbose) const noexcept override {
223 detail::argument_descriptor desc(this->_name.str(), this->_help_msg);
224
225 if (not verbose)
226 return desc;
227
228 desc.params.reserve(6);
229 if (this->_required)
230 desc.add_param("required", "true");
231 if (this->bypass_required_enabled())
232 desc.add_param("bypass required", "true");
233 if (this->_nargs_range.is_bound())
234 desc.add_param("nargs", this->_nargs_range);
235 if constexpr (detail::c_writable<value_type>) {
236 if (not this->_choices.empty())
237 desc.add_range_param("choices", this->_choices);
238 if (this->_default_value.has_value())
239 desc.add_param("default value", std::any_cast<value_type>(this->_default_value));
240 if (this->_implicit_value.has_value())
241 desc.add_param("implicit value", std::any_cast<value_type>(this->_implicit_value));
242 }
243
244 return desc;
245 }
246
248 bool mark_used() override {
249 ++this->_count;
250 for (const auto& action : this->_flag_actions)
251 action();
252 return this->_accepts_further_values();
253 }
254
256 [[nodiscard]] bool is_used() const noexcept override {
257 return this->_count > 0;
258 }
259
261 [[nodiscard]] std::size_t count() const noexcept override {
262 return this->_count;
263 }
264
271 bool set_value(const std::string& str_value) override {
272 if (not this->_accepts_further_values())
273 throw parsing_failure::invalid_nvalues(this->_name, std::weak_ordering::greater);
274
277 value = value_type(str_value);
278 }
279 else {
280 if (not (std::istringstream(str_value) >> value))
281 throw parsing_failure::invalid_value(this->_name, str_value);
282 }
283
284 if (not detail::is_valid_choice(value, this->_choices))
285 throw parsing_failure::invalid_choice(this->_name, str_value);
286
287 const auto apply_visitor = action::detail::apply_visitor<value_type>{value};
288 for (const auto& action : this->_value_actions)
289 std::visit(apply_visitor, action);
290
291 this->_values.emplace_back(std::move(value));
292 return this->_accepts_further_values();
293 }
294
296 [[nodiscard]] bool has_value() const noexcept override {
297 return this->has_parsed_values() or this->_has_predefined_value();
298 }
299
301 [[nodiscard]] bool has_parsed_values() const noexcept override {
302 return not this->_values.empty();
303 }
304
306 [[nodiscard]] std::weak_ordering nvalues_ordering() const noexcept override {
307 if (this->_values.empty() and this->_has_predefined_value())
308 return std::weak_ordering::equivalent;
309
310 return this->_nargs_range.ordering(this->_values.size());
311 }
312
314 [[nodiscard]] const std::any& value() const override {
315 return this->_values.empty() ? this->_predefined_value() : this->_values.front();
316 }
317
319 [[nodiscard]] const std::vector<std::any>& values() const override {
320 return this->_values;
321 }
322
324 [[nodiscard]] bool _has_predefined_value() const noexcept {
325 return this->_default_value.has_value()
326 or (this->is_used() and this->_implicit_value.has_value());
327 }
328
333 [[nodiscard]] const std::any& _predefined_value() const {
334 if (this->is_used()) {
335 if (not this->_implicit_value.has_value())
336 throw(std::logic_error(
337 std::format("No implicit value specified for argument `{}`.", this->_name.str())
338 ));
339
340 return this->_implicit_value;
341 }
342
343 if (not this->_default_value.has_value())
344 throw(std::logic_error(
345 std::format("No default value specified for argument `{}`.", this->_name.str())
346 ));
347
348 return this->_default_value;
349 }
350
351 [[nodiscard]] bool _accepts_further_values() const noexcept {
352 return not std::is_gt(this->_nargs_range.ordering(this->_values.size() + 1ull));
353 }
354
358 std::vector<value_type> _choices;
359 std::vector<flag_action_type> _flag_actions;
360 std::vector<value_action_type> _value_actions;
361
362 std::size_t _count = 0ull;
363 std::vector<std::any> _values;
364};
365
366} // namespace ap::argument
Defines the base argument class and common utility.
Defines structures for formatting argument descriptions.
The optioanl argument class.
Definition optional.hpp:31
optional & bypass_required(const bool br=true) noexcept
Enable/disable bypassing the required attribute for the optional argument.
Definition optional.hpp:94
const std::vector< std::any > & values() const override
Definition optional.hpp:319
std::weak_ordering nvalues_ordering() const noexcept override
Definition optional.hpp:306
optional & help(std::string_view help_msg) noexcept
Set the help message for the optional argument.
Definition optional.hpp:60
std::vector< value_action_type > _value_actions
Definition optional.hpp:360
bool has_value() const noexcept override
Definition optional.hpp:296
bool set_value(const std::string &str_value) override
Set the value for the optional argument.
Definition optional.hpp:271
optional(const detail::argument_name &name)
Constructor for optional argument with the name identifier.
Definition optional.hpp:42
detail::argument_descriptor desc(const bool verbose) const noexcept override
Definition optional.hpp:222
bool _accepts_further_values() const noexcept
Definition optional.hpp:351
const std::any & _predefined_value() const
Definition optional.hpp:333
optional & choices(std::initializer_list< value_type > choices) noexcept
Set the choices for the optional argument.
Definition optional.hpp:175
bool is_used() const noexcept override
Definition optional.hpp:256
optional & action(F &&action) noexcept
Set the action for the optional argument.
Definition optional.hpp:140
std::vector< std::any > _values
Definition optional.hpp:363
optional & default_value(const std::convertible_to< value_type > auto &default_value) noexcept
Set the default value for the optional argument.
Definition optional.hpp:187
optional & implicit_value(const std::convertible_to< value_type > auto &implicit_value) noexcept
Set the implicit value for the optional argument.
Definition optional.hpp:198
nargs::range _nargs_range
Definition optional.hpp:355
bool mark_used() override
Mark the optional argument as used.
Definition optional.hpp:248
const std::any & value() const override
Definition optional.hpp:314
T value_type
The argument's value type.
Definition optional.hpp:33
bool has_parsed_values() const noexcept override
Definition optional.hpp:301
optional & required(const bool r=true) noexcept
Set the required attribute of the optional argument.
Definition optional.hpp:81
optional & nargs(const nargs::range &range) noexcept
Set the nargs range for the optional argument.
Definition optional.hpp:106
optional & nargs(const count_type lower_bound, const count_type upper_bound) noexcept
Set the nargs range for the optional argument.
Definition optional.hpp:127
optional & choices(const CR &choices) noexcept
Set the choices for the optional argument.
Definition optional.hpp:161
bool _has_predefined_value() const noexcept
Definition optional.hpp:324
std::size_t count() const noexcept override
Definition optional.hpp:261
typename action_type::on_flag::type flag_action_type
Definition optional.hpp:216
nargs::range::count_type count_type
The argument's value count type.
Definition optional.hpp:34
bool operator==(const optional &other) const noexcept
Equality comparison operator for optional argument.
Definition optional.hpp:51
optional & hidden(const bool h=true) noexcept
Set the hidden attribute for the positional argument.
Definition optional.hpp:70
std::vector< flag_action_type > _flag_actions
Definition optional.hpp:359
optional & nargs(const count_type n) noexcept
Set the nargs range for the optional argument.
Definition optional.hpp:116
std::vector< value_type > _choices
Definition optional.hpp:358
action::detail::value_action_variant_type< T > value_action_type
The argument's value action type.
Definition optional.hpp:215
Argument class interface.
const ap::detail::argument_name _name
const ap::detail::argument_name & name() const noexcept
bool bypass_required_enabled() const noexcept
std::optional< std::string > _help_msg
argument_base(const argument_name &name, const bool required=false)
A structure used to represent an argument's description.
void add_param(const std::string &param_name, const std::string &value)
Adds a parameter descriptor with the given string value.
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
Argument's number of values managing class.
Definition range.hpp:18
std::size_t count_type
Definition range.hpp:20
std::weak_ordering ordering(const range::count_type n) const noexcept
Determines the ordering of the count against a range instance.
Definition range.hpp:65
bool is_bound() const noexcept
Returns true if at least one bound (lower, upper) is set. Otherwise returns false
Definition range.hpp:48
The concept is satisfied when AS is a valid value action action specifier.
Definition utility.hpp:24
The concept is satisfied when T can be constructed from const std::string&.
Definition concepts.hpp:29
The concept is satisfied when T overloads the std::ostream operator <<.
Definition concepts.hpp:36
Provides the general concept definitions.
typename AS::template type< T > callable_type
Template argument action callable type alias.
Definition utility.hpp:36
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 utility.hpp:43
bool is_valid_choice(const T &value, const std::vector< T > &choices) noexcept
Checks if the provided choice is valid for the given set of choices.
range any() noexcept
range class builder function. Creates a range [0, inf].
Definition range.hpp:170
Defines the nargs::range class and it's builder functions.
A visitor structure used to apply value actions.
Definition utility.hpp:50
std::function< void()> type
Structure holding the argument's name.
std::string str() const noexcept
Get a string representation of the argument_name.
static parsing_failure invalid_value(const detail::argument_name &arg_name, const std::string &value) noexcept
static parsing_failure invalid_nvalues(const detail::argument_name &arg_name, const std::weak_ordering ordering) noexcept
static parsing_failure invalid_choice(const detail::argument_name &arg_name, const std::string &value) noexcept
Defines general action-related utility.