CPP-AP 2.2.6
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;
34 using count_type = nargs::range::count_type;
35
36 optional() = delete;
37
42 optional(const detail::argument_name& name) : argument_base(name) {}
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
71 optional& required() noexcept {
72 this->_required = true;
73 return *this;
74 }
75
81 this->_bypass_required = true;
82 return *this;
83 }
84
90 optional& nargs(const nargs::range& range) noexcept {
91 this->_nargs_range = range;
92 return *this;
93 }
94
100 optional& nargs(const count_type n) noexcept {
101 this->_nargs_range = nargs::range(n);
102 return *this;
103 }
104
111 optional& nargs(const count_type lower_bound, const count_type upper_bound) noexcept {
112 this->_nargs_range = nargs::range(lower_bound, upper_bound);
113 return *this;
114 }
115
123 template <action::detail::c_action_specifier AS, typename F>
124 optional& action(F&& action) noexcept {
127 this->_value_actions.emplace_back(std::forward<callable_type>(action));
128 }
129 else {
130 this->_flag_actions.emplace_back(std::forward<flag_action_type>(action));
131 }
132
133 return *this;
134 }
135
144 template <detail::c_range_of<value_type, detail::type_validator::convertible> CR>
145 optional& choices(const CR& choices) noexcept
146 requires(std::equality_comparable<value_type>)
147 {
148 for (const auto& choice : choices)
149 this->_choices.emplace_back(choice);
150 return *this;
151 }
152
159 optional& choices(std::initializer_list<value_type> choices) noexcept
160 requires(std::equality_comparable<value_type>)
161 {
162 return this->choices<>(choices);
163 }
164
171 this->_default_value = default_value;
172 return *this;
173 }
174
181 this->_implicit_value = implicit_value;
182 return *this;
183 }
184
186 friend class ::ap::argument_parser;
187
188#ifdef AP_TESTING
192 friend struct ::ap_testing::optional_argument_test_fixture;
193#endif
194
195private:
196 using value_action_type =
198 using flag_action_type = typename action_type::on_flag::type;
199
205 [[nodiscard]] detail::argument_descriptor desc(const bool verbose, const char flag_char)
206 const noexcept override {
207 detail::argument_descriptor desc(this->_name.str(flag_char), this->_help_msg);
208
209 if (not verbose)
210 return desc;
211
212 desc.params.reserve(6);
213 if (this->_required)
214 desc.add_param("required", "true");
215 if (this->_bypass_required)
216 desc.add_param("bypass required", "true");
217 if (this->_nargs_range.is_bound())
218 desc.add_param("nargs", this->_nargs_range);
219 if constexpr (detail::c_writable<value_type>) {
220 if (not this->_choices.empty())
221 desc.add_range_param("choices", this->_choices);
222 if (this->_default_value.has_value())
223 desc.add_param("default value", std::any_cast<value_type>(this->_default_value));
224 if (this->_implicit_value.has_value())
225 desc.add_param("implicit value", std::any_cast<value_type>(this->_implicit_value));
226 }
227
228 return desc;
229 }
230
232 [[nodiscard]] bool is_required() const noexcept override {
233 return this->_required;
234 }
235
237 [[nodiscard]] bool bypass_required_enabled() const noexcept override {
238 return this->_bypass_required;
239 }
240
242 bool mark_used() override {
243 ++this->_count;
244 for (const auto& action : this->_flag_actions)
245 action();
246 return this->_accepts_further_values();
247 }
248
250 [[nodiscard]] bool is_used() const noexcept override {
251 return this->_count > 0;
252 }
253
255 [[nodiscard]] std::size_t count() const noexcept override {
256 return this->_count;
257 }
258
265 bool set_value(const std::string& str_value) override {
266 if (not this->_accepts_further_values())
267 throw parsing_failure::invalid_nvalues(this->_name, std::weak_ordering::greater);
268
269 value_type value;
270 if (not (std::istringstream(str_value) >> value))
271 throw parsing_failure::invalid_value(this->_name, str_value);
272
273 if (not detail::is_valid_choice(value, this->_choices))
274 throw parsing_failure::invalid_choice(this->_name, str_value);
275
276 const auto apply_visitor = action::detail::apply_visitor<value_type>{value};
277 for (const auto& action : this->_value_actions)
278 std::visit(apply_visitor, action);
279
280 this->_values.emplace_back(std::move(value));
281 return this->_accepts_further_values();
282 }
283
285 [[nodiscard]] bool has_value() const noexcept override {
286 return this->has_parsed_values() or this->_has_predefined_value();
287 }
288
290 [[nodiscard]] bool has_parsed_values() const noexcept override {
291 return not this->_values.empty();
292 }
293
295 [[nodiscard]] std::weak_ordering nvalues_ordering() const noexcept override {
296 if (this->_values.empty() and this->_has_predefined_value())
297 return std::weak_ordering::equivalent;
298
299 return this->_nargs_range.ordering(this->_values.size());
300 }
301
303 [[nodiscard]] const std::any& value() const override {
304 return this->_values.empty() ? this->_predefined_value() : this->_values.front();
305 }
306
308 [[nodiscard]] const std::vector<std::any>& values() const override {
309 return this->_values;
310 }
311
313 [[nodiscard]] bool _has_predefined_value() const noexcept {
314 return this->_default_value.has_value()
315 or (this->is_used() and this->_implicit_value.has_value());
316 }
317
322 [[nodiscard]] const std::any& _predefined_value() const {
323 if (this->is_used()) {
324 if (not this->_implicit_value.has_value())
325 throw(std::logic_error(
326 std::format("No implicit value specified for argument `{}`.", this->_name.str())
327 ));
328
329 return this->_implicit_value;
330 }
331
332 if (not this->_default_value.has_value())
333 throw(std::logic_error(
334 std::format("No default value specified for argument `{}`.", this->_name.str())
335 ));
336
337 return this->_default_value;
338 }
339
340 [[nodiscard]] bool _accepts_further_values() const noexcept {
341 return not std::is_gt(this->_nargs_range.ordering(this->_values.size() + 1ull));
342 }
343
344 bool _required = false;
345 bool _bypass_required = false;
346 nargs::range _nargs_range = nargs::any();
347 std::any _default_value;
348 std::any _implicit_value;
349 std::vector<value_type> _choices;
350 std::vector<flag_action_type> _flag_actions;
351 std::vector<value_action_type> _value_actions;
352
353 std::size_t _count = 0ull;
354 std::vector<std::any> _values;
355};
356
357} // 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 & help(std::string_view help_msg) noexcept
Set the help message for the optional argument.
Definition optional.hpp:60
optional(const detail::argument_name &name)
Constructor for optional argument with the name identifier.
Definition optional.hpp:42
optional & default_value(const value_type &default_value) noexcept
Set the default value for the optional argument.
Definition optional.hpp:170
optional & choices(std::initializer_list< value_type > choices) noexcept
Set the choices for the optional argument.
Definition optional.hpp:159
optional & required() noexcept
Mark the optional argument as required.
Definition optional.hpp:71
optional & action(F &&action) noexcept
Set the action for the optional argument.
Definition optional.hpp:124
optional & implicit_value(const value_type &implicit_value) noexcept
Set the implicit value for the optional argument.
Definition optional.hpp:180
T value_type
The argument's value type.
Definition optional.hpp:33
optional & nargs(const nargs::range &range) noexcept
Set the nargs range for the optional argument.
Definition optional.hpp:90
optional & bypass_required() noexcept
Enable bypassing the required status for the optional argument.
Definition optional.hpp:80
optional & nargs(const count_type lower_bound, const count_type upper_bound) noexcept
Set the nargs range for the optional argument.
Definition optional.hpp:111
optional & choices(const CR &choices) noexcept
Set the choices for the optional argument.
Definition optional.hpp:145
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 & nargs(const count_type n) noexcept
Set the nargs range for the optional argument.
Definition optional.hpp:100
Argument class interface.
const ap::detail::argument_name & name() const noexcept
A structure used to represent an argument's description.
void add_range_param(const std::string &name, const R &range, const std::string_view delimiter=default_delimiter)
Adds a range parameter descriptor with the given value.
void add_param(const std::string &name, const std::string &value)
Adds a parameter descriptor with the given string value.
Argument's number of values managing class.
Definition range.hpp:18
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 overloads the std::ostream operator <<.
Definition concepts.hpp:29
Provides the general concept definitions.
Defines the nargs::range class and it's builder functions.
Structure holding the argument's name.
std::string str(const std::optional< char > flag_char=std::nullopt) const noexcept
Get a string representation of the argument_name.
Defines general action-related utility.
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