Commit 494a65f8120145d5e923c8c62668d9e4d1f414d3
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.
Showing
11 changed files
with
181 additions
and
664 deletions
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) { |