@@ -71,6 +71,15 @@ The built-in validators for CLI11 are:
71
71
| ` ExistingPath ` | Check for an existing path |
72
72
| ` NonexistentPath ` | Check for an non-existing path |
73
73
| ` Range(min=0, max) ` | Produce a range (factory). Min and max are inclusive. |
74
+ | ` NonNegativeNumber ` | Range(0,max<double >) |
75
+ | ` PositiveNumber ` | Range(epsilon,max<double >) |
76
+
77
+ A few built-in transformers are also available
78
+
79
+ | Transformer | Description |
80
+ | ------------------- | ---------------------------------------------------------- |
81
+ | ` EscapedString ` | modify a string using defined escape characters |
82
+ | ` FileOnDefaultPath ` | Modify a path if the file is a particular default location |
74
83
75
84
And, the protected members that you can set when you make your own are:
76
85
@@ -82,3 +91,141 @@ And, the protected members that you can set when you make your own are:
82
91
| ` int ` (` -1 ` ) | ` application_index_ ` | The element this validator applies to (-1 for all) |
83
92
| ` bool ` (` true ` ) | ` active_ ` | This can be disabled |
84
93
| ` bool ` (` false ` ) | ` non_modifying_ ` | Specify that this is a Validator instead of a Transformer |
94
+
95
+ ## Extra Validators
96
+
97
+ Until CLI11 v3.0 these validators will be available by default. They can be
98
+ disabled at compilation time by defining CLI11_DISABLE_EXTRA_VALIDATORS to 1.
99
+ After version 3.0 they can be enabled by defining CLI11_ENABLE_EXTRA_VALIDATORS
100
+ to 1. Some of the Validators are template heavy so if they are not needed and
101
+ compilation time is a concern they can be disabled.
102
+
103
+ | Validator | Description |
104
+ | -------------------- | ------------------------------------------------------------------ |
105
+ | ` ValidIPV4 ` | check for valid IPV4 address XX.XX.XX.XX |
106
+ | ` TypeValidator<T> ` | template for checking that a value can convert to a specific type |
107
+ | ` Number ` | Check that a value can convert to a number |
108
+ | ` IsMember ` | Check that a value is one of a set of values |
109
+ | ` CheckedTransformer ` | Values must be one of the transformed set or the result |
110
+ | ` AsNumberWithUnit ` | checks for numbers with a unit as part of a specified set of units |
111
+ | ` AsSizeValue ` | As Number with Unit with support for SI prefixes |
112
+
113
+ | Transformer | Description |
114
+ | ---------------------- | --------------------------------------------------- |
115
+ | ` Bound<T>(min=0, max) ` | Force a range (factory). Min and max are inclusive. |
116
+ | ` Transformer ` | Modify values in a set to the matching pair value |
117
+
118
+ ## New Extra Validators
119
+
120
+ Some additional validators can be enabled by using CLI11_ENABLE_EXTRA_VALIDATORS
121
+ to 1. These validators are disabled by default.
122
+
123
+ ## Custom Validators
124
+
125
+ CLI11 also supports the use of custom validators, this includes using the
126
+ Validator class constructor with a custom function calls or subclassing
127
+ Validator to define a new class.
128
+
129
+ ### Custom Validator operation
130
+
131
+ The simplest way to make a new Validator is to mimic how many of the existing
132
+ Validators are created. Take for example the ` IPV4Validator `
133
+
134
+ ``` cpp
135
+ class IPV4Validator : public Validator {
136
+ public:
137
+ IPV4Validator();
138
+ };
139
+
140
+ CLI11_INLINE IPV4Validator::IPV4Validator() : Validator("IPV4") {
141
+ func_ = [ ] (std::string &ip_addr) {
142
+ auto cdot = std::count(ip_addr.begin(), ip_addr.end(), '.');
143
+ if(cdot != 3u) {
144
+ return std::string("Invalid IPV4 address: must have 3 separators");
145
+ }
146
+ auto result = CLI::detail::split(ip_addr, '.');
147
+ if(result.size() != 4) {
148
+ return std::string("Invalid IPV4 address: must have four parts (") + ip_addr + ')';
149
+ }
150
+ int num = 0;
151
+ for(const auto &var : result) {
152
+ using CLI::detail::lexical_cast;
153
+ bool retval = lexical_cast(var, num);
154
+ if(!retval) {
155
+ return std::string("Failed parsing number (") + var + ')';
156
+ }
157
+ if(num < 0 || num > 255) {
158
+ return std::string("Each IP number must be between 0 and 255 ") + var;
159
+ }
160
+ }
161
+ return std::string{};
162
+ };
163
+ }
164
+ ```
165
+
166
+ The `IPV4Validator` class inherits from `Validator` and creates a new
167
+ constructor. In that constructor it defines the lambda function that does the
168
+ checking. Then IPV4 can be used like any other Validator. One specific item of
169
+ note is that the class does not define any new member variables, so the class if
170
+ copyable to a Validator, only the constructor is different.
171
+
172
+ If additional members are needed, then the `check` and `transform` overloads
173
+ that use shared pointers need to be used. The other overloads pass by value so
174
+ polymorphism doesn't work. The custom_validator example shows a case like this.
175
+
176
+ ```cpp
177
+ template <typename T> class DeltaRange : public CLI::Validator {
178
+ public:
179
+ T center_point;
180
+ T delta;
181
+ DeltaRange(const T ¢er, const T &range)
182
+ : CLI::Validator(
183
+ [this](const std::string &value) -> std::string {
184
+ T newValue;
185
+ auto result = CLI::detail::lexical_cast(value, newValue);
186
+ if(!(result && this->check(newValue))) {
187
+ return std::string("value not within range");
188
+ }
189
+ return std::string{};
190
+ },
191
+ "RANGE"),
192
+ center_point(center), delta(range) {}
193
+
194
+ CLI11_NODISCARD bool check(const T &test) const { return (test >= (center_point - delta)) && (test <= (center_point + delta)); }
195
+ CLI11_NODISCARD T center() const { return center_point; }
196
+ CLI11_NODISCARD T range() const { return delta; }
197
+ void center(const T &value) { center_point = value; }
198
+ void range(const T &value) { delta = value; }
199
+ };
200
+
201
+ int main(int argc, char **argv) {
202
+ /* this application creates custom validator which is a range center+/- range The center and range can be defined by
203
+ * other command line options and are updated dynamically
204
+ */
205
+ CLI::App app("custom range validator");
206
+
207
+ std::string value;
208
+ auto dr = std::make_shared<DeltaRange<int>>(7, 3);
209
+ app.add_option("--number", value, "enter value in the related range")->check(dr)->required();
210
+
211
+ app.add_option_function<int>("--center", [&dr](int new_center) { dr->center(new_center); })->trigger_on_parse();
212
+ app.add_option_function<int>("--range", [&dr](int new_range) { dr->range(new_range); })->trigger_on_parse();
213
+
214
+ CLI11_PARSE(app, argc, argv);
215
+
216
+ std::cout << "number " << value << " in range = " << dr->center() << " +/- " << dr->range() << '\n';
217
+
218
+ return 0;
219
+ }
220
+ ```
221
+
222
+ The Validator defines some new operations, and in the use case the Validator is
223
+ constructed using shared_ptrs. This allows polymorphism to work and the
224
+ Validator instance to be shared across multiple options, and as in this example
225
+ adapted during the parsing and checking.
226
+
227
+ There are a few limitation in this, single instances should not be used with
228
+ both transform and check. Check modifies some flags in the Validator to prevent
229
+ value modification, so that would prevent its use as a transform. Which could be
230
+ user modified later but that would potentially allow the check to modify the
231
+ value unintentionally.
0 commit comments