Commit 494a65f8120145d5e923c8c62668d9e4d1f414d3

Authored by Henry Fredrick Schreiner
1 parent c7dadfc5

Dropping Make syntax, moving to pointers from combiners, structured errors.

This mostly is cleanup, with fewer alternative methods and more standard syntax, avoiding the use of the namespace all the time. Validators are simpler and are added through `->check()`.

Defaults are automatic, and can be specified with a final arg to the options.

Expected arguments and required arguments are now accessed through a pointer to option.

Option now can be checked as a bool to see if the argument was passed.

Errors have better organisation.
CMakeLists.txt
@@ -20,14 +20,15 @@ add_compile_options(-pedantic -Wall -Wextra) @@ -20,14 +20,15 @@ add_compile_options(-pedantic -Wall -Wextra)
20 add_library(CLI INTERFACE) 20 add_library(CLI INTERFACE)
21 target_include_directories(CLI INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") 21 target_include_directories(CLI INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include")
22 22
  23 +file(GLOB CLI_headers "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/*")
  24 +
23 # Single file test 25 # Single file test
24 option(CLI_SINGLE_FILE "Generate a single header file (and test)" ${CUR_PROJ}) 26 option(CLI_SINGLE_FILE "Generate a single header file (and test)" ${CUR_PROJ})
25 if(CLI_SINGLE_FILE) 27 if(CLI_SINGLE_FILE)
26 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include") 28 file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
27 add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" 29 add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
28 COMMAND python "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" 30 COMMAND python "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py" "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
29 - DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp"  
30 - IMPLICIT_DEPENDS CXX "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" 31 + DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI_headers}
31 ) 32 )
32 add_custom_target(generate_cli_single_file 33 add_custom_target(generate_cli_single_file
33 DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp") 34 DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
examples/try.cpp
@@ -12,7 +12,7 @@ int main (int argc, char** argv) { @@ -12,7 +12,7 @@ int main (int argc, char** argv) {
12 app.add_flag("-c,--count", count, "Counter"); 12 app.add_flag("-c,--count", count, "Counter");
13 13
14 double value = 3.14; 14 double value = 3.14;
15 - app.add_option("-d,--double", value, "Some Value", CLI::Default); 15 + app.add_option("-d,--double", value, "Some Value", false);
16 16
17 try { 17 try {
18 app.run(argc, argv); 18 app.run(argc, argv);
include/CLI/App.hpp
@@ -20,9 +20,7 @@ @@ -20,9 +20,7 @@
20 #include "CLI/TypeTools.hpp" 20 #include "CLI/TypeTools.hpp"
21 #include "CLI/StringTools.hpp" 21 #include "CLI/StringTools.hpp"
22 #include "CLI/Split.hpp" 22 #include "CLI/Split.hpp"
23 -#include "CLI/Combiner.hpp"  
24 #include "CLI/Option.hpp" 23 #include "CLI/Option.hpp"
25 -#include "CLI/Value.hpp"  
26 24
27 namespace CLI { 25 namespace CLI {
28 26
@@ -92,8 +90,6 @@ public: @@ -92,8 +90,6 @@ public:
92 } 90 }
93 91
94 92
95 - //------------ ADD STYLE ---------//  
96 -  
97 /// Add an option, will automatically understand the type for common types. 93 /// Add an option, will automatically understand the type for common types.
98 /** To use, create a variable with the expected type, and pass it in after the name. 94 /** To use, create a variable with the expected type, and pass it in after the name.
99 * After start is called, you can use count to see if the value was passed, and 95 * After start is called, you can use count to see if the value was passed, and
@@ -111,9 +107,9 @@ public: @@ -111,9 +107,9 @@ public:
111 std::string name, 107 std::string name,
112 callback_t callback, 108 callback_t callback,
113 std::string description="", 109 std::string description="",
114 - detail::Combiner opts=Validators 110 + bool defaulted=true
115 ) { 111 ) {
116 - Option myopt{name, description, opts, callback}; 112 + Option myopt{name, description, callback, defaulted};
117 if(std::find(std::begin(options), std::end(options), myopt) == std::end(options)) 113 if(std::find(std::begin(options), std::end(options), myopt) == std::end(options))
118 options.push_back(myopt); 114 options.push_back(myopt);
119 else 115 else
@@ -128,12 +124,10 @@ public: @@ -128,12 +124,10 @@ public:
128 std::string name, 124 std::string name,
129 T &variable, ///< The variable to set 125 T &variable, ///< The variable to set
130 std::string description="", 126 std::string description="",
131 - detail::Combiner opts=Validators 127 + bool defaulted=true
132 ) { 128 ) {
133 129
134 130
135 - if(opts.num!=1)  
136 - throw IncorrectConstruction("Must have Args(1) or be a vector.");  
137 CLI::callback_t fun = [&variable](CLI::results_t res){ 131 CLI::callback_t fun = [&variable](CLI::results_t res){
138 if(res.size()!=1) { 132 if(res.size()!=1) {
139 return false; 133 return false;
@@ -144,9 +138,9 @@ public: @@ -144,9 +138,9 @@ public:
144 return detail::lexical_cast(res[0][0], variable); 138 return detail::lexical_cast(res[0][0], variable);
145 }; 139 };
146 140
147 - Option* retval = add_option(name, fun, description, opts); 141 + Option* retval = add_option(name, fun, description, defaulted);
148 retval->typeval = detail::type_name<T>(); 142 retval->typeval = detail::type_name<T>();
149 - if(opts.defaulted) { 143 + if(defaulted) {
150 std::stringstream out; 144 std::stringstream out;
151 out << variable; 145 out << variable;
152 retval->defaultval = out.str(); 146 retval->defaultval = out.str();
@@ -160,11 +154,9 @@ public: @@ -160,11 +154,9 @@ public:
160 std::string name, 154 std::string name,
161 std::vector<T> &variable, ///< The variable vector to set 155 std::vector<T> &variable, ///< The variable vector to set
162 std::string description="", 156 std::string description="",
163 - detail::Combiner opts=Args 157 + bool defaulted=true
164 ) { 158 ) {
165 159
166 - if(opts.num==0)  
167 - throw IncorrectConstruction("Must have Args or be a vector.");  
168 CLI::callback_t fun = [&variable](CLI::results_t res){ 160 CLI::callback_t fun = [&variable](CLI::results_t res){
169 bool retval = true; 161 bool retval = true;
170 variable.clear(); 162 variable.clear();
@@ -176,27 +168,16 @@ public: @@ -176,27 +168,16 @@ public:
176 return variable.size() > 0 && retval; 168 return variable.size() > 0 && retval;
177 }; 169 };
178 170
179 - Option* retval = add_option(name, fun, description, opts); 171 + Option* retval = add_option(name, fun, description, defaulted);
  172 + retval->allow_vector = true;
  173 + retval->_expected = -1;
180 retval->typeval = detail::type_name<T>(); 174 retval->typeval = detail::type_name<T>();
181 - if(opts.defaulted) { 175 + if(defaulted)
182 retval->defaultval = "[" + detail::join(variable) + "]"; 176 retval->defaultval = "[" + detail::join(variable) + "]";
183 - }  
184 return retval; 177 return retval;
185 } 178 }
186 179
187 180
188 - /// Multiple options are supported  
189 - template<typename T, typename... Args>  
190 - Option* add_option(  
191 - std::string name,  
192 - T &variable, ///< The variable to set  
193 - std::string description,  
194 - detail::Combiner opts,  
195 - detail::Combiner opts2,  
196 - Args... args ///< More options  
197 - ) {  
198 - return add_option(name, variable, description, opts|opts2, args...);  
199 - }  
200 /// Add option for flag 181 /// Add option for flag
201 Option* add_flag( 182 Option* add_flag(
202 std::string name, 183 std::string name,
@@ -206,9 +187,10 @@ public: @@ -206,9 +187,10 @@ public:
206 return true; 187 return true;
207 }; 188 };
208 189
209 - Option* opt = add_option(name, fun, description, Nothing);  
210 - if(opt->positional()) 190 + Option* opt = add_option(name, fun, description, false);
  191 + if(opt->get_positional())
211 throw IncorrectConstruction("Flags cannot be positional"); 192 throw IncorrectConstruction("Flags cannot be positional");
  193 + opt->_expected = 0;
212 return opt; 194 return opt;
213 } 195 }
214 196
@@ -227,9 +209,10 @@ public: @@ -227,9 +209,10 @@ public:
227 return true; 209 return true;
228 }; 210 };
229 211
230 - Option* opt = add_option(name, fun, description, Nothing);  
231 - if(opt->positional()) 212 + Option* opt = add_option(name, fun, description, false);
  213 + if(opt->get_positional())
232 throw IncorrectConstruction("Flags cannot be positional"); 214 throw IncorrectConstruction("Flags cannot be positional");
  215 + opt->_expected = 0;
233 return opt; 216 return opt;
234 } 217 }
235 218
@@ -248,9 +231,10 @@ public: @@ -248,9 +231,10 @@ public:
248 return res.size() == 1; 231 return res.size() == 1;
249 }; 232 };
250 233
251 - Option* opt = add_option(name, fun, description, Nothing);  
252 - if(opt->positional()) 234 + Option* opt = add_option(name, fun, description, false);
  235 + if(opt->get_positional())
253 throw IncorrectConstruction("Flags cannot be positional"); 236 throw IncorrectConstruction("Flags cannot be positional");
  237 + opt->_expected = 0;
254 return opt; 238 return opt;
255 } 239 }
256 240
@@ -262,12 +246,9 @@ public: @@ -262,12 +246,9 @@ public:
262 T &member, ///< The selected member of the set 246 T &member, ///< The selected member of the set
263 std::set<T> options, ///< The set of posibilities 247 std::set<T> options, ///< The set of posibilities
264 std::string description="", 248 std::string description="",
265 - detail::Combiner opts=Validators 249 + bool defaulted=true
266 ) { 250 ) {
267 251
268 - if(opts.num!=1)  
269 - throw IncorrectConstruction("Must have Args(1).");  
270 -  
271 CLI::callback_t fun = [&member, options](CLI::results_t res){ 252 CLI::callback_t fun = [&member, options](CLI::results_t res){
272 if(res.size()!=1) { 253 if(res.size()!=1) {
273 return false; 254 return false;
@@ -281,223 +262,16 @@ public: @@ -281,223 +262,16 @@ public:
281 return std::find(std::begin(options), std::end(options), member) != std::end(options); 262 return std::find(std::begin(options), std::end(options), member) != std::end(options);
282 }; 263 };
283 264
284 - Option* retval = add_option(name, fun, description, opts); 265 + Option* retval = add_option(name, fun, description, defaulted);
285 retval->typeval = detail::type_name<T>(); 266 retval->typeval = detail::type_name<T>();
286 retval->typeval += " in {" + detail::join(options) + "}"; 267 retval->typeval += " in {" + detail::join(options) + "}";
287 - if(opts.defaulted) {  
288 - std::stringstream out;  
289 - out << member;  
290 - retval->defaultval = out.str();  
291 - } 268 + std::stringstream out;
  269 + out << member;
  270 + retval->defaultval = out.str();
292 return retval; 271 return retval;
293 } 272 }
294 273
295 274
296 - template<typename T, typename... Args>  
297 - Option* add_set(  
298 - std::string name,  
299 - T &member,  
300 - std::set<T> options, ///< The set of posibilities  
301 - std::string description,  
302 - detail::Combiner opts,  
303 - detail::Combiner opts2,  
304 - Args... args  
305 - ) {  
306 - return add_set(name, member, options, description, opts|opts2, args...);  
307 - }  
308 -  
309 -  
310 - //------------ MAKE STYLE ---------//  
311 -  
312 - /// Prototype for new output style  
313 - template<typename T = std::string,  
314 - enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>  
315 - Value<T> make_option(  
316 - std::string name,  
317 - std::string description="",  
318 - detail::Combiner opts=Validators  
319 - ) {  
320 -  
321 - if(opts.num!=1)  
322 - throw IncorrectConstruction("Must have Args(1).");  
323 -  
324 - Value<T> out(name);  
325 - std::shared_ptr<std::unique_ptr<T>> ptr = out.value;  
326 -  
327 - CLI::callback_t fun = [ptr](CLI::results_t res){  
328 - if(res.size()!=1) {  
329 - return false;  
330 - }  
331 - if(res[0].size()!=1) {  
332 - return false;  
333 - }  
334 - ptr->reset(new T()); // resets the internal ptr  
335 - return detail::lexical_cast(res[0][0], **ptr);  
336 - };  
337 - Option* retval = add_option(name, fun, description, opts);  
338 - retval->typeval = detail::type_name<T>();  
339 - return out;  
340 - }  
341 -  
342 - template<typename T = std::string, typename... Args>  
343 - Value<T> make_option(  
344 - std::string name,  
345 - std::string description,  
346 - detail::Combiner opts,  
347 - detail::Combiner opts2,  
348 - Args... args  
349 - ) {  
350 - return make_option(name, description, opts|opts2, args...);  
351 - }  
352 -  
353 - /// Prototype for new output style with default  
354 - template<typename T,  
355 - enable_if_t<!is_vector<T>::value, detail::enabler> = detail::dummy>  
356 - Value<T> make_option(  
357 - std::string name,  
358 - const T& default_value,  
359 - std::string description="",  
360 - detail::Combiner opts=Validators  
361 - ) {  
362 -  
363 - if(opts.num!=1)  
364 - throw IncorrectConstruction("Must have Args(1).");  
365 -  
366 - Value<T> out(name);  
367 - std::shared_ptr<std::unique_ptr<T>> ptr = out.value;  
368 - ptr->reset(new T(default_value)); // resets the internal ptr  
369 -  
370 - CLI::callback_t fun = [ptr](CLI::results_t res){  
371 - if(res.size()!=1) {  
372 - return false;  
373 - }  
374 - if(res[0].size()!=1) {  
375 - return false;  
376 - }  
377 - ptr->reset(new T()); // resets the internal ptr  
378 - return detail::lexical_cast(res[0][0], **ptr);  
379 - };  
380 - Option* retval = add_option(name, fun, description, opts);  
381 - retval->typeval = detail::type_name<T>();  
382 - std::stringstream ot;  
383 - ot << default_value;  
384 - retval->defaultval = ot.str();  
385 - return out;  
386 - }  
387 -  
388 - /// Prototype for new output style, vector  
389 - template<typename T,  
390 - enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy>  
391 - Value<T> make_option(  
392 - std::string name,  
393 - std::string description="",  
394 - detail::Combiner opts=Args  
395 - ) {  
396 -  
397 - if(opts.num==0)  
398 - throw IncorrectConstruction("Must have Args or be a vector.");  
399 -  
400 - Value<T> out(name);  
401 - std::shared_ptr<std::unique_ptr<T>> ptr = out.value;  
402 -  
403 - CLI::callback_t fun = [ptr](CLI::results_t res){  
404 - ptr->reset(new T()); // resets the internal ptr  
405 - bool retval = true;  
406 - for(const auto &a : res)  
407 - for(const auto &b : a) {  
408 - (*ptr)->emplace_back();  
409 - retval &= detail::lexical_cast(b, (*ptr)->back());  
410 - }  
411 - return (*ptr)->size() > 0 && retval;  
412 - };  
413 - Option* retval = add_option(name, fun, description, opts);  
414 - retval->typeval = detail::type_name<T>();  
415 - return out;  
416 - }  
417 -  
418 -  
419 - template<typename T, typename... Args>  
420 - Value<T> make_option(  
421 - std::string name,  
422 - const T& default_value,  
423 - std::string description,  
424 - detail::Combiner opts,  
425 - detail::Combiner opts2,  
426 - Args... args  
427 - ) {  
428 - return make_option(name, default_value, description, opts|opts2, args...);  
429 - }  
430 -  
431 - /// Prototype for new output style: flag  
432 - Value<int> make_flag(  
433 - std::string name,  
434 - std::string description=""  
435 - ) {  
436 -  
437 - Value<int> out(name);  
438 - std::shared_ptr<std::unique_ptr<int>> ptr = out.value;  
439 - ptr->reset(new int()); // resets the internal ptr  
440 - **ptr = 0;  
441 -  
442 - CLI::callback_t fun = [ptr](CLI::results_t res){  
443 - **ptr = (int) res.size();  
444 - return true;  
445 - };  
446 -  
447 - Option* opt = add_option(name, fun, description, Nothing);  
448 - if(opt->positional())  
449 - throw IncorrectConstruction("Flags cannot be positional");  
450 - return out;  
451 - }  
452 -  
453 - /// Add set of options  
454 - template<typename T>  
455 - Value<T> make_set(  
456 - std::string name,  
457 - std::set<T> options, ///< The set of posibilities  
458 - std::string description="",  
459 - detail::Combiner opts=Validators  
460 - ) {  
461 -  
462 - Value<T> out(name);  
463 - std::shared_ptr<std::unique_ptr<T>> ptr = out.value;  
464 -  
465 - if(opts.num!=1)  
466 - throw IncorrectConstruction("Must have Args(1).");  
467 -  
468 - CLI::callback_t fun = [ptr, options](CLI::results_t res){  
469 - if(res.size()!=1) {  
470 - return false;  
471 - }  
472 - if(res[0].size()!=1) {  
473 - return false;  
474 - }  
475 - ptr->reset(new T());  
476 - bool retval = detail::lexical_cast(res[0][0], **ptr);  
477 - if(!retval)  
478 - return false;  
479 - return std::find(std::begin(options), std::end(options), **ptr) != std::end(options);  
480 - };  
481 -  
482 - Option* retval = add_option(name, fun, description, opts);  
483 - retval->typeval = detail::type_name<T>();  
484 - retval->typeval += " in {" + detail::join(options) + "}";  
485 - return out;  
486 - }  
487 -  
488 -  
489 - template<typename T, typename... Args>  
490 - Value<T> make_set(  
491 - std::string name,  
492 - std::set<T> options,  
493 - std::string description,  
494 - detail::Combiner opts,  
495 - detail::Combiner opts2,  
496 - Args... args  
497 - ) {  
498 - return make_set(name, options, description, opts|opts2, args...);  
499 - }  
500 -  
501 /// This allows subclasses to inject code before callbacks but after parse 275 /// This allows subclasses to inject code before callbacks but after parse
502 virtual void pre_callback() {} 276 virtual void pre_callback() {}
503 277
@@ -510,6 +284,7 @@ public: @@ -510,6 +284,7 @@ public:
510 parse(args); 284 parse(args);
511 } 285 }
512 286
  287 + /// The real work is done here. Expects a reversed vector
513 void parse(std::vector<std::string> & args) { 288 void parse(std::vector<std::string> & args) {
514 parsed = true; 289 parsed = true;
515 290
@@ -546,16 +321,16 @@ public: @@ -546,16 +321,16 @@ public:
546 321
547 322
548 for(Option& opt : options) { 323 for(Option& opt : options) {
549 - while (opt.positional() && opt.count() < opt.expected() && positionals.size() > 0) { 324 + while (opt.get_positional() && opt.count() < opt.get_expected() && positionals.size() > 0) {
550 opt.get_new(); 325 opt.get_new();
551 opt.add_result(0, positionals.front()); 326 opt.add_result(0, positionals.front());
552 positionals.pop_front(); 327 positionals.pop_front();
553 } 328 }
554 - if (opt.required() && opt.count() < opt.expected()) 329 + if (opt.get_required() && opt.count() < opt.get_expected())
555 throw RequiredError(opt.get_name()); 330 throw RequiredError(opt.get_name());
556 if (opt.count() > 0) { 331 if (opt.count() > 0) {
557 if(!opt.run_callback()) 332 if(!opt.run_callback())
558 - throw ParseError(opt.get_name()); 333 + throw ConversionError(opt.get_name());
559 } 334 }
560 335
561 } 336 }
@@ -595,7 +370,7 @@ public: @@ -595,7 +370,7 @@ public:
595 } 370 }
596 371
597 int vnum = op->get_new(); 372 int vnum = op->get_new();
598 - int num = op->expected(); 373 + int num = op->get_expected();
599 374
600 if(num == 0) 375 if(num == 0)
601 op->add_result(vnum, ""); 376 op->add_result(vnum, "");
@@ -660,7 +435,7 @@ public: @@ -660,7 +435,7 @@ public:
660 435
661 436
662 int vnum = op->get_new(); 437 int vnum = op->get_new();
663 - int num = op->expected(); 438 + int num = op->get_expected();
664 439
665 440
666 if(value != "") { 441 if(value != "") {
@@ -742,7 +517,7 @@ public: @@ -742,7 +517,7 @@ public:
742 // Positionals 517 // Positionals
743 bool pos=false; 518 bool pos=false;
744 for(const Option &opt : options) 519 for(const Option &opt : options)
745 - if(opt.positional()) { 520 + if(opt.get_positional()) {
746 out << " " << opt.help_positional(); 521 out << " " << opt.help_positional();
747 if(opt.has_description()) 522 if(opt.has_description())
748 pos=true; 523 pos=true;
@@ -754,7 +529,7 @@ public: @@ -754,7 +529,7 @@ public:
754 if(pos) { 529 if(pos) {
755 out << "Positionals:" << std::endl; 530 out << "Positionals:" << std::endl;
756 for(const Option &opt : options) 531 for(const Option &opt : options)
757 - if(opt.positional() && opt.has_description()) 532 + if(opt.get_positional() && opt.has_description())
758 detail::format_help(out, opt.get_pname(), opt.get_description(), wid); 533 detail::format_help(out, opt.get_pname(), opt.get_description(), wid);
759 out << std::endl; 534 out << std::endl;
760 535
include/CLI/CLI.hpp
@@ -8,8 +8,7 @@ @@ -8,8 +8,7 @@
8 #include "CLI/TypeTools.hpp" 8 #include "CLI/TypeTools.hpp"
9 #include "CLI/StringTools.hpp" 9 #include "CLI/StringTools.hpp"
10 #include "CLI/Split.hpp" 10 #include "CLI/Split.hpp"
11 -#include "CLI/Combiner.hpp" 11 +#include "CLI/Validators.hpp"
12 #include "CLI/Option.hpp" 12 #include "CLI/Option.hpp"
13 -#include "CLI/Value.hpp"  
14 #include "CLI/App.hpp" 13 #include "CLI/App.hpp"
15 14
include/CLI/Error.hpp
@@ -11,54 +11,74 @@ namespace CLI { @@ -11,54 +11,74 @@ namespace CLI {
11 11
12 // Error definitions 12 // Error definitions
13 13
14 - 14 +/// All errors derive from this one
15 struct Error : public std::runtime_error { 15 struct Error : public std::runtime_error {
16 int exit_code; 16 int exit_code;
17 bool print_help; 17 bool print_help;
18 Error(std::string parent, std::string name, int exit_code=255, bool print_help=true) : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {} 18 Error(std::string parent, std::string name, int exit_code=255, bool print_help=true) : runtime_error(parent + ": " + name), exit_code(exit_code), print_help(print_help) {}
19 }; 19 };
20 20
  21 +/// This is a successful completion on parsing, supposed to exit
21 struct Success : public Error { 22 struct Success : public Error {
22 Success() : Error("Success", "Successfully completed, should be caught and quit", 0, false) {} 23 Success() : Error("Success", "Successfully completed, should be caught and quit", 0, false) {}
23 }; 24 };
24 25
  26 +/// -h or --help on command line
25 struct CallForHelp : public Error { 27 struct CallForHelp : public Error {
26 CallForHelp() : Error("CallForHelp", "This should be caught in your main function, see examples", 0) {} 28 CallForHelp() : Error("CallForHelp", "This should be caught in your main function, see examples", 0) {}
27 }; 29 };
28 30
29 -struct BadNameString : public Error {  
30 - BadNameString(std::string name) : Error("BadNameString", name, 1) {} 31 +// Construction errors (not in parsing)
  32 +
  33 +struct ConstructionError : public Error {
  34 + using Error::Error;
31 }; 35 };
32 36
  37 +/// Thrown when an option is set to conflicting values (non-vector and multi args, for example)
  38 +struct IncorrectConstruction : public ConstructionError {
  39 + IncorrectConstruction(std::string name) : ConstructionError("ConstructionError", name, 8) {}
  40 +};
33 41
34 -struct ParseError : public Error {  
35 - ParseError(std::string name) : Error("ParseError", name, 2) {} 42 +/// Thrown on construction of a bad name
  43 +struct BadNameString : public ConstructionError {
  44 + BadNameString(std::string name) : ConstructionError("BadNameString", name, 1) {}
36 }; 45 };
37 46
38 -struct OptionAlreadyAdded : public Error {  
39 - OptionAlreadyAdded(std::string name) : Error("OptionAlreadyAdded", name, 3) {} 47 +/// Thrown when an option already exists
  48 +struct OptionAlreadyAdded : public ConstructionError {
  49 + OptionAlreadyAdded(std::string name) : ConstructionError("OptionAlreadyAdded", name, 3) {}
40 }; 50 };
41 51
42 -struct OptionNotFound : public Error {  
43 - OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {} 52 +// Parsing errors
  53 +
  54 +struct ParseError : public Error {
  55 + using Error::Error;
44 }; 56 };
45 57
46 -struct RequiredError : public Error {  
47 - RequiredError(std::string name) : Error("RequiredError", name, 5) {} 58 +/// Thrown when conversion call back fails, such as when an int fails to coerse to a string
  59 +struct ConversionError : public ParseError {
  60 + ConversionError(std::string name) : ParseError("ConversionError", name, 2) {}
48 }; 61 };
49 62
50 -struct PositionalError : public Error {  
51 - PositionalError(std::string name) : Error("PositionalError", name, 6) {} 63 +/// Thrown when a required option is missing
  64 +struct RequiredError : public ParseError {
  65 + RequiredError(std::string name) : ParseError("RequiredError", name, 5) {}
52 }; 66 };
53 67
54 -struct HorribleError : public Error {  
55 - HorribleError(std::string name) : Error("HorribleError", "(You should never see this error) " + name, 7) {} 68 +/// Thrown when too many positionals are found
  69 +struct PositionalError : public ParseError {
  70 + PositionalError(std::string name) : ParseError("PositionalError", name, 6) {}
56 }; 71 };
57 -struct IncorrectConstruction : public Error {  
58 - IncorrectConstruction(std::string name) : Error("IncorrectConstruction", name, 8) {} 72 +
  73 +/// This is just a safety check to verify selection and parsing match
  74 +struct HorribleError : public ParseError {
  75 + HorribleError(std::string name) : ParseError("HorribleError", "(You should never see this error) " + name, 7) {}
59 }; 76 };
60 -struct EmptyError : public Error {  
61 - EmptyError(std::string name) : Error("EmptyError", name, 9) {} 77 +
  78 +/// Thrown when counting a non-existent option
  79 +struct OptionNotFound : public Error {
  80 + OptionNotFound(std::string name) : Error("OptionNotFound", name, 4) {}
62 }; 81 };
63 82
  83 +
64 } 84 }
include/CLI/Option.hpp
@@ -9,16 +9,15 @@ @@ -9,16 +9,15 @@
9 #include <tuple> 9 #include <tuple>
10 #include <algorithm> 10 #include <algorithm>
11 11
  12 +#include "CLI/Error.hpp"
12 #include "CLI/StringTools.hpp" 13 #include "CLI/StringTools.hpp"
13 #include "CLI/Split.hpp" 14 #include "CLI/Split.hpp"
14 -#include "CLI/Combiner.hpp"  
15 15
16 namespace CLI { 16 namespace CLI {
17 17
18 typedef std::vector<std::vector<std::string>> results_t; 18 typedef std::vector<std::vector<std::string>> results_t;
19 typedef std::function<bool(results_t)> callback_t; 19 typedef std::function<bool(results_t)> callback_t;
20 20
21 -  
22 class App; 21 class App;
23 22
24 class Option { 23 class Option {
@@ -29,7 +28,6 @@ protected: @@ -29,7 +28,6 @@ protected:
29 std::vector<std::string> lnames; 28 std::vector<std::string> lnames;
30 std::string pname; 29 std::string pname;
31 30
32 - detail::Combiner opts;  
33 std::string description; 31 std::string description;
34 callback_t callback; 32 callback_t callback;
35 33
@@ -37,33 +35,66 @@ protected: @@ -37,33 +35,66 @@ protected:
37 std::string defaultval; 35 std::string defaultval;
38 std::string typeval; 36 std::string typeval;
39 37
  38 +
  39 + bool _default {false};
  40 + bool _required {false};
  41 + int _expected {1};
  42 + bool allow_vector {false};
  43 + std::vector<std::function<bool(std::string)>> _validators;
  44 +
40 // Results 45 // Results
41 results_t results {}; 46 results_t results {};
42 47
43 48
44 public: 49 public:
45 - Option(std::string name, std::string description = "", detail::Combiner opts=Nothing, std::function<bool(results_t)> callback=[](results_t){return true;}) :  
46 - opts(opts), description(description), callback(callback){ 50 + Option(std::string name, std::string description = "", std::function<bool(results_t)> callback=[](results_t){return true;}, bool _default=true) :
  51 + description(description), callback(callback), _default(_default) {
47 std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name)); 52 std::tie(snames, lnames, pname) = detail::get_names(detail::split_names(name));
48 } 53 }
49 54
  55 +
  56 + // This class is "true" if optio passed.
  57 + operator bool() const {
  58 + return results.size() > 0;
  59 + }
  60 +
50 /// Clear the parsed results (mostly for testing) 61 /// Clear the parsed results (mostly for testing)
51 void clear() { 62 void clear() {
52 results.clear(); 63 results.clear();
53 } 64 }
54 65
55 - /// True if option is required  
56 - bool required() const {  
57 - return opts.required; 66 + /// Set the option as required
  67 + Option* required(bool value = true) {
  68 + _required = value;
  69 + return this;
  70 + }
  71 +
  72 + bool get_required() const {
  73 + return _required;
  74 + }
  75 +
  76 + /// Set the number of expected arguments (Flags bypass this)
  77 + Option* expected(int value) {
  78 + if(value == 0)
  79 + throw IncorrectConstruction("Cannot set 0 expected, use a flag instead");
  80 + if(!allow_vector && value != 1)
  81 + throw IncorrectConstruction("You can only change the Expected arguments for vectors");
  82 + _expected = value;
  83 + return this;
58 } 84 }
59 85
60 /// The number of arguments the option expects 86 /// The number of arguments the option expects
61 - int expected() const {  
62 - return opts.num; 87 + int get_expected() const {
  88 + return _expected;
  89 + }
  90 +
  91 + /// True if this has a default value
  92 + int get_default() const {
  93 + return _default;
63 } 94 }
64 95
65 /// True if the argument can be given directly 96 /// True if the argument can be given directly
66 - bool positional() const { 97 + bool get_positional() const {
67 return pname.length() > 0; 98 return pname.length() > 0;
68 } 99 }
69 100
@@ -72,16 +103,18 @@ public: @@ -72,16 +103,18 @@ public:
72 return (snames.size() + lnames.size()) > 0; 103 return (snames.size() + lnames.size()) > 0;
73 } 104 }
74 105
75 - /// True if this should print the default string  
76 - bool defaulted() const {  
77 - return opts.defaulted;  
78 - }  
79 -  
80 /// True if option has description 106 /// True if option has description
81 bool has_description() const { 107 bool has_description() const {
82 return description.length() > 0; 108 return description.length() > 0;
83 } 109 }
84 110
  111 + /// Adds a validator
  112 + Option* check(std::function<bool(std::string)> validator) {
  113 +
  114 + _validators.push_back(validator);
  115 + return this;
  116 + }
  117 +
85 /// Get the description 118 /// Get the description
86 const std::string& get_description() const { 119 const std::string& get_description() const {
87 return description; 120 return description;
@@ -90,11 +123,11 @@ public: @@ -90,11 +123,11 @@ public:
90 /// The name and any extras needed for positionals 123 /// The name and any extras needed for positionals
91 std::string help_positional() const { 124 std::string help_positional() const {
92 std::string out = pname; 125 std::string out = pname;
93 - if(expected()<1)  
94 - out = out + "x" + std::to_string(expected());  
95 - else if(expected()==-1) 126 + if(get_expected()<1)
  127 + out = out + "x" + std::to_string(get_expected());
  128 + else if(get_expected()==-1)
96 out = out + "..."; 129 out = out + "...";
97 - out = required() ? out : "["+out+"]"; 130 + out = get_required() ? out : "["+out+"]";
98 return out; 131 return out;
99 } 132 }
100 133
@@ -105,9 +138,9 @@ public: @@ -105,9 +138,9 @@ public:
105 138
106 /// Process the callback 139 /// Process the callback
107 bool run_callback() const { 140 bool run_callback() const {
108 - if(opts.validators.size()>0) { 141 + if(_validators.size()>0) {
109 for(const std::string & result : flatten_results()) 142 for(const std::string & result : flatten_results())
110 - for(const std::function<bool(std::string)> &vali : opts.validators) 143 + for(const std::function<bool(std::string)> &vali : _validators)
111 if(!vali(result)) 144 if(!vali(result))
112 return false; 145 return false;
113 } 146 }
@@ -196,14 +229,14 @@ public: @@ -196,14 +229,14 @@ public:
196 std::string help_name() const { 229 std::string help_name() const {
197 std::stringstream out; 230 std::stringstream out;
198 out << get_name(); 231 out << get_name();
199 - if(expected() != 0) { 232 + if(get_expected() != 0) {
200 if(typeval != "") 233 if(typeval != "")
201 out << " " << typeval; 234 out << " " << typeval;
202 if(defaultval != "") 235 if(defaultval != "")
203 out << "=" << defaultval; 236 out << "=" << defaultval;
204 - if(expected() > 1)  
205 - out << " x " << expected();  
206 - if(expected() == -1) 237 + if(get_expected() > 1)
  238 + out << " x " << get_expected();
  239 + if(get_expected() == -1)
207 out << " ..."; 240 out << " ...";
208 } 241 }
209 return out.str(); 242 return out.str();
include/CLI/Combiner.hpp renamed to include/CLI/Validators.hpp
@@ -4,8 +4,6 @@ @@ -4,8 +4,6 @@
4 // file LICENSE or https://github.com/henryiii/CLI11 for details. 4 // file LICENSE or https://github.com/henryiii/CLI11 for details.
5 5
6 #include <string> 6 #include <string>
7 -#include <functional>  
8 -#include <vector>  
9 7
10 8
11 // C standard library 9 // C standard library
@@ -16,42 +14,9 @@ @@ -16,42 +14,9 @@
16 14
17 namespace CLI { 15 namespace CLI {
18 16
19 -namespace detail {  
20 -  
21 -struct Combiner {  
22 - int num;  
23 - bool required;  
24 - bool defaulted;  
25 - std::vector<std::function<bool(std::string)>> validators;  
26 -  
27 - /// Can be or-ed together  
28 - Combiner operator | (Combiner b) const {  
29 - Combiner self;  
30 - self.num = std::min(num, b.num) == -1 ? -1 : std::max(num, b.num);  
31 - self.required = required || b.required;  
32 - self.defaulted = defaulted || b.defaulted;  
33 - self.validators.reserve(validators.size() + b.validators.size());  
34 - self.validators.insert(self.validators.end(), validators.begin(), validators.end());  
35 - self.validators.insert(self.validators.end(), b.validators.begin(), b.validators.end());  
36 - return self;  
37 - }  
38 -  
39 - /// Call to give the number of arguments expected on cli  
40 - Combiner operator() (int n) const {  
41 - Combiner self = *this;  
42 - self.num = n;  
43 - return self;  
44 - }  
45 - /// Call to give a validator  
46 - Combiner operator() (std::function<bool(std::string)> func) const {  
47 - Combiner self = *this;  
48 - self.validators.push_back(func);  
49 - return self;  
50 - }  
51 -};  
52 17
53 /// Check for an existing file 18 /// Check for an existing file
54 -bool _ExistingFile(std::string filename) { 19 +bool ExistingFile(std::string filename) {
55 // std::fstream f(name.c_str()); 20 // std::fstream f(name.c_str());
56 // return f.good(); 21 // return f.good();
57 // Fastest way according to http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c 22 // Fastest way according to http://stackoverflow.com/questions/12774207/fastest-way-to-check-if-a-file-exist-using-standard-c-c11-c
@@ -60,7 +25,7 @@ bool _ExistingFile(std::string filename) { @@ -60,7 +25,7 @@ bool _ExistingFile(std::string filename) {
60 } 25 }
61 26
62 /// Check for an existing directory 27 /// Check for an existing directory
63 -bool _ExistingDirectory(std::string filename) { 28 +bool ExistingDirectory(std::string filename) {
64 struct stat buffer; 29 struct stat buffer;
65 if(stat(filename.c_str(), &buffer) == 0 && (buffer.st_mode & S_IFDIR) ) 30 if(stat(filename.c_str(), &buffer) == 0 && (buffer.st_mode & S_IFDIR) )
66 return true; 31 return true;
@@ -68,32 +33,10 @@ bool _ExistingDirectory(std::string filename) { @@ -68,32 +33,10 @@ bool _ExistingDirectory(std::string filename) {
68 } 33 }
69 34
70 /// Check for a non-existing path 35 /// Check for a non-existing path
71 -bool _NonexistentPath(std::string filename) { 36 +bool NonexistentPath(std::string filename) {
72 struct stat buffer; 37 struct stat buffer;
73 return stat(filename.c_str(), &buffer) != 0; 38 return stat(filename.c_str(), &buffer) != 0;
74 } 39 }
75 40
76 41
77 -  
78 -  
79 -}  
80 -  
81 -  
82 -  
83 -// Defines for common Combiners (don't use combiners directly)  
84 -  
85 -const detail::Combiner Nothing {0, false, false, {}};  
86 -const detail::Combiner Required {1, true, false, {}};  
87 -const detail::Combiner Default {1, false, true, {}};  
88 -const detail::Combiner Args {-1, false, false, {}};  
89 -const detail::Combiner Validators {1, false, false, {}};  
90 -  
91 -// Warning about using these validators:  
92 -// The files could be added/deleted after the validation. This is not common,  
93 -// but if this is a possibility, check the file you open afterwards  
94 -const detail::Combiner ExistingFile {1, false, false, {detail::_ExistingFile}};  
95 -const detail::Combiner ExistingDirectory {1, false, false, {detail::_ExistingDirectory}};  
96 -const detail::Combiner NonexistentPath {1, false, false, {detail::_NonexistentPath}};  
97 -  
98 -  
99 } 42 }
include/CLI/Value.hpp deleted
1 -#pragma once  
2 -  
3 -// Distributed under the LGPL version 3.0 license. See accompanying  
4 -// file LICENSE or https://github.com/henryiii/CLI11 for details.  
5 -  
6 -#include <string>  
7 -#include <memory>  
8 -  
9 -#include "CLI/Error.hpp"  
10 -  
11 -namespace CLI {  
12 -  
13 -class App;  
14 -  
15 -// Prototype return value test  
16 -template <typename T>  
17 -class Value {  
18 - friend App;  
19 -protected:  
20 - std::shared_ptr<std::unique_ptr<T>> value {new std::unique_ptr<T>()};  
21 - std::string name;  
22 -public:  
23 - Value(std::string name) : name(name) {}  
24 -  
25 - operator bool() const {return (bool) *value;}  
26 -  
27 - T& get() const {  
28 - if(*value)  
29 - return **value;  
30 - else  
31 - throw EmptyError(name);  
32 - }  
33 - /// Note this does not throw on assignment, though  
34 - /// afterwards it seems to work fine. Best to use  
35 - /// explicit * notation.  
36 - T& operator *() const {  
37 - return get();  
38 - }  
39 -};  
40 -  
41 -}  
include/Program.hpp deleted
1 -#pragma once  
2 -  
3 -#include <string>  
4 -  
5 -#include <boost/program_options.hpp>  
6 -  
7 -  
8 -// This is unreachable outside this file; you should not use Combiner directly  
9 -namespace {  
10 -  
11 -struct Combiner {  
12 - int positional;  
13 - bool required;  
14 - bool defaulted;  
15 -  
16 - /// Can be or-ed together  
17 - Combiner operator | (Combiner b) const {  
18 - Combiner self;  
19 - self.positional = positional + b.positional;  
20 - self.required = required || b.required;  
21 - self.defaulted = defaulted || b.defaulted;  
22 - return self;  
23 - }  
24 -  
25 - /// Call to give the number of arguments expected on cli  
26 - Combiner operator() (int n) const {  
27 - return Combiner{n, required, defaulted};  
28 - }  
29 - Combiner operator, (Combiner b) const {  
30 - return *this | b;  
31 - }  
32 -};  
33 -}  
34 -  
35 -  
36 -  
37 -/// Creates a command line program, with very few defaults.  
38 -/** To use, create a new Program() instance with argc, argv, and a help description. The templated  
39 -* add_option methods make it easy to prepare options. Remember to call `.start` before starting your  
40 -* program, so that the options can be evaluated and the help option doesn't accidentally run your program. */  
41 -class Program {  
42 -public:  
43 - static constexpr Combiner REQUIRED{0,true,false};  
44 - static constexpr Combiner DEFAULT{0,false,true};  
45 - static constexpr Combiner POSITIONAL{1,false,false};  
46 -  
47 -protected:  
48 - boost::program_options::options_description desc;  
49 - boost::program_options::positional_options_description p;  
50 - boost::program_options::variables_map vm;  
51 -  
52 - int argc;  
53 - char **argv;  
54 -  
55 - /// Parses the command line (internal function)  
56 - void parse() {  
57 - try {  
58 - boost::program_options::store(boost::program_options::command_line_parser(argc, argv)  
59 - .options(desc).positional(p).run(), vm);  
60 -  
61 - if(vm.count("help")){  
62 - std::cout << desc;  
63 - exit(0);  
64 - }  
65 -  
66 - boost::program_options::notify(vm);  
67 - } catch(const boost::program_options::error& e) {  
68 - std::cerr << "ERROR: " << e.what() << std::endl << std::endl;  
69 - std::cerr << desc << std::endl;  
70 - exit(1);  
71 - }  
72 - }  
73 -  
74 -  
75 -public:  
76 -  
77 - /// Create a new program. Pass in the same arguments as main(), along with a help string.  
78 - Program(int argc, char** argv, std::string discription)  
79 - : argc(argc), argv(argv), desc(discription) {  
80 - desc.add_options()  
81 - ("help,h", "Display this help message");  
82 - }  
83 -  
84 - /// Allows you to manually add options in the boost style.  
85 - /** Usually the specialized methods are easier, but this remains for people used to Boost and for  
86 - * unusual situations. */  
87 - boost::program_options::options_description_easy_init add_options() {  
88 - return desc.add_options();  
89 - }  
90 -  
91 - /// Add an option, will automatically understand the type for common types.  
92 - /** To use, create a variable with the expected type, and pass it in after the name.  
93 - * After start is called, you can use count to see if the value was passed, and  
94 - * the value will be initialized properly.  
95 - *  
96 - * Program::REQUIRED, Program::DEFAULT, and Program::POSITIONAL are options, and can be `|`  
97 - * together. The positional options take an optional number of arguments.  
98 - *  
99 - * For example,  
100 - *  
101 - * std::string filename  
102 - * program.add_option("filename", filename, "description of filename");  
103 - */  
104 - template<typename T>  
105 - void add_option(  
106 - std::string name, ///< The name, long,short  
107 - T &value, ///< The value  
108 - std::string description, ///< Discription string  
109 - Combiner options ///< The options (REQUIRED, DEFAULT, POSITIONAL)  
110 - ) {  
111 - auto po_value = boost::program_options::value<T>(&value);  
112 - if(options.defaulted)  
113 - po_value = po_value->default_value(value);  
114 - if(options.required)  
115 - po_value = po_value->required();  
116 - desc.add_options()(name.c_str(),po_value,description.c_str());  
117 - if(options.positional!=0)  
118 - p.add(name.c_str(), options.positional);  
119 - }  
120 -  
121 - /// Adds a flag style option  
122 - void add_option(std::string name, std::string description) {  
123 - desc.add_options()(name.c_str(),description.c_str());  
124 - }  
125 -  
126 -  
127 - /// This must be called after the options are in but before the rest of the program.  
128 - /** Calls the Boost boost::program_options initialization, causing the program to exit  
129 - * if -h or an invalid option is passed. */  
130 - void start() {  
131 - parse();  
132 - }  
133 -  
134 - /// Counts the number of times the given option was passed.  
135 - int count(std::string name) const {  
136 - return vm.count(name.c_str());  
137 - }  
138 -  
139 -  
140 -};  
tests/CLITest.cpp
@@ -165,7 +165,7 @@ TEST_F(TApp, BoolAndIntFlags) { @@ -165,7 +165,7 @@ TEST_F(TApp, BoolAndIntFlags) {
165 app.reset(); 165 app.reset();
166 166
167 args = {"-b", "-b"}; 167 args = {"-b", "-b"};
168 - EXPECT_THROW(run(), CLI::ParseError); 168 + EXPECT_THROW(run(), CLI::ConversionError);
169 169
170 app.reset(); 170 app.reset();
171 bflag = false; 171 bflag = false;
@@ -199,8 +199,8 @@ TEST_F(TApp, Flags) { @@ -199,8 +199,8 @@ TEST_F(TApp, Flags) {
199 int i = 3; 199 int i = 3;
200 std::string s = "HI"; 200 std::string s = "HI";
201 201
202 - app.add_option("-i,i", i, "", CLI::Default);  
203 - app.add_option("-s,s", s, "", CLI::Default); 202 + app.add_option("-i,i", i, "", false);
  203 + app.add_option("-s,s", s, "", true);
204 204
205 args = {"-i2", "9"}; 205 args = {"-i2", "9"};
206 206
@@ -276,10 +276,10 @@ TEST_F(TApp, Reset) { @@ -276,10 +276,10 @@ TEST_F(TApp, Reset) {
276 276
277 TEST_F(TApp, FileNotExists) { 277 TEST_F(TApp, FileNotExists) {
278 std::string myfile{"TestNonFileNotUsed.txt"}; 278 std::string myfile{"TestNonFileNotUsed.txt"};
279 - EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile)); 279 + EXPECT_TRUE(CLI::NonexistentPath(myfile));
280 280
281 std::string filename; 281 std::string filename;
282 - app.add_option("--file", filename, "", CLI::NonexistentPath); 282 + app.add_option("--file", filename)->check(CLI::NonexistentPath);
283 args = {"--file", myfile}; 283 args = {"--file", myfile};
284 284
285 EXPECT_NO_THROW(run()); 285 EXPECT_NO_THROW(run());
@@ -290,21 +290,21 @@ TEST_F(TApp, FileNotExists) { @@ -290,21 +290,21 @@ TEST_F(TApp, FileNotExists) {
290 290
291 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file 291 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
292 EXPECT_TRUE(ok); 292 EXPECT_TRUE(ok);
293 - EXPECT_THROW(run(), CLI::ParseError); 293 + EXPECT_THROW(run(), CLI::ConversionError);
294 294
295 std::remove(myfile.c_str()); 295 std::remove(myfile.c_str());
296 - EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); 296 + EXPECT_FALSE(CLI::ExistingFile(myfile));
297 } 297 }
298 298
299 TEST_F(TApp, FileExists) { 299 TEST_F(TApp, FileExists) {
300 std::string myfile{"TestNonFileNotUsed.txt"}; 300 std::string myfile{"TestNonFileNotUsed.txt"};
301 - EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); 301 + EXPECT_FALSE(CLI::ExistingFile(myfile));
302 302
303 std::string filename = "Failed"; 303 std::string filename = "Failed";
304 - app.add_option("--file", filename, "", CLI::ExistingFile); 304 + app.add_option("--file", filename)->check(CLI::ExistingFile);
305 args = {"--file", myfile}; 305 args = {"--file", myfile};
306 306
307 - EXPECT_THROW(run(), CLI::ParseError); 307 + EXPECT_THROW(run(), CLI::ConversionError);
308 EXPECT_EQ("Failed", filename); 308 EXPECT_EQ("Failed", filename);
309 309
310 app.reset(); 310 app.reset();
@@ -315,7 +315,7 @@ TEST_F(TApp, FileExists) { @@ -315,7 +315,7 @@ TEST_F(TApp, FileExists) {
315 EXPECT_EQ(myfile, filename); 315 EXPECT_EQ(myfile, filename);
316 316
317 std::remove(myfile.c_str()); 317 std::remove(myfile.c_str());
318 - EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); 318 + EXPECT_FALSE(CLI::ExistingFile(myfile));
319 } 319 }
320 320
321 TEST_F(TApp, InSet) { 321 TEST_F(TApp, InSet) {
@@ -331,15 +331,15 @@ TEST_F(TApp, InSet) { @@ -331,15 +331,15 @@ TEST_F(TApp, InSet) {
331 app.reset(); 331 app.reset();
332 332
333 args = {"--quick", "four"}; 333 args = {"--quick", "four"};
334 - EXPECT_THROW(run(), CLI::ParseError); 334 + EXPECT_THROW(run(), CLI::ConversionError);
335 } 335 }
336 336
337 TEST_F(TApp, VectorFixedString) { 337 TEST_F(TApp, VectorFixedString) {
338 std::vector<std::string> strvec; 338 std::vector<std::string> strvec;
339 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; 339 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
340 340
341 - CLI::Option* opt = app.add_option("-s,--string", strvec, "", CLI::Args(3));  
342 - EXPECT_EQ(3, opt->expected()); 341 + CLI::Option* opt = app.add_option("-s,--string", strvec)->expected(3);
  342 + EXPECT_EQ(3, opt->get_expected());
343 343
344 args = {"--string", "mystring", "mystring2", "mystring3"}; 344 args = {"--string", "mystring", "mystring2", "mystring3"};
345 run(); 345 run();
@@ -354,7 +354,7 @@ TEST_F(TApp, VectorUnlimString) { @@ -354,7 +354,7 @@ TEST_F(TApp, VectorUnlimString) {
354 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"}; 354 std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
355 355
356 CLI::Option* opt = app.add_option("-s,--string", strvec); 356 CLI::Option* opt = app.add_option("-s,--string", strvec);
357 - EXPECT_EQ(-1, opt->expected()); 357 + EXPECT_EQ(-1, opt->get_expected());
358 358
359 args = {"--string", "mystring", "mystring2", "mystring3"}; 359 args = {"--string", "mystring", "mystring2", "mystring3"};
360 EXPECT_NO_THROW(run()); 360 EXPECT_NO_THROW(run());
@@ -363,6 +363,27 @@ TEST_F(TApp, VectorUnlimString) { @@ -363,6 +363,27 @@ TEST_F(TApp, VectorUnlimString) {
363 } 363 }
364 364
365 365
  366 +TEST_F(TApp, VectorFancyOpts) {
  367 + std::vector<std::string> strvec;
  368 + std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
  369 +
  370 + CLI::Option* opt = app.add_option("-s,--string", strvec)->required()->expected(3);
  371 + EXPECT_EQ(3, opt->get_expected());
  372 +
  373 + args = {"--string", "mystring", "mystring2", "mystring3"};
  374 + EXPECT_NO_THROW(run());
  375 + EXPECT_EQ(3, app.count("--string"));
  376 + EXPECT_EQ(answer, strvec);
  377 +
  378 + app.reset();
  379 + args = {"one", "two"};
  380 + EXPECT_THROW(run(), CLI::RequiredError);
  381 +
  382 + app.reset();
  383 + EXPECT_THROW(run(), CLI::ConversionError);
  384 +}
  385 +
  386 +
366 387
367 TEST_F(TApp, BasicSubcommands) { 388 TEST_F(TApp, BasicSubcommands) {
368 auto sub1 = app.add_subcommand("sub1"); 389 auto sub1 = app.add_subcommand("sub1");
@@ -450,99 +471,5 @@ TEST_F(SubcommandProgram, SpareSub) { @@ -450,99 +471,5 @@ TEST_F(SubcommandProgram, SpareSub) {
450 EXPECT_THROW(run(), CLI::PositionalError); 471 EXPECT_THROW(run(), CLI::PositionalError);
451 } 472 }
452 473
453 -class TAppValue : public TApp {};  
454 -  
455 -TEST_F(TAppValue, OneString) {  
456 - auto str = app.make_option("-s,--string");  
457 - std::string v;  
458 - args = {"--string", "mystring"};  
459 - EXPECT_FALSE((bool) str);  
460 - EXPECT_THROW(v = *str, CLI::EmptyError);  
461 - //EXPECT_THROW(v = str, CLI::EmptyError);  
462 - EXPECT_FALSE((bool) str);  
463 - EXPECT_NO_THROW(run());  
464 - EXPECT_TRUE((bool) str);  
465 - EXPECT_NO_THROW(v = *str);  
466 - EXPECT_NO_THROW(v = str);  
467 -  
468 - EXPECT_EQ(1, app.count("-s"));  
469 - EXPECT_EQ(1, app.count("--string"));  
470 - EXPECT_EQ(*str, "mystring");  
471 -  
472 -}  
473 -  
474 -TEST_F(TAppValue, SeveralInts) {  
475 - auto value = app.make_option<int>("--first");  
476 - CLI::Value<int> value2 = app.make_option<int>("-s");  
477 - int v;  
478 - args = {"--first", "12", "-s", "19"};  
479 - EXPECT_FALSE((bool) value);  
480 - EXPECT_FALSE((bool) value2);  
481 -  
482 - EXPECT_THROW(v = *value, CLI::EmptyError);  
483 - //EXPECT_THROW(v = str, CLI::EmptyError);  
484 - EXPECT_NO_THROW(run());  
485 - EXPECT_TRUE((bool) value);  
486 - EXPECT_NO_THROW(v = *value);  
487 - EXPECT_NO_THROW(v = value);  
488 -  
489 - EXPECT_EQ(1, app.count("-s"));  
490 - EXPECT_EQ(1, app.count("--first"));  
491 - EXPECT_EQ(*value, 12);  
492 - EXPECT_EQ(*value2, 19);  
493 -  
494 -}  
495 -  
496 -TEST_F(TAppValue, Vector) {  
497 - auto value = app.make_option<std::vector<int>>("--first", "", CLI::Args);  
498 - auto value2 = app.make_option<std::vector<std::string>>("--second");  
499 -  
500 - std::vector<int> i;  
501 - std::vector<std::string> s;  
502 -  
503 - args = {"--first", "12", "3", "9", "--second", "thing", "try"};  
504 -  
505 - EXPECT_FALSE((bool) value);  
506 - EXPECT_FALSE((bool) value2);  
507 -  
508 - EXPECT_THROW(i = *value, CLI::EmptyError);  
509 - EXPECT_THROW(s = *value2, CLI::EmptyError);  
510 -  
511 - EXPECT_NO_THROW(run());  
512 -  
513 - EXPECT_TRUE((bool) value);  
514 - EXPECT_TRUE((bool) value2);  
515 -  
516 - EXPECT_NO_THROW(i = *value);  
517 - //EXPECT_NO_THROW(i = value);  
518 -  
519 - EXPECT_NO_THROW(s = *value2);  
520 - //EXPECT_NO_THROW(s = value2);  
521 -  
522 - EXPECT_EQ(3, app.count("--first"));  
523 - EXPECT_EQ(2, app.count("--second"));  
524 -  
525 - EXPECT_EQ(std::vector<int>({12,3,9}), *value);  
526 - EXPECT_EQ(std::vector<std::string>({"thing", "try"}), *value2);  
527 -  
528 -}  
529 -  
530 -TEST_F(TAppValue, DoubleVector) {  
531 - auto value = app.make_option<std::vector<double>>("--simple");  
532 - std::vector<double> d;  
533 -  
534 - args = {"--simple", "1.2", "3.4", "-1"};  
535 -  
536 - EXPECT_THROW(d = *value, CLI::EmptyError);  
537 -  
538 - EXPECT_NO_THROW(run());  
539 -  
540 - EXPECT_NO_THROW(d = *value);  
541 -  
542 - EXPECT_EQ(3, app.count("--simple"));  
543 - EXPECT_EQ(std::vector<double>({1.2, 3.4, -1}), *value);  
544 -}  
545 474
546 -// TODO: Check help output, better formatting  
547 -// TODO: Add default/type info to help  
548 -// TODO: Add README 475 +// TODO: Check help output and formatting
tests/SmallTest.cpp
@@ -11,24 +11,24 @@ @@ -11,24 +11,24 @@
11 11
12 TEST(Validators, FileExists) { 12 TEST(Validators, FileExists) {
13 std::string myfile{"TestFileNotUsed.txt"}; 13 std::string myfile{"TestFileNotUsed.txt"};
14 - EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); 14 + EXPECT_FALSE(CLI::ExistingFile(myfile));
15 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file 15 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
16 EXPECT_TRUE(ok); 16 EXPECT_TRUE(ok);
17 - EXPECT_TRUE(CLI::detail::_ExistingFile(myfile)); 17 + EXPECT_TRUE(CLI::ExistingFile(myfile));
18 18
19 std::remove(myfile.c_str()); 19 std::remove(myfile.c_str());
20 - EXPECT_FALSE(CLI::detail::_ExistingFile(myfile)); 20 + EXPECT_FALSE(CLI::ExistingFile(myfile));
21 } 21 }
22 22
23 TEST(Validators, FileNotExists) { 23 TEST(Validators, FileNotExists) {
24 std::string myfile{"TestFileNotUsed.txt"}; 24 std::string myfile{"TestFileNotUsed.txt"};
25 - EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile)); 25 + EXPECT_TRUE(CLI::NonexistentPath(myfile));
26 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file 26 bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a')); // create file
27 EXPECT_TRUE(ok); 27 EXPECT_TRUE(ok);
28 - EXPECT_FALSE(CLI::detail::_NonexistentPath(myfile)); 28 + EXPECT_FALSE(CLI::NonexistentPath(myfile));
29 29
30 std::remove(myfile.c_str()); 30 std::remove(myfile.c_str());
31 - EXPECT_TRUE(CLI::detail::_NonexistentPath(myfile)); 31 + EXPECT_TRUE(CLI::NonexistentPath(myfile));
32 } 32 }
33 33
34 TEST(Split, StringList) { 34 TEST(Split, StringList) {