Skip to content

Commit c8d84e7

Browse files
committed
YT-CPPAP-37: Greedy arguments
- Extended the `argument` class with a `bool _greedy` member (`false` by default) - Added a `greedy(bool)` setter method to the `argument` class. - Aligned the parsing logic to assign all values to a greedy argument (after it's encountered) until its upper `nargs` range is reached
1 parent c6da61e commit c8d84e7

File tree

13 files changed

+311
-162
lines changed

13 files changed

+311
-162
lines changed

CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ else()
77
endif()
88

99
project(cpp-ap
10-
VERSION 3.0.0.5
10+
VERSION 3.0.0.6
1111
DESCRIPTION "Command-line argument parser for C++20"
1212
HOMEPAGE_URL "https://github.com/SpectraL519/cpp-ap"
1313
LANGUAGES CXX

Doxyfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ PROJECT_NAME = CPP-AP
4848
# could be handy for archiving the generated documentation or if some version
4949
# control system is used.
5050

51-
PROJECT_NUMBER = 3.0.0.5
51+
PROJECT_NUMBER = 3.0.0.6
5252

5353
# Using the PROJECT_BRIEF tag one can provide an optional one line description
5454
# for a project that appears at the top of each page and should give viewer a

MODULE.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
module(
22
name = "cpp-ap",
3-
version = "3.0.0.5",
3+
version = "3.0.0.6",
44
)

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ Command-line argument parser for C++20
5454
- [The Parser Class](/docs/tutorial.md#the-parser-class)
5555
- [Adding Arguments](/docs/tutorial.md#adding-arguments)
5656
- [Argument Parameters](/docs/tutorial.md#argument-parameters)
57-
- [Common Parameters](/docs/tutorial.md##common-parameters)
58-
- [Parameters Specific for Optional Arguments](/docs/tutorial.md##parameters-specific-for-optional-arguments)
57+
- [Common Parameters](/docs/tutorial.md#common-parameters)
58+
- [Parameters Specific for Optional Arguments](/docs/tutorial.md#parameters-specific-for-optional-arguments)
5959
- [Default Arguments](/docs/tutorial.md#default-arguments)
6060
- [Parsing Arguments](/docs/tutorial.md#parsing-arguments)
6161
- [Basic Argument Parsing Rules](/docs/tutorial.md#basic-argument-parsing-rules)

docs/tutorial.md

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,18 @@
88
- [Adding Arguments](#adding-arguments)
99
- [Argument Parameters](#argument-parameters)
1010
- [Common Parameters](#common-parameters)
11+
- [help](#1-help---the-arguments-description-which-will-be-printed-when-printing-the-parser-class-instance)
12+
- [hidden](#2-hidden---if-this-option-is-set-for-an-argument-then-it-will-not-be-included-in-the-program-description)
13+
- [required](#3-required---if-this-option-is-set-for-an-argument-and-its-value-is-not-passed-in-the-command-line-an-exception-will-be-thrown)
14+
- [bypass required](#4-bypass_required---if-this-option-is-set-for-an-argument-the-required-option-for-other-arguments-will-be-discarded-if-the-bypassing-argument-is-used-in-the-command-line)
15+
- [nargs](#5-nargs---sets-the-allowed-number-of-values-to-be-parsed-for-an-argument)
16+
- [greedy](#6-greedy---if-this-option-is-set-the-argument-will-consume-all-command-line-values-until-its-upper-nargs-bound-is-reached)
17+
- [choices](#7-choices---a-list-of-valid-argument-values)
18+
- [value actions](#8-value-actions---functions-that-are-called-after-parsing-an-arguments-value)
19+
- [default values](#9-default_values---a-list-of-values-which-will-be-used-if-no-values-for-an-argument-have-been-parsed)
1120
- [Parameters Specific for Optional Arguments](#parameters-specific-for-optional-arguments)
21+
- [on-flag actions](#1-on-flag-actions---functions-that-are-called-immediately-after-parsing-an-arguments-flag)
22+
- [implicit values](#2-implicit_values---a-list-of-values-which-will-be-set-for-an-argument-if-only-its-flag-but-no-values-are-parsed-from-the-command-line)
1223
- [Predefined Parameter Values](#predefined-parameter-values)
1324
- [Default Arguments](#default-arguments)
1425
- [Parsing Arguments](#parsing-arguments)
@@ -189,9 +200,10 @@ parser.add_<positional/optional>_argument<value_type>("argument", "a");
189200
>
190201
> - If the argument's value type is `ap::none_type`, the argument will not accept any values and therefore no value-related parameters can be set for such argument. This includes:
191202
> - [nargs](#5-nargs---sets-the-allowed-number-of-values-to-be-parsed-for-an-argument-this-can-be-set-as-a)
192-
> - [choices](#6-choices---a-list-of-valid-argument-values)
193-
> - [value actions](#7-value-actions---function-performed-after-parsing-an-arguments-value)
194-
> - [default_values](#8-default_values---a-list-of-values-which-will-be-used-if-no-values-for-an-argument-have-been-parsed)
203+
> - [greedy](#6-greedy---if-this-option-is-set-the-argument-will-consume-all-command-line-values-until-its-upper-nargs-bound-is-reached)
204+
> - [choices](#7-choices---a-list-of-valid-argument-values)
205+
> - [value actions](#8-value-actions---functions-that-are-called-after-parsing-an-arguments-value)
206+
> - [default_values](#9-default_values---a-list-of-values-which-will-be-used-if-no-values-for-an-argument-have-been-parsed)
195207
> - [implicit_values](#2-implicit_values---a-list-of-values-which-will-be-set-for-an-argument-if-only-its-flag-but-no-values-are-parsed-from-the-command-line)
196208
197209
You can also add boolean flags:
@@ -343,8 +355,8 @@ Command Result
343355
344356
> [!NOTE]
345357
>
346-
> - Both positional and optional arguments have the `bypass_required` option disabled.
347-
> - The default value of the value parameter of the `bypass_required(bool)` function is `true` for both positional and optional arguments.
358+
> - Both all arguments have the `bypass_required` option disabled.
359+
> - The default value of the value parameter of the `argument::bypass_required(bool)` method is `true` for all arguments.
348360
349361
> [!WARNING]
350362
>
@@ -377,7 +389,9 @@ os << data << std::endl;
377389
378390
<br />
379391
380-
#### 5. `nargs` - Sets the allowed number of values to be parsed for an argument. This can be set as a:
392+
#### 5. `nargs` - Sets the allowed number of values to be parsed for an argument.
393+
394+
The `nargs` parameter can be set as:
381395
382396
- Specific number:
383397
@@ -416,7 +430,49 @@ os << data << std::endl;
416430
417431
<br />
418432
419-
#### 6. `choices` - A list of valid argument values.
433+
#### 6. `greedy` - If this option is set, the argument will consume ALL command-line values until it's upper nargs bound is reached.
434+
435+
> [!NOTE]
436+
>
437+
> - By default the `greedy` option is disabled for all arguments.
438+
> - The default value of the parameter of the `argument::greedy(bool)` method is true for all arguments.
439+
440+
> [!TIP]
441+
>
442+
> - Enabling the `greedy` option for an argument only makes sense for arguments with string-like value types.
443+
> - If no explicit `nargs` bound is set for a greedy argument, once it starts being parsed, it will consume all remaining command-line arguments.
444+
445+
Consider a simple example:
446+
447+
```cpp
448+
ap::argument_parser parser;
449+
parser.program_name("run-script")
450+
.default_arguments(ap::default_argument::o_help);
451+
452+
parser.add_positional_argument("script")
453+
.help("The name of the script to run");
454+
parser.add_optional_argument("args")
455+
.greedy()
456+
.help("Set the execution option");
457+
458+
parser.try_parse_args(argc, argv);
459+
460+
// Application logic here
461+
std::cout << "Executing: " << parser.value("script") << " " << ap::util::join(parser.values("args")) << std::endl;
462+
```
463+
464+
Here the program execution should look something like this:
465+
466+
```txt
467+
> ./run-script remove-comments --args module.py -v --type py
468+
Executing: remove-comments module.py -v --type py
469+
```
470+
471+
Notice that even though the `-v` and `--type` command-line arguments have flag prefixes and are not defined in the program, they are not treated as unknown arguments (and therefore no exception is thrown) because the `--args` argument is marked as `greedy` and it consumes these command-line arguments as its values.
472+
473+
<br />
474+
475+
#### 7. `choices` - A list of valid argument values.
420476
421477
```cpp
422478
parser.add_optional_argument<char>("method", "m").choices('a', 'b', 'c');
@@ -433,7 +489,7 @@ parser.add_optional_argument<char>("method", "m").choices('a', 'b', 'c');
433489
434490
<br />
435491
436-
#### 7. Value actions - Function performed after parsing an argument's value.
492+
#### 8. value actions - Functions that are called after parsing an argument's value.
437493
Actions are represented as functions, which take the argument's value as an argument. The available action types are:
438494
439495
- `observe` actions | `void(const value_type&)` - applied to the parsed value. No value is returned - this action type is used to perform some logic on the parsed value without modifying it.
@@ -478,7 +534,7 @@ Actions are represented as functions, which take the argument's value as an argu
478534
479535
<br />
480536
481-
#### 8. `default_values` - A list of values which will be used if no values for an argument have been parsed
537+
#### 9. `default_values` - A list of values which will be used if no values for an argument have been parsed
482538
483539
> [!WARNING]
484540
>
@@ -541,7 +597,7 @@ Command Result
541597
542598
Apart from the common parameters listed above, for optional arguments you can also specify the following parameters:
543599
544-
#### 1. On-flag actions - For optional arguments, apart from value actions, you can specify on-flag actions which are executed immediately after parsing an argument's flag.
600+
#### 1. on-flag actions - Functions that are called immediately after parsing an argument's flag.
545601
546602
```cpp
547603
void print_debug_info() noexcept {

include/ap/argument.hpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,11 @@ class argument : public detail::argument_base {
123123
return not this->_required and this->_bypass_required;
124124
}
125125

126+
/// @return `true` if the argument is greedy, `false` otherwise.
127+
[[nodiscard]] bool is_greedy() const noexcept override {
128+
return this->_greedy;
129+
}
130+
126131
// attribute setters
127132

128133
/**
@@ -171,6 +176,19 @@ class argument : public detail::argument_base {
171176
return *this;
172177
}
173178

179+
/**
180+
* @brief Set the `greedy` attribute of the argument.
181+
* @param g The attribute value.
182+
* @return Reference to the argument instance.
183+
* @note The method is enabled only if `value_type` is not `none_type`.
184+
*/
185+
argument& greedy(const bool g = true) noexcept
186+
requires(not util::c_is_none<value_type>)
187+
{
188+
this->_greedy = g;
189+
return *this;
190+
}
191+
174192
/**
175193
* @brief Set the nargs range for the argument.
176194
* @param range The attribute value.
@@ -193,8 +211,7 @@ class argument : public detail::argument_base {
193211
argument& nargs(const count_type n) noexcept
194212
requires(not util::c_is_none<value_type>)
195213
{
196-
this->_nargs_range = nargs::range(n);
197-
return *this;
214+
return this->nargs(nargs::range(n));
198215
}
199216

200217
/**
@@ -207,8 +224,7 @@ class argument : public detail::argument_base {
207224
argument& nargs(const count_type lower, const count_type upper) noexcept
208225
requires(not util::c_is_none<value_type>)
209226
{
210-
this->_nargs_range = nargs::range(lower, upper);
211-
return *this;
227+
return this->nargs(nargs::range(lower, upper));
212228
}
213229

214230
/**
@@ -671,7 +687,7 @@ class argument : public detail::argument_base {
671687
// attributes
672688
const ap::detail::argument_name _name; ///< The argument's name.
673689
std::optional<std::string> _help_msg; ///< The argument's help message.
674-
nargs::range _nargs_range; ///< The argument's nargs range attribute.
690+
nargs::range _nargs_range; ///< The argument's nargs range attribute value.
675691
[[no_unique_address]] value_arg_specific_type<std::vector<std::any>>
676692
_default_values; ///< The argument's default value list.
677693
[[no_unique_address]] value_arg_specific_type<optional_specific_type<std::vector<std::any>>>
@@ -683,9 +699,10 @@ class argument : public detail::argument_base {
683699
[[no_unique_address]] value_arg_specific_type<std::vector<value_action_type>>
684700
_value_actions; ///< The argument's value actions collection.
685701

686-
bool _required : 1; ///< The argument's `required` attribute.
687-
bool _bypass_required : 1 = false; ///< The argument's `bypass_required` attribute.
688-
bool _hidden : 1 = false; ///< The argument's `hidden` attribute.
702+
bool _required : 1; ///< The argument's `required` attribute value.
703+
bool _bypass_required : 1 = false; ///< The argument's `bypass_required` attribute value.
704+
bool _greedy : 1 = false; ///< The argument's `greedy` attribute value.
705+
bool _hidden : 1 = false; ///< The argument's `hidden` attribute value.
689706

690707
// parsing result
691708
[[no_unique_address]] optional_specific_type<std::size_t>

0 commit comments

Comments
 (0)