Commit 5c35c182f634d61710661e3ee5a3c5bc1b62c915

Authored by Ondřej Čertík
Committed by GitHub
1 parent 611431ce

Use e.get_name instead of dynamic_cast (#467)

* Use e.get_name instead of dynamic_cast

Also use std::static_pointer_cast instead of std::dynamic_pointer_cast

Fixes #466

* feat: Allow RTTI to be turned off

* ci: Fix CXX flags

* doc: Adding update to book

Co-authored-by: Henry Schreiner <henryschreineriii@gmail.com>
azure-pipelines.yml
... ... @@ -63,7 +63,11 @@ jobs:
63 63 Windowslatest:
64 64 vmImage: 'windows-2019'
65 65 cli11.std: 20
66   - cli11.options: -DCMAKE_CXX_FLAG="/std:c++latest"
  66 + cli11.options: -DCMAKE_CXX_FLAGS="/std:c++latest /EHsc"
  67 + Linux17nortti:
  68 + vmImage: 'ubuntu-latest'
  69 + cli11.std: 17
  70 + cli11.options: -DCMAKE_CXX_FLAGS="-fno-rtti"
67 71 pool:
68 72 vmImage: $(vmImage)
69 73 steps:
... ...
book/chapters/config.md
... ... @@ -135,7 +135,7 @@ The default configuration file will read INI files, but will write out files in
135 135 ```cpp
136 136 app.config_formatter(std::make_shared<CLI::ConfigINI>());
137 137 ```
138   -which makes use of a predefined modification of the ConfigBase class which TOML also uses. If a custom formatter is used that is not inheriting from the from ConfigBase class `get_config_formatter_base() will return a nullptr, so some care must be exercised in its us with custom configurations.
  138 +which makes use of a predefined modification of the ConfigBase class which TOML also uses. If a custom formatter is used that is not inheriting from the from ConfigBase class `get_config_formatter_base() will return a nullptr if RTTI is on (usually the default), or garbage if RTTI is off, so some care must be exercised in its use with custom configurations.
139 139  
140 140 ## Custom formats
141 141  
... ...
include/CLI/App.hpp
... ... @@ -1374,20 +1374,20 @@ class App {
1374 1374 int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const {
1375 1375  
1376 1376 /// Avoid printing anything if this is a CLI::RuntimeError
1377   - if(dynamic_cast<const CLI::RuntimeError *>(&e) != nullptr)
  1377 + if(e.get_name() == "RuntimeError")
1378 1378 return e.get_exit_code();
1379 1379  
1380   - if(dynamic_cast<const CLI::CallForHelp *>(&e) != nullptr) {
  1380 + if(e.get_name() == "CallForHelp") {
1381 1381 out << help();
1382 1382 return e.get_exit_code();
1383 1383 }
1384 1384  
1385   - if(dynamic_cast<const CLI::CallForAllHelp *>(&e) != nullptr) {
  1385 + if(e.get_name() == "CallForAllHelp") {
1386 1386 out << help("", AppFormatMode::All);
1387 1387 return e.get_exit_code();
1388 1388 }
1389 1389  
1390   - if(dynamic_cast<const CLI::CallForVersion *>(&e) != nullptr) {
  1390 + if(e.get_name() == "CallForVersion") {
1391 1391 out << e.what() << std::endl;
1392 1392 return e.get_exit_code();
1393 1393 }
... ... @@ -1606,7 +1606,12 @@ class App {
1606 1606  
1607 1607 /// Access the config formatter as a configBase pointer
1608 1608 std::shared_ptr<ConfigBase> get_config_formatter_base() const {
  1609 + // This is safer as a dynamic_cast if we have RTTI, as Config -> ConfigBase
  1610 +#if defined(__cpp_rtti) || (defined(__GXX_RTTI) && __GXX_RTTI) || (defined(_HAS_STATIC_RTTI) && (_HAS_STATIC_RTTI == 0))
1609 1611 return std::dynamic_pointer_cast<ConfigBase>(config_formatter_);
  1612 +#else
  1613 + return std::static_pointer_cast<ConfigBase>(config_formatter_);
  1614 +#endif
1610 1615 }
1611 1616  
1612 1617 /// Get the app or subcommand description
... ...
include/CLI/Option.hpp
... ... @@ -508,7 +508,7 @@ class Option : public OptionBase&lt;Option&gt; {
508 508  
509 509 /// Can find a string if needed
510 510 template <typename T = App> Option *needs(std::string opt_name) {
511   - auto opt = dynamic_cast<T *>(parent_)->get_option_no_throw(opt_name);
  511 + auto opt = static_cast<T *>(parent_)->get_option_no_throw(opt_name);
512 512 if(opt == nullptr) {
513 513 throw IncorrectConstruction::MissingOption(opt_name);
514 514 }
... ... @@ -550,7 +550,7 @@ class Option : public OptionBase&lt;Option&gt; {
550 550  
551 551 /// Can find a string if needed
552 552 template <typename T = App> Option *excludes(std::string opt_name) {
553   - auto opt = dynamic_cast<T *>(parent_)->get_option_no_throw(opt_name);
  553 + auto opt = static_cast<T *>(parent_)->get_option_no_throw(opt_name);
554 554 if(opt == nullptr) {
555 555 throw IncorrectConstruction::MissingOption(opt_name);
556 556 }
... ... @@ -587,7 +587,7 @@ class Option : public OptionBase&lt;Option&gt; {
587 587 template <typename T = App> Option *ignore_case(bool value = true) {
588 588 if(!ignore_case_ && value) {
589 589 ignore_case_ = value;
590   - auto *parent = dynamic_cast<T *>(parent_);
  590 + auto *parent = static_cast<T *>(parent_);
591 591 for(const Option_p &opt : parent->options_) {
592 592 if(opt.get() == this) {
593 593 continue;
... ... @@ -612,7 +612,7 @@ class Option : public OptionBase&lt;Option&gt; {
612 612  
613 613 if(!ignore_underscore_ && value) {
614 614 ignore_underscore_ = value;
615   - auto *parent = dynamic_cast<T *>(parent_);
  615 + auto *parent = static_cast<T *>(parent_);
616 616 for(const Option_p &opt : parent->options_) {
617 617 if(opt.get() == this) {
618 618 continue;
... ...