From a5ab5b0ce8f3108fa5bb265292584e67f2a3ab54 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Thu, 5 Jan 2023 08:19:57 -0700 Subject: [PATCH 1/7] Update minimum supported MSVC version https://learn.microsoft.com/en-us/cpp/overview/visual-cpp-language-conformance?view=msvc-170 Closes #228 Reported-by: @c0rn1ie Signed-off-by: Sean Robinson --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1398455..dee67b9 100644 --- a/README.md +++ b/README.md @@ -1157,7 +1157,7 @@ sudo make install | :------------------- | :--------------- | :----------------- | | GCC >= 8.3.0 | libstdc++ | Ubuntu 18.04 | | Clang >= 7.0.0 | libc++ | Xcode 10.2 | -| MSVC >= 14.16 | Microsoft STL | Visual Studio 2017 | +| MSVC >= 16.8 | Microsoft STL | Visual Studio 2019 | ## Contributing Contributions are welcome, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information. From 04ac1fe366281cdc2a6ad44e6e93a070ee8d2e68 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Thu, 5 Jan 2023 10:23:16 -0700 Subject: [PATCH 2/7] Refactor Parent Parsers documentation This replaces the verbiage copied from the Python argparse documentation and makes the code sample more concrete. This illustrates how to avoid the multiple help output problem reported in #165. Closes #165 Signed-off-by: Sean Robinson --- README.md | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index dee67b9..3736fc5 100644 --- a/README.md +++ b/README.md @@ -687,25 +687,30 @@ main ### Parent Parsers -Sometimes, several parsers share a common set of arguments. Rather than repeating the definitions of these arguments, a single parser with all the common arguments can be added as a parent to another ArgumentParser instance. The ```.add_parents``` method takes a list of ArgumentParser objects, collects all the positional and optional actions from them, and adds these actions to the ArgumentParser object being constructed: +A parser may use arguments that could be used by other parsers. + +These shared arguments can be added to a parser which is then used as a "parent" for parsers which also need those arguments. One or more parent parsers may be added to a parser with `.add_parents`. The positional and optional arguments in each parent is added to the child parser. ```cpp -argparse::ArgumentParser parent_parser("main"); -parent_parser.add_argument("--parent") +argparse::ArgumentParser surface_parser("surface", 1.0, argparse::default_arguments::none); +parent_parser.add_argument("--area") .default_value(0) .scan<'i', int>(); -argparse::ArgumentParser foo_parser("foo"); -foo_parser.add_argument("foo"); -foo_parser.add_parents(parent_parser); -foo_parser.parse_args({ "./main", "--parent", "2", "XXX" }); // parent = 2, foo = XXX +argparse::ArgumentParser floor_parser("floor"); +floor_parser.add_argument("tile_size").scan<'i', int>(); +floor_parser.add_parents(surface_parser); +floor_parser.parse_args({ "./main", "--area", "200", "12" }); // --area = 200, tile_size = 12 -argparse::ArgumentParser bar_parser("bar"); -bar_parser.add_argument("--bar"); -bar_parser.parse_args({ "./main", "--bar", "YYY" }); // bar = YYY +argparse::ArgumentParser ceiling_parser("ceiling"); +ceiling_parser.add_argument("--color"); +ceiling_parser.add_parents(surface_parser); +ceiling_parser.parse_args({ "./main", "--color", "gray" }); // --area = 0, --color = "gray" ``` -Note You must fully initialize the parsers before passing them via ```.add_parents```. If you change the parent parsers after the child parser, those changes will not be reflected in the child. +Changes made to parents after they are added to a parser are not reflected in any child parsers. Completely initialize parent parsers before adding them to a parser. + +Each parser will have the standard set of default arguments. Disable the default arguments in parent parsers to avoid duplicate help output. ### Subcommands From d8453810286466c3622168f4041ce041e3f66aae Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Thu, 5 Jan 2023 12:08:03 -0700 Subject: [PATCH 3/7] Fix Argument bool bit fields The intent of ": 1" is to use individual bits to store the bool state of these class values. Because true != 0, this worked. But it was likely to bite someone sometime. (My bad: 0fe17e22f6.) This commit also adds m_accepts_optional_like_value to the bit field and sets the default false value in the constructor. Because we cannot set a default value during declaration (until C++20). make sure future coders know to set the preferred default in the constructor. Closes #213 Signed-off-by: Sean Robinson --- include/argparse/argparse.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index d0ce31d..ed02ddf 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -376,7 +376,8 @@ class Argument { explicit Argument(std::string_view prefix_chars, std::array &&a, std::index_sequence /*unused*/) - : m_is_optional((is_optional(a[I], prefix_chars) || ...)), + : m_accepts_optional_like_value(false), + m_is_optional((is_optional(a[I], prefix_chars) || ...)), m_is_required(false), m_is_repeatable(false), m_is_used(false), m_prefix_chars(prefix_chars) { ((void)m_names.emplace_back(a[I]), ...); @@ -1033,11 +1034,12 @@ private: [](const std::string &value) { return value; }}; std::vector m_values; NArgsRange m_num_args_range{1, 1}; - bool m_accepts_optional_like_value = false; - bool m_is_optional : true; - bool m_is_required : true; - bool m_is_repeatable : true; - bool m_is_used : true; // True if the optional argument is used by user + // Bit field of bool values. Set default value in ctor. + bool m_accepts_optional_like_value : 1; + bool m_is_optional : 1; + bool m_is_required : 1; + bool m_is_repeatable : 1; + bool m_is_used : 1; std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars }; From 7ed952f4fec9d09abf8939acfdf195ba6289e430 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Tue, 17 Jan 2023 13:23:19 -0700 Subject: [PATCH 4/7] Add test for ArgumentParser::get() with inappropriate type Signed-off-by: Sean Robinson --- test/test_get.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/test_get.cpp b/test/test_get.cpp index 9cc046c..ad719b0 100644 --- a/test/test_get.cpp +++ b/test/test_get.cpp @@ -33,3 +33,10 @@ TEST_CASE("Implicit argument" * test_suite("ArgumentParser::get")) { REQUIRE_THROWS_WITH_AS(program.get("--stuff"), "No value provided for '--stuff'.", std::logic_error); } + +TEST_CASE("Mismatched type for argument" * test_suite("ArgumentParser::get")) { + argparse::ArgumentParser program("test"); + program.add_argument("-s", "--stuff"); // as default type, a std::string + REQUIRE_NOTHROW(program.parse_args({"test", "-s", "321"})); + REQUIRE_THROWS_AS(program.get("--stuff"), std::bad_any_cast); +} From 6974f46851064e2425290f9081a96c56993d0db6 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Tue, 17 Jan 2023 13:57:16 -0700 Subject: [PATCH 5/7] Revert "Use references for `any_cast`" This attempts to fix Issue #225-1 by reverting the change that turned a std::bad_any_cast exception into a nullptr. Reverts commit 357068156e. Signed-off-by: Sean Robinson --- include/argparse/argparse.hpp | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index ed02ddf..bfede16 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -699,10 +699,13 @@ public: if constexpr (!details::IsContainer) { return get() == rhs; } else { + using ValueType = typename T::value_type; auto lhs = get(); return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs), std::end(rhs), - [](const auto &a, const auto &b) { return a == b; }); + [](const auto &a, const auto &b) { + return std::any_cast(a) == b; + }); } } @@ -969,18 +972,16 @@ private: * Get argument value given a type * @throws std::logic_error in case of incompatible types */ - template - auto get() const - -> std::conditional_t, T, const T &> { + template T get() const { if (!m_values.empty()) { if constexpr (details::IsContainer) { return any_cast_container(m_values); } else { - return *std::any_cast(&m_values.front()); + return std::any_cast(m_values.front()); } } if (m_default_value.has_value()) { - return *std::any_cast(&m_default_value); + return std::any_cast(m_default_value); } if constexpr (details::IsContainer) { if (!m_accepts_optional_like_value) { @@ -1016,7 +1017,7 @@ private: T result; std::transform( std::begin(operand), std::end(operand), std::back_inserter(result), - [](const auto &value) { return *std::any_cast(&value); }); + [](const auto &value) { return std::any_cast(value); }); return result; } @@ -1247,9 +1248,7 @@ public: * @throws std::logic_error if the option has no value * @throws std::bad_any_cast if the option is not of type T */ - template - auto get(std::string_view arg_name) const - -> std::conditional_t, T, const T &> { + template T get(std::string_view arg_name) const { if (!m_is_parsed) { throw std::logic_error("Nothing parsed, no arguments are available."); } From be705d191b0b02a48c175626d8f2d365e7aacee9 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Tue, 17 Jan 2023 14:15:05 -0700 Subject: [PATCH 6/7] Remove cmake option for removable '-v' This was made obsolete by commit ea1f7ef663. Signed-off-by: Sean Robinson --- CMakeLists.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c269bb1..0654064 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,6 @@ project(argparse option(ARGPARSE_INSTALL ON) option(ARGPARSE_BUILD_TESTS OFF) -option(ARGPARSE_LONG_VERSION_ARG_ONLY OFF) include(GNUInstallDirs) include(CMakePackageConfigHelpers) @@ -17,11 +16,6 @@ include(CMakePackageConfigHelpers) add_library(argparse INTERFACE) add_library(argparse::argparse ALIAS argparse) - -if (ARGPARSE_LONG_VERSION_ARG_ONLY) - target_compile_definitions(argparse INTERFACE ARGPARSE_LONG_VERSION_ARG_ONLY=true) -endif () - target_compile_features(argparse INTERFACE cxx_std_17) target_include_directories(argparse INTERFACE $ From d0beb40d647a85fde3bcb641e173f6b8ead23974 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Tue, 17 Jan 2023 14:26:14 -0700 Subject: [PATCH 7/7] Fix cmake option command by including help text The option() command expects a help string between the variable and the initial value. Closes #241 Signed-off-by: Sean Robinson --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0654064..19d8942 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,8 +7,8 @@ project(argparse LANGUAGES CXX ) -option(ARGPARSE_INSTALL ON) -option(ARGPARSE_BUILD_TESTS OFF) +option(ARGPARSE_INSTALL "Include an install target" ON) +option(ARGPARSE_BUILD_TESTS "Build tests" OFF) include(GNUInstallDirs) include(CMakePackageConfigHelpers)