Commit 5c35c182f634d61710661e3ee5a3c5bc1b62c915
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>
Showing
4 changed files
with
19 additions
and
10 deletions
azure-pipelines.yml
| @@ -63,7 +63,11 @@ jobs: | @@ -63,7 +63,11 @@ jobs: | ||
| 63 | Windowslatest: | 63 | Windowslatest: |
| 64 | vmImage: 'windows-2019' | 64 | vmImage: 'windows-2019' |
| 65 | cli11.std: 20 | 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 | pool: | 71 | pool: |
| 68 | vmImage: $(vmImage) | 72 | vmImage: $(vmImage) |
| 69 | steps: | 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,7 +135,7 @@ The default configuration file will read INI files, but will write out files in | ||
| 135 | ```cpp | 135 | ```cpp |
| 136 | app.config_formatter(std::make_shared<CLI::ConfigINI>()); | 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 | ## Custom formats | 140 | ## Custom formats |
| 141 | 141 |
include/CLI/App.hpp
| @@ -1374,20 +1374,20 @@ class App { | @@ -1374,20 +1374,20 @@ class App { | ||
| 1374 | int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const { | 1374 | int exit(const Error &e, std::ostream &out = std::cout, std::ostream &err = std::cerr) const { |
| 1375 | 1375 | ||
| 1376 | /// Avoid printing anything if this is a CLI::RuntimeError | 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 | return e.get_exit_code(); | 1378 | return e.get_exit_code(); |
| 1379 | 1379 | ||
| 1380 | - if(dynamic_cast<const CLI::CallForHelp *>(&e) != nullptr) { | 1380 | + if(e.get_name() == "CallForHelp") { |
| 1381 | out << help(); | 1381 | out << help(); |
| 1382 | return e.get_exit_code(); | 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 | out << help("", AppFormatMode::All); | 1386 | out << help("", AppFormatMode::All); |
| 1387 | return e.get_exit_code(); | 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 | out << e.what() << std::endl; | 1391 | out << e.what() << std::endl; |
| 1392 | return e.get_exit_code(); | 1392 | return e.get_exit_code(); |
| 1393 | } | 1393 | } |
| @@ -1606,7 +1606,12 @@ class App { | @@ -1606,7 +1606,12 @@ class App { | ||
| 1606 | 1606 | ||
| 1607 | /// Access the config formatter as a configBase pointer | 1607 | /// Access the config formatter as a configBase pointer |
| 1608 | std::shared_ptr<ConfigBase> get_config_formatter_base() const { | 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 | return std::dynamic_pointer_cast<ConfigBase>(config_formatter_); | 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 | /// Get the app or subcommand description | 1617 | /// Get the app or subcommand description |
include/CLI/Option.hpp
| @@ -508,7 +508,7 @@ class Option : public OptionBase<Option> { | @@ -508,7 +508,7 @@ class Option : public OptionBase<Option> { | ||
| 508 | 508 | ||
| 509 | /// Can find a string if needed | 509 | /// Can find a string if needed |
| 510 | template <typename T = App> Option *needs(std::string opt_name) { | 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 | if(opt == nullptr) { | 512 | if(opt == nullptr) { |
| 513 | throw IncorrectConstruction::MissingOption(opt_name); | 513 | throw IncorrectConstruction::MissingOption(opt_name); |
| 514 | } | 514 | } |
| @@ -550,7 +550,7 @@ class Option : public OptionBase<Option> { | @@ -550,7 +550,7 @@ class Option : public OptionBase<Option> { | ||
| 550 | 550 | ||
| 551 | /// Can find a string if needed | 551 | /// Can find a string if needed |
| 552 | template <typename T = App> Option *excludes(std::string opt_name) { | 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 | if(opt == nullptr) { | 554 | if(opt == nullptr) { |
| 555 | throw IncorrectConstruction::MissingOption(opt_name); | 555 | throw IncorrectConstruction::MissingOption(opt_name); |
| 556 | } | 556 | } |
| @@ -587,7 +587,7 @@ class Option : public OptionBase<Option> { | @@ -587,7 +587,7 @@ class Option : public OptionBase<Option> { | ||
| 587 | template <typename T = App> Option *ignore_case(bool value = true) { | 587 | template <typename T = App> Option *ignore_case(bool value = true) { |
| 588 | if(!ignore_case_ && value) { | 588 | if(!ignore_case_ && value) { |
| 589 | ignore_case_ = value; | 589 | ignore_case_ = value; |
| 590 | - auto *parent = dynamic_cast<T *>(parent_); | 590 | + auto *parent = static_cast<T *>(parent_); |
| 591 | for(const Option_p &opt : parent->options_) { | 591 | for(const Option_p &opt : parent->options_) { |
| 592 | if(opt.get() == this) { | 592 | if(opt.get() == this) { |
| 593 | continue; | 593 | continue; |
| @@ -612,7 +612,7 @@ class Option : public OptionBase<Option> { | @@ -612,7 +612,7 @@ class Option : public OptionBase<Option> { | ||
| 612 | 612 | ||
| 613 | if(!ignore_underscore_ && value) { | 613 | if(!ignore_underscore_ && value) { |
| 614 | ignore_underscore_ = value; | 614 | ignore_underscore_ = value; |
| 615 | - auto *parent = dynamic_cast<T *>(parent_); | 615 | + auto *parent = static_cast<T *>(parent_); |
| 616 | for(const Option_p &opt : parent->options_) { | 616 | for(const Option_p &opt : parent->options_) { |
| 617 | if(opt.get() == this) { | 617 | if(opt.get() == this) { |
| 618 | continue; | 618 | continue; |