Skip to content

Commit 80ecc26

Browse files
authored
YT-CPPAP-46: Add compound argument flags support
Modified the command-line tokenization logic to try to split unknown secondary flag arguments into multiple secondary argument flag tokens (one for each character of the unknown flag). Example: If the program defines an argument with a secondary name `v`, then `-vvv` would be equivalent to `-v -v -v`, unless the program also defines a `-vvv` argument. Additional: - Added support for declaring the program version - Optimized argument lookup during argument tokenization and parsing stages
1 parent 7366309 commit 80ecc26

13 files changed

+424
-142
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 2.5.3
10+
VERSION 2.6.0
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 = 2.5.3
51+
PROJECT_NUMBER = 2.6.0
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 = "2.5.3",
3+
version = "2.6.0",
44
)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ Command-line argument parser for C++20
5656
- [Argument Parameters](/docs/tutorial.md#argument-parameters)
5757
- [Default Arguments](/docs/tutorial.md#default-arguments)
5858
- [Parsing Arguments](/docs/tutorial.md#parsing-arguments)
59+
- [Argument Parsing Rules](/docs/tutorial.md#argument-parsing-rules)
60+
- [Compound Arguments](/docs/tutorial.md#compound-arguments)
5961
- [Retrieving Argument Values](/docs/tutorial.md#retrieving-argument-values)
6062
- [Examples](/docs/tutorial.md#examples)
6163
- [Dev notes](/docs/dev_notes.md#dev-notes)

docs/tutorial.md

Lines changed: 114 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
- [Predefined Parameter Values](#predefined-parameter-values)
1111
- [Default Arguments](#default-arguments)
1212
- [Parsing Arguments](#parsing-arguments)
13+
- [Argument Parsing Rules](#argument-parsing-rules)
14+
- [Compound Arguments](#compound-arguments)
1315
- [Retrieving Argument Values](#retrieving-argument-values)
1416
- [Examples](#examples)
1517

@@ -103,17 +105,34 @@ To use the argument parser in your code you need to use the `ap::argument_parser
103105

104106
The parameters you can specify for a parser's instance are:
105107

106-
- Program name and description - used in the parser's configuration output (`std::cout << parser`).
108+
- The program's name, version and description - used in the parser's configuration output (`std::cout << parser`).
107109
- Verbosity mode - `false` by default; if set to `true` the parser's configuration output will include more detailed info about arguments' parameters in addition to their names and help messages.
108110
- [Arguments](#adding-arguments) - specify the values/options accepted by the program.
109111

110112
```cpp
111113
ap::argument_parser parser;
112114
parser.program_name("Name of the program")
115+
.program_version("alhpa")
113116
.program_description("Description of the program")
114117
.verbose();
115118
```
116119

120+
> [!TIP]
121+
>
122+
> You can specify the program version using a string (like in the example above) or using the `ap::version` structure:
123+
>
124+
> ```cpp
125+
> parser.program_version({0u, 0u, 0u})
126+
> parser.program_version({ .major = 1u, .minor = 1u, .patch = 1u });
127+
> ap::version ver{2u, 2u, 2u};
128+
> parser.program_version(ver);
129+
> ```
130+
>
131+
> **NOTE:** The `ap::version` struct
132+
> - contains the three members - `major`, `minor`, `patch` - all of which are of type `std::uint32_t`,
133+
> - defines a `std::string str() const` method which returns a `v{major}.{minor}.{path}` version string,
134+
> - defines the `std::ostream& operator<<` for stream insertion.
135+
117136
<br/>
118137
<br/>
119138
<br/>
@@ -803,41 +822,37 @@ int main(int argc, char* argv[]) {
803822
804823
```shell
805824
./power 2
806-
# out:
807-
# no exponent values given
825+
no exponent values given
808826
```
809827
810828
```shell
811829
./power
812-
# out:
813-
# [ERROR] : No values parsed for a required argument [base]
814-
# Program: power calculator
815-
#
816-
# Calculates the value of an expression: base ^ exponent
817-
#
818-
# Positional arguments:
819-
#
820-
# base : the exponentation base value
821-
#
822-
# Optional arguments:
823-
#
824-
# --exponent, -e : the exponent value
825-
# --help, -h : Display the help message
830+
[ERROR] : No values parsed for a required argument [base]
831+
Program: power calculator
832+
833+
Calculates the value of an expression: base ^ exponent
834+
835+
Positional arguments:
836+
837+
base : the exponentation base value
838+
839+
Optional arguments:
840+
841+
--exponent, -e : the exponent value
842+
--help, -h : Display the help message
826843
```
827844
828845
> [!IMPORTANT]
829846
>
830847
> For each positional argument there must be **exactly one value**.
831848
832-
- Optional arguments are parsed only with a flag:
849+
- Optional arguments are parsed only with a flag. The values passed after an argument flag will be treated as the values of the last optional argument that preceeds them. If no argument flag preceeds a value argument, then it will be treated as an **unknown** value.
833850
834851
```shell
835-
./power 2 --exponent 1 2 3
836-
# equivalent to: ./power 2 -e 1 2 3
837-
# out:
838-
# 2 ^ 1 = 2
839-
# 2 ^ 2 = 4
840-
# 2 ^ 3 = 8
852+
./power 2 --exponent 1 2 3 # equivalent to: ./power 2 -e 1 2 3
853+
2 ^ 1 = 2
854+
2 ^ 2 = 4
855+
2 ^ 3 = 8
841856
```
842857
843858
You can use the flag for each command-line value:
@@ -850,25 +865,85 @@ int main(int argc, char* argv[]) {
850865
851866
```shell
852867
./power 2 1 2 3
853-
# out:
854-
# [ERROR] : Failed to deduce the argument for values [1, 2, 3]
855-
# Program: power calculator
856-
#
857-
# Calculates the value of an expression: base ^ exponent
858-
#
859-
# Positional arguments:
860-
#
861-
# base : the exponentation base value
862-
#
863-
# Optional arguments:
864-
#
865-
# --exponent, -e : the exponent value
866-
# --help, -h : Display the help message
868+
[ERROR] : Failed to deduce the argument for values [1, 2, 3]
869+
Program: power calculator
870+
871+
Calculates the value of an expression: base ^ exponent
872+
873+
Positional arguments:
874+
875+
base : the exponentation base value
876+
877+
Optional arguments:
878+
879+
--exponent, -e : the exponent value
880+
--help, -h : Display the help message
867881
```
868882
883+
> [!WARNING]
884+
>
885+
> If an optional argument has the `nargs` parameter set with an upper bound, then the values that succeed this argument's flag will be assigned to this argument only until the specified upper bound is reached. Further values will be treated as **unknown** values.
886+
>
887+
> **Example:**
888+
>
889+
> ```cpp
890+
> parser.add_optional_argument<int>("exponent", "e").nargs(ap::nargs::up_to(3))
891+
> ```
892+
> ```shell
893+
> ./power 2 -e 1 2 3 4 5
894+
> [ERROR] : Failed to deduce the argument for values [4, 5]
895+
> Program: power calculator
896+
>
897+
> Calculates the value of an expression: base ^ exponent
898+
>
899+
> Positional arguments:
900+
>
901+
> base : the exponentation base value
902+
>
903+
> Optional arguments:
904+
>
905+
> --exponent, -e : the exponent value
906+
> --help, -h : Display the help message
907+
> ```
908+
909+
<br />
910+
911+
### Compound Arguments:
912+
913+
Compound argument flags are **secondary** argument flags of which **every** character matches the secondary name of an optional argument.
914+
915+
Example:
916+
917+
```cpp
918+
parser.add_optional_argument("verbose", "v")
919+
.nargs(0)
920+
.help("Increase verbosity level");
921+
922+
parser.add_flag("option", "o")
923+
.help("Enable an option flag");
924+
925+
parser.add_optional_argument<int>("numbers", "n")
926+
.help("Provide integer values");
927+
928+
parser.try_parse_args(argc, argv);
929+
930+
std::cout << "Verbosity level: " << parser.count("verbose")
931+
<< "\nOption used: " << std::boolalpha << parser.value<bool>("use-option")
932+
<< "\nNumbers: " << join(parser.values<int>("numbers"), ", ") // join is an imaginary function :)
933+
<< std::endl;
934+
935+
/*
936+
> ./program -vvon 1 2 3
937+
Verbosity level: 2
938+
Option used: true
939+
Numbers: 1, 2, 3
940+
```
941+
869942
> [!IMPORTANT]
870943
>
871-
> The parser's behavior depends on the argument definitions - see [Argument Parameters](#argument-parameters) section.
944+
> - If there exists an argument whose secondary name matches a possible compound of other arguments, the parser will still treat the flag as a flag of the **single matching argument**, not as multiple flags.
945+
> - The argument parser will try to assign the values following a compound argument flag to the argument represented by the **last character** of the compound flag.
946+
872947
873948
<br/>
874949
<br/>

0 commit comments

Comments
 (0)