Commit 17ddce2fb2e76d538ec449c395ae9ae0a36ca215
Committed by
Henry Schreiner
1 parent
ba7aac9c
Add classification type traits (#286)
This cleans up the type checking a bit and makes it more readable, along with some other cleanup. * start work on trying to clean up the type traits for which lexical cast overload to use * fix readme issue and make the condition tests a little clearer * add a check for out of range errors on boolean conversions * Fix capitalization and some comments on option functions * fix a few code analysis warnings for VS2019
Showing
6 changed files
with
171 additions
and
75 deletions
README.md
| ... | ... | @@ -604,7 +604,7 @@ The subcommand method |
| 604 | 604 | .add_option_group(name,description) |
| 605 | 605 | ``` |
| 606 | 606 | |
| 607 | -Will create an option group, and return a pointer to it. An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements. They allow specific sets of options to be composed and controlled as a collective. For an example see [range test](./tests/ranges.cpp). Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups. Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through | |
| 607 | +Will create an option group, and return a pointer to it. An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements. They allow specific sets of options to be composed and controlled as a collective. For an example see [range example](https://github.com/CLIUtils/CLI11/blob/master/examples/ranges.cpp). Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups. Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through | |
| 608 | 608 | |
| 609 | 609 | ```cpp |
| 610 | 610 | ogroup->add_option(option_pointer); | ... | ... |
include/CLI/App.hpp
| ... | ... | @@ -1292,7 +1292,7 @@ class App { |
| 1292 | 1292 | } |
| 1293 | 1293 | |
| 1294 | 1294 | std::vector<std::string> args; |
| 1295 | - args.reserve(static_cast<size_t>(argc - 1)); | |
| 1295 | + args.reserve(static_cast<size_t>(argc) - 1); | |
| 1296 | 1296 | for(int i = argc - 1; i > 0; i--) |
| 1297 | 1297 | args.emplace_back(argv[i]); |
| 1298 | 1298 | parse(std::move(args)); |
| ... | ... | @@ -2317,7 +2317,7 @@ class App { |
| 2317 | 2317 | |
| 2318 | 2318 | // LCOV_EXCL_START |
| 2319 | 2319 | default: |
| 2320 | - HorribleError("unrecognized classifier (you should not see this!)"); | |
| 2320 | + throw HorribleError("unrecognized classifier (you should not see this!)"); | |
| 2321 | 2321 | // LCOV_EXCL_END |
| 2322 | 2322 | } |
| 2323 | 2323 | return retval; | ... | ... |
include/CLI/Option.hpp
| ... | ... | @@ -527,7 +527,7 @@ class Option : public OptionBase<Option> { |
| 527 | 527 | return this; |
| 528 | 528 | } |
| 529 | 529 | |
| 530 | - /// disable flag overrides | |
| 530 | + /// Disable flag overrides values, e.g. --flag=<value> is not allowed | |
| 531 | 531 | Option *disable_flag_override(bool value = true) { |
| 532 | 532 | disable_flag_override_ = value; |
| 533 | 533 | return this; |
| ... | ... | @@ -564,7 +564,7 @@ class Option : public OptionBase<Option> { |
| 564 | 564 | /// Get the short names |
| 565 | 565 | const std::vector<std::string> get_snames() const { return snames_; } |
| 566 | 566 | |
| 567 | - /// get the flag names with specified default values | |
| 567 | + /// Get the flag names with specified default values | |
| 568 | 568 | const std::vector<std::string> get_fnames() const { return fnames_; } |
| 569 | 569 | |
| 570 | 570 | /// The number of times the option expects to be included |
| ... | ... | @@ -790,6 +790,7 @@ class Option : public OptionBase<Option> { |
| 790 | 790 | return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0); |
| 791 | 791 | } |
| 792 | 792 | |
| 793 | + /// Get the value that goes for a flag, nominally gets the default value but allows for overrides if not disabled | |
| 793 | 794 | std::string get_flag_value(std::string name, std::string input_value) const { |
| 794 | 795 | static const std::string trueString{"true"}; |
| 795 | 796 | static const std::string falseString{"false"}; |
| ... | ... | @@ -855,7 +856,7 @@ class Option : public OptionBase<Option> { |
| 855 | 856 | /// Get a copy of the results |
| 856 | 857 | std::vector<std::string> results() const { return results_; } |
| 857 | 858 | |
| 858 | - /// get the results as a particular type | |
| 859 | + /// Get the results as a specified type | |
| 859 | 860 | template <typename T, |
| 860 | 861 | enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy> |
| 861 | 862 | void results(T &output) const { |
| ... | ... | @@ -884,7 +885,7 @@ class Option : public OptionBase<Option> { |
| 884 | 885 | throw ConversionError(get_name(), results_); |
| 885 | 886 | } |
| 886 | 887 | } |
| 887 | - /// get the results as a vector of a particular type | |
| 888 | + /// Get the results as a vector of the specified type | |
| 888 | 889 | template <typename T> void results(std::vector<T> &output) const { |
| 889 | 890 | output.clear(); |
| 890 | 891 | bool retval = true; |
| ... | ... | @@ -899,7 +900,7 @@ class Option : public OptionBase<Option> { |
| 899 | 900 | } |
| 900 | 901 | } |
| 901 | 902 | |
| 902 | - /// return the results as a particular type | |
| 903 | + /// Return the results as the specified type | |
| 903 | 904 | template <typename T> T as() const { |
| 904 | 905 | T output; |
| 905 | 906 | results(output); |
| ... | ... | @@ -980,7 +981,7 @@ class Option : public OptionBase<Option> { |
| 980 | 981 | } |
| 981 | 982 | |
| 982 | 983 | private: |
| 983 | - // run through the validators | |
| 984 | + // Run a result through the validators | |
| 984 | 985 | std::string _validate(std::string &result) { |
| 985 | 986 | std::string err_msg; |
| 986 | 987 | for(const auto &vali : validators_) { |
| ... | ... | @@ -995,6 +996,7 @@ class Option : public OptionBase<Option> { |
| 995 | 996 | return err_msg; |
| 996 | 997 | } |
| 997 | 998 | |
| 999 | + /// Add a single result to the result set, taking into account delimiters | |
| 998 | 1000 | int _add_result(std::string &&result) { |
| 999 | 1001 | int result_count = 0; |
| 1000 | 1002 | if(delimiter_ == '\0') { | ... | ... |
include/CLI/TypeTools.hpp
| ... | ... | @@ -228,6 +228,118 @@ std::string checked_to_string(T &&) { |
| 228 | 228 | return std::string{}; |
| 229 | 229 | } |
| 230 | 230 | |
| 231 | +// Enumeration of the different supported categorizations of objects | |
| 232 | +enum objCategory : int { | |
| 233 | + integral_value = 2, | |
| 234 | + unsigned_integral = 4, | |
| 235 | + enumeration = 6, | |
| 236 | + boolean_value = 8, | |
| 237 | + floating_point = 10, | |
| 238 | + number_constructible = 12, | |
| 239 | + double_constructible = 14, | |
| 240 | + integer_constructible = 16, | |
| 241 | + vector_value = 30, | |
| 242 | + // string assignable or greater used in a condition so anything string like must come last | |
| 243 | + string_assignable = 50, | |
| 244 | + string_constructible = 60, | |
| 245 | + other = 200, | |
| 246 | + | |
| 247 | +}; | |
| 248 | + | |
| 249 | +/// some type that is not otherwise recognized | |
| 250 | +template <typename T, typename Enable = void> struct classify_object { static constexpr objCategory value{other}; }; | |
| 251 | + | |
| 252 | +/// Set of overloads to classify an object according to type | |
| 253 | +template <typename T> | |
| 254 | +struct classify_object<T, | |
| 255 | + typename std::enable_if<std::is_integral<T>::value && std::is_signed<T>::value && | |
| 256 | + !is_bool<T>::value && !std::is_enum<T>::value>::type> { | |
| 257 | + static constexpr objCategory value{integral_value}; | |
| 258 | +}; | |
| 259 | + | |
| 260 | +/// Unsigned integers | |
| 261 | +template <typename T> | |
| 262 | +struct classify_object< | |
| 263 | + T, | |
| 264 | + typename std::enable_if<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value>::type> { | |
| 265 | + static constexpr objCategory value{unsigned_integral}; | |
| 266 | +}; | |
| 267 | + | |
| 268 | +/// Boolean values | |
| 269 | +template <typename T> struct classify_object<T, typename std::enable_if<is_bool<T>::value>::type> { | |
| 270 | + static constexpr objCategory value{boolean_value}; | |
| 271 | +}; | |
| 272 | + | |
| 273 | +/// Floats | |
| 274 | +template <typename T> struct classify_object<T, typename std::enable_if<std::is_floating_point<T>::value>::type> { | |
| 275 | + static constexpr objCategory value{floating_point}; | |
| 276 | +}; | |
| 277 | + | |
| 278 | +/// String and similar direct assignment | |
| 279 | +template <typename T> | |
| 280 | +struct classify_object< | |
| 281 | + T, | |
| 282 | + typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 283 | + std::is_assignable<T &, std::string>::value && !is_vector<T>::value>::type> { | |
| 284 | + static constexpr objCategory value{string_assignable}; | |
| 285 | +}; | |
| 286 | + | |
| 287 | +/// String and similar constructible and copy assignment | |
| 288 | +template <typename T> | |
| 289 | +struct classify_object< | |
| 290 | + T, | |
| 291 | + typename std::enable_if<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 292 | + !std::is_assignable<T &, std::string>::value && | |
| 293 | + std::is_constructible<T, std::string>::value && !is_vector<T>::value>::type> { | |
| 294 | + static constexpr objCategory value{string_constructible}; | |
| 295 | +}; | |
| 296 | + | |
| 297 | +/// Enumerations | |
| 298 | +template <typename T> struct classify_object<T, typename std::enable_if<std::is_enum<T>::value>::type> { | |
| 299 | + static constexpr objCategory value{enumeration}; | |
| 300 | +}; | |
| 301 | + | |
| 302 | +/// Handy helper to contain a bunch of checks that rule out many common types (integers, string like, floating point, | |
| 303 | +/// vectors, and enumerations | |
| 304 | +template <typename T> struct uncommon_type { | |
| 305 | + using type = typename std::conditional<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 306 | + !std::is_assignable<T &, std::string>::value && | |
| 307 | + !std::is_constructible<T, std::string>::value && !is_vector<T>::value && | |
| 308 | + !std::is_enum<T>::value, | |
| 309 | + std::true_type, | |
| 310 | + std::false_type>::type; | |
| 311 | + static const bool value = type::value; | |
| 312 | +}; | |
| 313 | + | |
| 314 | +/// Assignable from double or int | |
| 315 | +template <typename T> | |
| 316 | +struct classify_object<T, | |
| 317 | + typename std::enable_if<uncommon_type<T>::value && is_direct_constructible<T, double>::value && | |
| 318 | + is_direct_constructible<T, int>::value>::type> { | |
| 319 | + static constexpr objCategory value{number_constructible}; | |
| 320 | +}; | |
| 321 | + | |
| 322 | +/// Assignable from int | |
| 323 | +template <typename T> | |
| 324 | +struct classify_object<T, | |
| 325 | + typename std::enable_if<uncommon_type<T>::value && !is_direct_constructible<T, double>::value && | |
| 326 | + is_direct_constructible<T, int>::value>::type> { | |
| 327 | + static const objCategory value{integer_constructible}; | |
| 328 | +}; | |
| 329 | + | |
| 330 | +/// Assignable from double | |
| 331 | +template <typename T> | |
| 332 | +struct classify_object<T, | |
| 333 | + typename std::enable_if<uncommon_type<T>::value && is_direct_constructible<T, double>::value && | |
| 334 | + !is_direct_constructible<T, int>::value>::type> { | |
| 335 | + static const objCategory value{double_constructible}; | |
| 336 | +}; | |
| 337 | + | |
| 338 | +/// vector type | |
| 339 | +template <typename T> struct classify_object<T, typename std::enable_if<is_vector<T>::value>::type> { | |
| 340 | + static const objCategory value{vector_value}; | |
| 341 | +}; | |
| 342 | + | |
| 231 | 343 | // Type name print |
| 232 | 344 | |
| 233 | 345 | /// Was going to be based on |
| ... | ... | @@ -235,38 +347,45 @@ std::string checked_to_string(T &&) { |
| 235 | 347 | /// But this is cleaner and works better in this case |
| 236 | 348 | |
| 237 | 349 | template <typename T, |
| 238 | - enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, detail::enabler> = detail::dummy> | |
| 350 | + enable_if_t<classify_object<T>::value == integral_value || classify_object<T>::value == integer_constructible, | |
| 351 | + detail::enabler> = detail::dummy> | |
| 239 | 352 | constexpr const char *type_name() { |
| 240 | 353 | return "INT"; |
| 241 | 354 | } |
| 242 | 355 | |
| 243 | -template <typename T, | |
| 244 | - enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value, detail::enabler> = detail::dummy> | |
| 356 | +template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy> | |
| 245 | 357 | constexpr const char *type_name() { |
| 246 | 358 | return "UINT"; |
| 247 | 359 | } |
| 248 | 360 | |
| 249 | -template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy> | |
| 361 | +template < | |
| 362 | + typename T, | |
| 363 | + enable_if_t<classify_object<T>::value == floating_point || classify_object<T>::value == number_constructible || | |
| 364 | + classify_object<T>::value == double_constructible, | |
| 365 | + detail::enabler> = detail::dummy> | |
| 250 | 366 | constexpr const char *type_name() { |
| 251 | 367 | return "FLOAT"; |
| 252 | 368 | } |
| 253 | 369 | |
| 254 | 370 | /// This one should not be used, since vector types print the internal type |
| 255 | -template <typename T, enable_if_t<is_vector<T>::value, detail::enabler> = detail::dummy> | |
| 371 | +template <typename T, enable_if_t<classify_object<T>::value == vector_value, detail::enabler> = detail::dummy> | |
| 256 | 372 | constexpr const char *type_name() { |
| 257 | 373 | return "VECTOR"; |
| 258 | 374 | } |
| 259 | 375 | /// Print name for enumeration types |
| 260 | -template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy> | |
| 376 | +template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy> | |
| 261 | 377 | constexpr const char *type_name() { |
| 262 | 378 | return "ENUM"; |
| 263 | 379 | } |
| 264 | 380 | |
| 381 | +/// Print name for enumeration types | |
| 382 | +template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy> | |
| 383 | +constexpr const char *type_name() { | |
| 384 | + return "BOOLEAN"; | |
| 385 | +} | |
| 386 | + | |
| 265 | 387 | /// Print for all other types |
| 266 | -template <typename T, | |
| 267 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && !is_vector<T>::value && | |
| 268 | - !std::is_enum<T>::value, | |
| 269 | - detail::enabler> = detail::dummy> | |
| 388 | +template <typename T, enable_if_t<classify_object<T>::value >= string_assignable, detail::enabler> = detail::dummy> | |
| 270 | 389 | constexpr const char *type_name() { |
| 271 | 390 | return "TEXT"; |
| 272 | 391 | } |
| ... | ... | @@ -286,6 +405,9 @@ inline int64_t to_flag_value(std::string val) { |
| 286 | 405 | val = detail::to_lower(val); |
| 287 | 406 | int64_t ret; |
| 288 | 407 | if(val.size() == 1) { |
| 408 | + if(val[0] >= '1' && val[0] <= '9') { | |
| 409 | + return (static_cast<int64_t>(val[0]) - '0'); | |
| 410 | + } | |
| 289 | 411 | switch(val[0]) { |
| 290 | 412 | case '0': |
| 291 | 413 | case 'f': |
| ... | ... | @@ -293,22 +415,11 @@ inline int64_t to_flag_value(std::string val) { |
| 293 | 415 | case '-': |
| 294 | 416 | ret = -1; |
| 295 | 417 | break; |
| 296 | - case '1': | |
| 297 | 418 | case 't': |
| 298 | 419 | case 'y': |
| 299 | 420 | case '+': |
| 300 | 421 | ret = 1; |
| 301 | 422 | break; |
| 302 | - case '2': | |
| 303 | - case '3': | |
| 304 | - case '4': | |
| 305 | - case '5': | |
| 306 | - case '6': | |
| 307 | - case '7': | |
| 308 | - case '8': | |
| 309 | - case '9': | |
| 310 | - ret = val[0] - '0'; | |
| 311 | - break; | |
| 312 | 423 | default: |
| 313 | 424 | throw std::invalid_argument("unrecognized character"); |
| 314 | 425 | } |
| ... | ... | @@ -325,10 +436,7 @@ inline int64_t to_flag_value(std::string val) { |
| 325 | 436 | } |
| 326 | 437 | |
| 327 | 438 | /// Signed integers |
| 328 | -template < | |
| 329 | - typename T, | |
| 330 | - enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value && !is_bool<T>::value && !std::is_enum<T>::value, | |
| 331 | - detail::enabler> = detail::dummy> | |
| 439 | +template <typename T, enable_if_t<classify_object<T>::value == integral_value, detail::enabler> = detail::dummy> | |
| 332 | 440 | bool lexical_cast(const std::string &input, T &output) { |
| 333 | 441 | try { |
| 334 | 442 | size_t n = 0; |
| ... | ... | @@ -343,9 +451,7 @@ bool lexical_cast(const std::string &input, T &output) { |
| 343 | 451 | } |
| 344 | 452 | |
| 345 | 453 | /// Unsigned integers |
| 346 | -template <typename T, | |
| 347 | - enable_if_t<std::is_integral<T>::value && std::is_unsigned<T>::value && !is_bool<T>::value, detail::enabler> = | |
| 348 | - detail::dummy> | |
| 454 | +template <typename T, enable_if_t<classify_object<T>::value == unsigned_integral, detail::enabler> = detail::dummy> | |
| 349 | 455 | bool lexical_cast(const std::string &input, T &output) { |
| 350 | 456 | if(!input.empty() && input.front() == '-') |
| 351 | 457 | return false; // std::stoull happily converts negative values to junk without any errors. |
| ... | ... | @@ -363,7 +469,7 @@ bool lexical_cast(const std::string &input, T &output) { |
| 363 | 469 | } |
| 364 | 470 | |
| 365 | 471 | /// Boolean values |
| 366 | -template <typename T, enable_if_t<is_bool<T>::value, detail::enabler> = detail::dummy> | |
| 472 | +template <typename T, enable_if_t<classify_object<T>::value == boolean_value, detail::enabler> = detail::dummy> | |
| 367 | 473 | bool lexical_cast(const std::string &input, T &output) { |
| 368 | 474 | try { |
| 369 | 475 | auto out = to_flag_value(input); |
| ... | ... | @@ -371,11 +477,16 @@ bool lexical_cast(const std::string &input, T &output) { |
| 371 | 477 | return true; |
| 372 | 478 | } catch(const std::invalid_argument &) { |
| 373 | 479 | return false; |
| 480 | + } catch(const std::out_of_range &) { | |
| 481 | + // if the number is out of the range of a 64 bit value then it is still a number and for this purpose is still | |
| 482 | + // valid all we care about the sign | |
| 483 | + output = (input[0] != '-'); | |
| 484 | + return true; | |
| 374 | 485 | } |
| 375 | 486 | } |
| 376 | 487 | |
| 377 | 488 | /// Floats |
| 378 | -template <typename T, enable_if_t<std::is_floating_point<T>::value, detail::enabler> = detail::dummy> | |
| 489 | +template <typename T, enable_if_t<classify_object<T>::value == floating_point, detail::enabler> = detail::dummy> | |
| 379 | 490 | bool lexical_cast(const std::string &input, T &output) { |
| 380 | 491 | try { |
| 381 | 492 | size_t n = 0; |
| ... | ... | @@ -389,27 +500,21 @@ bool lexical_cast(const std::string &input, T &output) { |
| 389 | 500 | } |
| 390 | 501 | |
| 391 | 502 | /// String and similar direct assignment |
| 392 | -template <typename T, | |
| 393 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 394 | - std::is_assignable<T &, std::string>::value, | |
| 395 | - detail::enabler> = detail::dummy> | |
| 503 | +template <typename T, enable_if_t<classify_object<T>::value == string_assignable, detail::enabler> = detail::dummy> | |
| 396 | 504 | bool lexical_cast(const std::string &input, T &output) { |
| 397 | 505 | output = input; |
| 398 | 506 | return true; |
| 399 | 507 | } |
| 400 | 508 | |
| 401 | 509 | /// String and similar constructible and copy assignment |
| 402 | -template <typename T, | |
| 403 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 404 | - !std::is_assignable<T &, std::string>::value && std::is_constructible<T, std::string>::value, | |
| 405 | - detail::enabler> = detail::dummy> | |
| 510 | +template <typename T, enable_if_t<classify_object<T>::value == string_constructible, detail::enabler> = detail::dummy> | |
| 406 | 511 | bool lexical_cast(const std::string &input, T &output) { |
| 407 | 512 | output = T(input); |
| 408 | 513 | return true; |
| 409 | 514 | } |
| 410 | 515 | |
| 411 | 516 | /// Enumerations |
| 412 | -template <typename T, enable_if_t<std::is_enum<T>::value, detail::enabler> = detail::dummy> | |
| 517 | +template <typename T, enable_if_t<classify_object<T>::value == enumeration, detail::enabler> = detail::dummy> | |
| 413 | 518 | bool lexical_cast(const std::string &input, T &output) { |
| 414 | 519 | typename std::underlying_type<T>::type val; |
| 415 | 520 | bool retval = detail::lexical_cast(input, val); |
| ... | ... | @@ -421,12 +526,7 @@ bool lexical_cast(const std::string &input, T &output) { |
| 421 | 526 | } |
| 422 | 527 | |
| 423 | 528 | /// Assignable from double or int |
| 424 | -template <typename T, | |
| 425 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 426 | - !std::is_assignable<T &, std::string>::value && | |
| 427 | - !std::is_constructible<T, std::string>::value && !std::is_enum<T>::value && | |
| 428 | - is_direct_constructible<T, double>::value && is_direct_constructible<T, int>::value, | |
| 429 | - detail::enabler> = detail::dummy> | |
| 529 | +template <typename T, enable_if_t<classify_object<T>::value == number_constructible, detail::enabler> = detail::dummy> | |
| 430 | 530 | bool lexical_cast(const std::string &input, T &output) { |
| 431 | 531 | int val; |
| 432 | 532 | if(lexical_cast(input, val)) { |
| ... | ... | @@ -442,13 +542,8 @@ bool lexical_cast(const std::string &input, T &output) { |
| 442 | 542 | return from_stream(input, output); |
| 443 | 543 | } |
| 444 | 544 | |
| 445 | -/// Assignable from int64 | |
| 446 | -template <typename T, | |
| 447 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 448 | - !std::is_assignable<T &, std::string>::value && | |
| 449 | - !std::is_constructible<T, std::string>::value && !std::is_enum<T>::value && | |
| 450 | - !is_direct_constructible<T, double>::value && is_direct_constructible<T, int>::value, | |
| 451 | - detail::enabler> = detail::dummy> | |
| 545 | +/// Assignable from int | |
| 546 | +template <typename T, enable_if_t<classify_object<T>::value == integer_constructible, detail::enabler> = detail::dummy> | |
| 452 | 547 | bool lexical_cast(const std::string &input, T &output) { |
| 453 | 548 | int val; |
| 454 | 549 | if(lexical_cast(input, val)) { |
| ... | ... | @@ -459,12 +554,7 @@ bool lexical_cast(const std::string &input, T &output) { |
| 459 | 554 | } |
| 460 | 555 | |
| 461 | 556 | /// Assignable from double |
| 462 | -template <typename T, | |
| 463 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 464 | - !std::is_assignable<T &, std::string>::value && | |
| 465 | - !std::is_constructible<T, std::string>::value && !std::is_enum<T>::value && | |
| 466 | - is_direct_constructible<T, double>::value && !is_direct_constructible<T, int>::value, | |
| 467 | - detail::enabler> = detail::dummy> | |
| 557 | +template <typename T, enable_if_t<classify_object<T>::value == double_constructible, detail::enabler> = detail::dummy> | |
| 468 | 558 | bool lexical_cast(const std::string &input, T &output) { |
| 469 | 559 | double val; |
| 470 | 560 | if(lexical_cast(input, val)) { |
| ... | ... | @@ -475,15 +565,10 @@ bool lexical_cast(const std::string &input, T &output) { |
| 475 | 565 | } |
| 476 | 566 | |
| 477 | 567 | /// Non-string parsable by a stream |
| 478 | -template <typename T, | |
| 479 | - enable_if_t<!std::is_floating_point<T>::value && !std::is_integral<T>::value && | |
| 480 | - !std::is_assignable<T &, std::string>::value && | |
| 481 | - !std::is_constructible<T, std::string>::value && !std::is_enum<T>::value && | |
| 482 | - !is_direct_constructible<T, double>::value && !is_direct_constructible<T, int>::value, | |
| 483 | - detail::enabler> = detail::dummy> | |
| 568 | +template <typename T, enable_if_t<classify_object<T>::value == other, detail::enabler> = detail::dummy> | |
| 484 | 569 | bool lexical_cast(const std::string &input, T &output) { |
| 485 | 570 | static_assert(is_istreamable<T>::value, |
| 486 | - "option object type must have a lexical cast overload or streaming input operator(>>) defined if it " | |
| 571 | + "option object type must have a lexical cast overload or streaming input operator(>>) defined, if it " | |
| 487 | 572 | "is convertible from another type use the add_option<T, XC>(...) with XC being the known type"); |
| 488 | 573 | return from_stream(input, output); |
| 489 | 574 | } | ... | ... |
tests/AppTest.cpp
| ... | ... | @@ -647,6 +647,15 @@ TEST_F(TApp, BoolOption) { |
| 647 | 647 | args = {"-b", "-7"}; |
| 648 | 648 | run(); |
| 649 | 649 | EXPECT_FALSE(bflag); |
| 650 | + | |
| 651 | + // cause an out of bounds error internally | |
| 652 | + args = {"-b", "751615654161688126132138844896646748852"}; | |
| 653 | + run(); | |
| 654 | + EXPECT_TRUE(bflag); | |
| 655 | + | |
| 656 | + args = {"-b", "-751615654161688126132138844896646748852"}; | |
| 657 | + run(); | |
| 658 | + EXPECT_FALSE(bflag); | |
| 650 | 659 | } |
| 651 | 660 | |
| 652 | 661 | TEST_F(TApp, ShortOpts) { | ... | ... |
tests/OptionGroupTest.cpp
| ... | ... | @@ -622,7 +622,7 @@ TEST_F(ManyGroups, Moving) { |
| 622 | 622 | } |
| 623 | 623 | |
| 624 | 624 | struct ManyGroupsPreTrigger : public ManyGroups { |
| 625 | - size_t triggerMain, trigger1{87u}, trigger2{34u}, trigger3{27u}; | |
| 625 | + size_t triggerMain{0u}, trigger1{87u}, trigger2{34u}, trigger3{27u}; | |
| 626 | 626 | ManyGroupsPreTrigger() { |
| 627 | 627 | remove_required(); |
| 628 | 628 | app.preparse_callback([this](size_t count) { triggerMain = count; }); | ... | ... |