@@ -71,6 +71,15 @@ The built-in validators for CLI11 are:
7171| ` ExistingPath ` | Check for an existing path |
7272| ` NonexistentPath ` | Check for an non-existing path |
7373| ` 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 |
7483
7584And, the protected members that you can set when you make your own are:
7685
@@ -82,3 +91,141 @@ And, the protected members that you can set when you make your own are:
8291| ` int ` (` -1 ` ) | ` application_index_ ` | The element this validator applies to (-1 for all) |
8392| ` bool ` (` true ` ) | ` active_ ` | This can be disabled |
8493| ` 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