Commit 17ddce2fb2e76d538ec449c395ae9ae0a36ca215

Authored by Philip Top
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
README.md
@@ -604,7 +604,7 @@ The subcommand method @@ -604,7 +604,7 @@ The subcommand method
604 .add_option_group(name,description) 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 ```cpp 609 ```cpp
610 ogroup->add_option(option_pointer); 610 ogroup->add_option(option_pointer);
include/CLI/App.hpp
@@ -1292,7 +1292,7 @@ class App { @@ -1292,7 +1292,7 @@ class App {
1292 } 1292 }
1293 1293
1294 std::vector<std::string> args; 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 for(int i = argc - 1; i > 0; i--) 1296 for(int i = argc - 1; i > 0; i--)
1297 args.emplace_back(argv[i]); 1297 args.emplace_back(argv[i]);
1298 parse(std::move(args)); 1298 parse(std::move(args));
@@ -2317,7 +2317,7 @@ class App { @@ -2317,7 +2317,7 @@ class App {
2317 2317
2318 // LCOV_EXCL_START 2318 // LCOV_EXCL_START
2319 default: 2319 default:
2320 - HorribleError("unrecognized classifier (you should not see this!)"); 2320 + throw HorribleError("unrecognized classifier (you should not see this!)");
2321 // LCOV_EXCL_END 2321 // LCOV_EXCL_END
2322 } 2322 }
2323 return retval; 2323 return retval;
include/CLI/Option.hpp
@@ -527,7 +527,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -527,7 +527,7 @@ class Option : public OptionBase&lt;Option&gt; {
527 return this; 527 return this;
528 } 528 }
529 529
530 - /// disable flag overrides 530 + /// Disable flag overrides values, e.g. --flag=<value> is not allowed
531 Option *disable_flag_override(bool value = true) { 531 Option *disable_flag_override(bool value = true) {
532 disable_flag_override_ = value; 532 disable_flag_override_ = value;
533 return this; 533 return this;
@@ -564,7 +564,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -564,7 +564,7 @@ class Option : public OptionBase&lt;Option&gt; {
564 /// Get the short names 564 /// Get the short names
565 const std::vector<std::string> get_snames() const { return snames_; } 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 const std::vector<std::string> get_fnames() const { return fnames_; } 568 const std::vector<std::string> get_fnames() const { return fnames_; }
569 569
570 /// The number of times the option expects to be included 570 /// The number of times the option expects to be included
@@ -790,6 +790,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -790,6 +790,7 @@ class Option : public OptionBase&lt;Option&gt; {
790 return (detail::find_member(name, fnames_, ignore_case_, ignore_underscore_) >= 0); 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 std::string get_flag_value(std::string name, std::string input_value) const { 794 std::string get_flag_value(std::string name, std::string input_value) const {
794 static const std::string trueString{"true"}; 795 static const std::string trueString{"true"};
795 static const std::string falseString{"false"}; 796 static const std::string falseString{"false"};
@@ -855,7 +856,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -855,7 +856,7 @@ class Option : public OptionBase&lt;Option&gt; {
855 /// Get a copy of the results 856 /// Get a copy of the results
856 std::vector<std::string> results() const { return results_; } 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 template <typename T, 860 template <typename T,
860 enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy> 861 enable_if_t<!is_vector<T>::value && !std::is_const<T>::value, detail::enabler> = detail::dummy>
861 void results(T &output) const { 862 void results(T &output) const {
@@ -884,7 +885,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -884,7 +885,7 @@ class Option : public OptionBase&lt;Option&gt; {
884 throw ConversionError(get_name(), results_); 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 template <typename T> void results(std::vector<T> &output) const { 889 template <typename T> void results(std::vector<T> &output) const {
889 output.clear(); 890 output.clear();
890 bool retval = true; 891 bool retval = true;
@@ -899,7 +900,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -899,7 +900,7 @@ class Option : public OptionBase&lt;Option&gt; {
899 } 900 }
900 } 901 }
901 902
902 - /// return the results as a particular type 903 + /// Return the results as the specified type
903 template <typename T> T as() const { 904 template <typename T> T as() const {
904 T output; 905 T output;
905 results(output); 906 results(output);
@@ -980,7 +981,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -980,7 +981,7 @@ class Option : public OptionBase&lt;Option&gt; {
980 } 981 }
981 982
982 private: 983 private:
983 - // run through the validators 984 + // Run a result through the validators
984 std::string _validate(std::string &result) { 985 std::string _validate(std::string &result) {
985 std::string err_msg; 986 std::string err_msg;
986 for(const auto &vali : validators_) { 987 for(const auto &vali : validators_) {
@@ -995,6 +996,7 @@ class Option : public OptionBase&lt;Option&gt; { @@ -995,6 +996,7 @@ class Option : public OptionBase&lt;Option&gt; {
995 return err_msg; 996 return err_msg;
996 } 997 }
997 998
  999 + /// Add a single result to the result set, taking into account delimiters
998 int _add_result(std::string &&result) { 1000 int _add_result(std::string &&result) {
999 int result_count = 0; 1001 int result_count = 0;
1000 if(delimiter_ == '\0') { 1002 if(delimiter_ == '\0') {
include/CLI/TypeTools.hpp
@@ -228,6 +228,118 @@ std::string checked_to_string(T &amp;&amp;) { @@ -228,6 +228,118 @@ std::string checked_to_string(T &amp;&amp;) {
228 return std::string{}; 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 // Type name print 343 // Type name print
232 344
233 /// Was going to be based on 345 /// Was going to be based on
@@ -235,38 +347,45 @@ std::string checked_to_string(T &amp;&amp;) { @@ -235,38 +347,45 @@ std::string checked_to_string(T &amp;&amp;) {
235 /// But this is cleaner and works better in this case 347 /// But this is cleaner and works better in this case
236 348
237 template <typename T, 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 constexpr const char *type_name() { 352 constexpr const char *type_name() {
240 return "INT"; 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 constexpr const char *type_name() { 357 constexpr const char *type_name() {
246 return "UINT"; 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 constexpr const char *type_name() { 366 constexpr const char *type_name() {
251 return "FLOAT"; 367 return "FLOAT";
252 } 368 }
253 369
254 /// This one should not be used, since vector types print the internal type 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 constexpr const char *type_name() { 372 constexpr const char *type_name() {
257 return "VECTOR"; 373 return "VECTOR";
258 } 374 }
259 /// Print name for enumeration types 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 constexpr const char *type_name() { 377 constexpr const char *type_name() {
262 return "ENUM"; 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 /// Print for all other types 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 constexpr const char *type_name() { 389 constexpr const char *type_name() {
271 return "TEXT"; 390 return "TEXT";
272 } 391 }
@@ -286,6 +405,9 @@ inline int64_t to_flag_value(std::string val) { @@ -286,6 +405,9 @@ inline int64_t to_flag_value(std::string val) {
286 val = detail::to_lower(val); 405 val = detail::to_lower(val);
287 int64_t ret; 406 int64_t ret;
288 if(val.size() == 1) { 407 if(val.size() == 1) {
  408 + if(val[0] >= '1' && val[0] <= '9') {
  409 + return (static_cast<int64_t>(val[0]) - '0');
  410 + }
289 switch(val[0]) { 411 switch(val[0]) {
290 case '0': 412 case '0':
291 case 'f': 413 case 'f':
@@ -293,22 +415,11 @@ inline int64_t to_flag_value(std::string val) { @@ -293,22 +415,11 @@ inline int64_t to_flag_value(std::string val) {
293 case '-': 415 case '-':
294 ret = -1; 416 ret = -1;
295 break; 417 break;
296 - case '1':  
297 case 't': 418 case 't':
298 case 'y': 419 case 'y':
299 case '+': 420 case '+':
300 ret = 1; 421 ret = 1;
301 break; 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 default: 423 default:
313 throw std::invalid_argument("unrecognized character"); 424 throw std::invalid_argument("unrecognized character");
314 } 425 }
@@ -325,10 +436,7 @@ inline int64_t to_flag_value(std::string val) { @@ -325,10 +436,7 @@ inline int64_t to_flag_value(std::string val) {
325 } 436 }
326 437
327 /// Signed integers 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 bool lexical_cast(const std::string &input, T &output) { 440 bool lexical_cast(const std::string &input, T &output) {
333 try { 441 try {
334 size_t n = 0; 442 size_t n = 0;
@@ -343,9 +451,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -343,9 +451,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
343 } 451 }
344 452
345 /// Unsigned integers 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 bool lexical_cast(const std::string &input, T &output) { 455 bool lexical_cast(const std::string &input, T &output) {
350 if(!input.empty() && input.front() == '-') 456 if(!input.empty() && input.front() == '-')
351 return false; // std::stoull happily converts negative values to junk without any errors. 457 return false; // std::stoull happily converts negative values to junk without any errors.
@@ -363,7 +469,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -363,7 +469,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
363 } 469 }
364 470
365 /// Boolean values 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 bool lexical_cast(const std::string &input, T &output) { 473 bool lexical_cast(const std::string &input, T &output) {
368 try { 474 try {
369 auto out = to_flag_value(input); 475 auto out = to_flag_value(input);
@@ -371,11 +477,16 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -371,11 +477,16 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
371 return true; 477 return true;
372 } catch(const std::invalid_argument &) { 478 } catch(const std::invalid_argument &) {
373 return false; 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 /// Floats 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 bool lexical_cast(const std::string &input, T &output) { 490 bool lexical_cast(const std::string &input, T &output) {
380 try { 491 try {
381 size_t n = 0; 492 size_t n = 0;
@@ -389,27 +500,21 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -389,27 +500,21 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
389 } 500 }
390 501
391 /// String and similar direct assignment 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 bool lexical_cast(const std::string &input, T &output) { 504 bool lexical_cast(const std::string &input, T &output) {
397 output = input; 505 output = input;
398 return true; 506 return true;
399 } 507 }
400 508
401 /// String and similar constructible and copy assignment 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 bool lexical_cast(const std::string &input, T &output) { 511 bool lexical_cast(const std::string &input, T &output) {
407 output = T(input); 512 output = T(input);
408 return true; 513 return true;
409 } 514 }
410 515
411 /// Enumerations 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 bool lexical_cast(const std::string &input, T &output) { 518 bool lexical_cast(const std::string &input, T &output) {
414 typename std::underlying_type<T>::type val; 519 typename std::underlying_type<T>::type val;
415 bool retval = detail::lexical_cast(input, val); 520 bool retval = detail::lexical_cast(input, val);
@@ -421,12 +526,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -421,12 +526,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
421 } 526 }
422 527
423 /// Assignable from double or int 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 bool lexical_cast(const std::string &input, T &output) { 530 bool lexical_cast(const std::string &input, T &output) {
431 int val; 531 int val;
432 if(lexical_cast(input, val)) { 532 if(lexical_cast(input, val)) {
@@ -442,13 +542,8 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -442,13 +542,8 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
442 return from_stream(input, output); 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 bool lexical_cast(const std::string &input, T &output) { 547 bool lexical_cast(const std::string &input, T &output) {
453 int val; 548 int val;
454 if(lexical_cast(input, val)) { 549 if(lexical_cast(input, val)) {
@@ -459,12 +554,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -459,12 +554,7 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
459 } 554 }
460 555
461 /// Assignable from double 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 bool lexical_cast(const std::string &input, T &output) { 558 bool lexical_cast(const std::string &input, T &output) {
469 double val; 559 double val;
470 if(lexical_cast(input, val)) { 560 if(lexical_cast(input, val)) {
@@ -475,15 +565,10 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) { @@ -475,15 +565,10 @@ bool lexical_cast(const std::string &amp;input, T &amp;output) {
475 } 565 }
476 566
477 /// Non-string parsable by a stream 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 bool lexical_cast(const std::string &input, T &output) { 569 bool lexical_cast(const std::string &input, T &output) {
485 static_assert(is_istreamable<T>::value, 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 "is convertible from another type use the add_option<T, XC>(...) with XC being the known type"); 572 "is convertible from another type use the add_option<T, XC>(...) with XC being the known type");
488 return from_stream(input, output); 573 return from_stream(input, output);
489 } 574 }
tests/AppTest.cpp
@@ -647,6 +647,15 @@ TEST_F(TApp, BoolOption) { @@ -647,6 +647,15 @@ TEST_F(TApp, BoolOption) {
647 args = {"-b", "-7"}; 647 args = {"-b", "-7"};
648 run(); 648 run();
649 EXPECT_FALSE(bflag); 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 TEST_F(TApp, ShortOpts) { 661 TEST_F(TApp, ShortOpts) {
tests/OptionGroupTest.cpp
@@ -622,7 +622,7 @@ TEST_F(ManyGroups, Moving) { @@ -622,7 +622,7 @@ TEST_F(ManyGroups, Moving) {
622 } 622 }
623 623
624 struct ManyGroupsPreTrigger : public ManyGroups { 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 ManyGroupsPreTrigger() { 626 ManyGroupsPreTrigger() {
627 remove_required(); 627 remove_required();
628 app.preparse_callback([this](size_t count) { triggerMain = count; }); 628 app.preparse_callback([this](size_t count) { triggerMain = count; });