From 5f22faa973a78f48269a0ca2a5cc1813c1323b0d Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Thu, 10 Nov 2022 13:56:14 -0700 Subject: [PATCH] Add ArgumentParser::at to retrieve arguments and subparsers This allows updating attached object properties without holding external references to the various Argument and ArgumentParser objects. Closes #227 Signed-off-by: Sean Robinson --- README.md | 14 ++++++++++ include/argparse/argparse.hpp | 16 +++++++++++ test/CMakeLists.txt | 1 + test/test_as_container.cpp | 52 +++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 test/test_as_container.cpp diff --git a/README.md b/README.md index 95b63e8..c2198cf 100644 --- a/README.md +++ b/README.md @@ -827,6 +827,20 @@ When a help message is requested from a subparser, only the help for that partic Additionally, every parser has the `.is_subcommand_used("")` and `.is_subcommand_used(subparser)` member functions to check if a subcommand was used. +### Getting Argument and Subparser Instances + +```Argument``` and ```ArgumentParser``` instances added to an ```ArgumentParser``` can be retrieved with ```.at()```. The default return type is ```Argument```. + +```cpp +argparse::ArgumentParser program("test"); + +program.add_argument("--dir"); +program.at("--dir").default_value(std::string("/home/user")); + +program.add_subparser(argparse::ArgumentParser{"walk"}); +program.at("walk").add_argument("depth"); +``` + ### Parse Known Args Sometimes a program may only parse a few of the command-line arguments, passing the remaining arguments on to another script or program. In these cases, the `parse_known_args()` function can be useful. It works much like `parse_args()` except that it does not produce an error when extra arguments are present. Instead, it returns a list of remaining argument strings. diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index 0a8e8b5..d0ce31d 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -1167,6 +1167,22 @@ public: return *this; } + /* Getter for arguments and subparsers. + * @throws std::logic_error in case of an invalid argument or subparser name + */ + template + T& at(std::string_view name) { + if constexpr (std::is_same_v) { + return (*this)[name]; + } else { + auto subparser_it = m_subparser_map.find(name); + if (subparser_it != m_subparser_map.end()) { + return subparser_it->second->get(); + } + throw std::logic_error("No such subparser: " + std::string(name)); + } + } + ArgumentParser &set_prefix_chars(std::string prefix_chars) { m_prefix_chars = std::move(prefix_chars); return *this; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 47b9ba5..84599ad 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,6 +27,7 @@ file(GLOB ARGPARSE_TEST_SOURCES main.cpp test_actions.cpp test_append.cpp + test_as_container.cpp test_bool_operator.cpp test_compound_arguments.cpp test_container_arguments.cpp diff --git a/test/test_as_container.cpp b/test/test_as_container.cpp new file mode 100644 index 0000000..2f795fb --- /dev/null +++ b/test/test_as_container.cpp @@ -0,0 +1,52 @@ +#include +#include + +using doctest::test_suite; + +TEST_CASE("Get argument with .at()" * test_suite("as_container")) { + argparse::ArgumentParser program("test"); + auto &dir_arg = program.add_argument("--dir"); + program.at("--dir").default_value(std::string("/home/user")); + + SUBCASE("and default value") { + program.parse_args({"test"}); + REQUIRE(&(program.at("--dir")) == &dir_arg); + REQUIRE(program["--dir"] == std::string("/home/user")); + REQUIRE(program.at("--dir") == std::string("/home/user")); + } + + SUBCASE("and user-supplied value") { + program.parse_args({"test", "--dir", "/usr/local/database"}); + REQUIRE(&(program.at("--dir")) == &dir_arg); + REQUIRE(program["--dir"] == std::string("/usr/local/database")); + REQUIRE(program.at("--dir") == std::string("/usr/local/database")); + } + + SUBCASE("with unknown argument") { + program.parse_args({"test"}); + REQUIRE_THROWS_WITH_AS(program.at("--folder"), + "No such argument: --folder", std::logic_error); + } +} + +TEST_CASE("Get subparser with .at()" * test_suite("as_container")) { + argparse::ArgumentParser program("test"); + + argparse::ArgumentParser walk_cmd("walk"); + auto &speed = walk_cmd.add_argument("speed"); + + program.add_subparser(walk_cmd); + + SUBCASE("and its argument") { + program.parse_args({"test", "walk", "4km/h"}); + REQUIRE(&(program.at("walk")) == &walk_cmd); + REQUIRE(&(program.at("walk").at("speed")) == &speed); + REQUIRE(program.at("walk").is_used("speed")); + } + + SUBCASE("with unknown command") { + program.parse_args({"test"}); + REQUIRE_THROWS_WITH_AS(program.at("fly"), + "No such subparser: fly", std::logic_error); + } +}