Skip to content

Commit

Permalink
get_subcommand_no_throw (CLIUtils#1016)
Browse files Browse the repository at this point in the history
get_subcommand when used for parsing config files, was throwing and
catching as part of control flow and expected operation, this resulting
in a performance hit in select cases. A get_subcommand_no_throw was
added to resolve this issue.

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
phlptp and pre-commit-ci[bot] authored Mar 12, 2024
1 parent c04c9b2 commit 6cd171a
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 12 deletions.
11 changes: 8 additions & 3 deletions include/CLI/App.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,10 @@ class App {
/// Check to see if a subcommand is part of this command (text version)
CLI11_NODISCARD App *get_subcommand(std::string subcom) const;

/// Get a subcommand by name (noexcept non-const version)
/// returns null if subcommand doesn't exist
CLI11_NODISCARD App *get_subcommand_no_throw(std::string subcom) const noexcept;

/// Get a pointer to subcommand by index
CLI11_NODISCARD App *get_subcommand(int index = 0) const;

Expand Down Expand Up @@ -907,8 +911,9 @@ class App {
}

/// Check with name instead of pointer to see if subcommand was selected
CLI11_NODISCARD bool got_subcommand(std::string subcommand_name) const {
return get_subcommand(subcommand_name)->parsed_ > 0;
CLI11_NODISCARD bool got_subcommand(std::string subcommand_name) const noexcept {
App *sub = get_subcommand_no_throw(subcommand_name);
return (sub != nullptr) ? (sub->parsed_ > 0) : false;
}

/// Sets excluded options for the subcommand
Expand Down Expand Up @@ -1038,7 +1043,7 @@ class App {
std::vector<Option *> get_options(const std::function<bool(Option *)> filter = {});

/// Get an option by name (noexcept non-const version)
Option *get_option_no_throw(std::string option_name) noexcept;
CLI11_NODISCARD Option *get_option_no_throw(std::string option_name) noexcept;

/// Get an option by name (noexcept const version)
CLI11_NODISCARD const Option *get_option_no_throw(std::string option_name) const noexcept;
Expand Down
14 changes: 7 additions & 7 deletions include/CLI/impl/App_inl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,10 @@ CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(std::string subcom) const
return subc;
}

CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand_no_throw(std::string subcom) const noexcept {
return _find_subcommand(subcom, false, false);
}

CLI11_NODISCARD CLI11_INLINE App *App::get_subcommand(int index) const {
if(index >= 0) {
auto uindex = static_cast<unsigned>(index);
Expand Down Expand Up @@ -796,7 +800,7 @@ CLI11_INLINE std::vector<Option *> App::get_options(const std::function<bool(Opt
return options;
}

CLI11_INLINE Option *App::get_option_no_throw(std::string option_name) noexcept {
CLI11_NODISCARD CLI11_INLINE Option *App::get_option_no_throw(std::string option_name) noexcept {
for(Option_p &opt : options_) {
if(opt->check_name(option_name)) {
return opt.get();
Expand Down Expand Up @@ -1441,12 +1445,8 @@ CLI11_INLINE void App::_parse_config(const std::vector<ConfigItem> &args) {
CLI11_INLINE bool App::_parse_single_config(const ConfigItem &item, std::size_t level) {

if(level < item.parents.size()) {
try {
auto *subcom = get_subcommand(item.parents.at(level));
return subcom->_parse_single_config(item, level + 1);
} catch(const OptionNotFound &) {
return false;
}
auto *subcom = get_subcommand_no_throw(item.parents.at(level));
return (subcom != nullptr) ? subcom->_parse_single_config(item, level + 1) : false;
}
// check for section open
if(item.name == "++") {
Expand Down
6 changes: 4 additions & 2 deletions tests/SubcommandTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ TEST_CASE_METHOD(TApp, "BasicSubcommands", "[subcom]") {

CHECK(app.get_subcommand(sub1) == sub1);
CHECK(app.get_subcommand("sub1") == sub1);
CHECK(app.get_subcommand_no_throw("sub1") == sub1);
CHECK_THROWS_AS(app.get_subcommand("sub3"), CLI::OptionNotFound);

CHECK_NOTHROW(app.get_subcommand_no_throw("sub3"));
CHECK(app.get_subcommand_no_throw("sub3") == nullptr);
run();
CHECK(app.get_subcommands().empty());

Expand Down Expand Up @@ -90,7 +92,7 @@ TEST_CASE_METHOD(TApp, "MultiSubFallthrough", "[subcom]") {
CHECK(!sub2->parsed());
CHECK(0u == sub2->count());

CHECK_THROWS_AS(app.got_subcommand("sub3"), CLI::OptionNotFound);
CHECK(!app.got_subcommand("sub3"));
}

TEST_CASE_METHOD(TApp, "CrazyNameSubcommand", "[subcom]") {
Expand Down

0 comments on commit 6cd171a

Please sign in to comment.