Merge pull request #254 from skrobinson/fix-maintenance

Various maintenance tasks
This commit is contained in:
Pranav 2023-02-19 10:29:59 -06:00 committed by GitHub
commit e516556733
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 43 additions and 36 deletions

View File

@ -7,9 +7,8 @@ project(argparse
LANGUAGES CXX LANGUAGES CXX
) )
option(ARGPARSE_INSTALL ON) option(ARGPARSE_INSTALL "Include an install target" ON)
option(ARGPARSE_BUILD_TESTS OFF) option(ARGPARSE_BUILD_TESTS "Build tests" OFF)
option(ARGPARSE_LONG_VERSION_ARG_ONLY OFF)
include(GNUInstallDirs) include(GNUInstallDirs)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
@ -17,11 +16,6 @@ include(CMakePackageConfigHelpers)
add_library(argparse INTERFACE) add_library(argparse INTERFACE)
add_library(argparse::argparse ALIAS argparse) 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_compile_features(argparse INTERFACE cxx_std_17)
target_include_directories(argparse INTERFACE target_include_directories(argparse INTERFACE
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>

View File

@ -687,25 +687,30 @@ main
### Parent Parsers ### 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 ```cpp
argparse::ArgumentParser parent_parser("main"); argparse::ArgumentParser surface_parser("surface", 1.0, argparse::default_arguments::none);
parent_parser.add_argument("--parent") parent_parser.add_argument("--area")
.default_value(0) .default_value(0)
.scan<'i', int>(); .scan<'i', int>();
argparse::ArgumentParser foo_parser("foo"); argparse::ArgumentParser floor_parser("floor");
foo_parser.add_argument("foo"); floor_parser.add_argument("tile_size").scan<'i', int>();
foo_parser.add_parents(parent_parser); floor_parser.add_parents(surface_parser);
foo_parser.parse_args({ "./main", "--parent", "2", "XXX" }); // parent = 2, foo = XXX floor_parser.parse_args({ "./main", "--area", "200", "12" }); // --area = 200, tile_size = 12
argparse::ArgumentParser bar_parser("bar"); argparse::ArgumentParser ceiling_parser("ceiling");
bar_parser.add_argument("--bar"); ceiling_parser.add_argument("--color");
bar_parser.parse_args({ "./main", "--bar", "YYY" }); // bar = YYY 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 ### Subcommands
@ -1157,7 +1162,7 @@ sudo make install
| :------------------- | :--------------- | :----------------- | | :------------------- | :--------------- | :----------------- |
| GCC >= 8.3.0 | libstdc++ | Ubuntu 18.04 | | GCC >= 8.3.0 | libstdc++ | Ubuntu 18.04 |
| Clang >= 7.0.0 | libc++ | Xcode 10.2 | | 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 ## Contributing
Contributions are welcome, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information. Contributions are welcome, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information.

View File

@ -376,7 +376,8 @@ class Argument {
explicit Argument(std::string_view prefix_chars, explicit Argument(std::string_view prefix_chars,
std::array<std::string_view, N> &&a, std::array<std::string_view, N> &&a,
std::index_sequence<I...> /*unused*/) std::index_sequence<I...> /*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_is_required(false), m_is_repeatable(false), m_is_used(false),
m_prefix_chars(prefix_chars) { m_prefix_chars(prefix_chars) {
((void)m_names.emplace_back(a[I]), ...); ((void)m_names.emplace_back(a[I]), ...);
@ -702,10 +703,13 @@ public:
if constexpr (!details::IsContainer<T>) { if constexpr (!details::IsContainer<T>) {
return get<T>() == rhs; return get<T>() == rhs;
} else { } else {
using ValueType = typename T::value_type;
auto lhs = get<T>(); auto lhs = get<T>();
return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs), return std::equal(std::begin(lhs), std::end(lhs), std::begin(rhs),
std::end(rhs), std::end(rhs),
[](const auto &a, const auto &b) { return a == b; }); [](const auto &a, const auto &b) {
return std::any_cast<const ValueType &>(a) == b;
});
} }
} }
@ -972,18 +976,16 @@ private:
* Get argument value given a type * Get argument value given a type
* @throws std::logic_error in case of incompatible types * @throws std::logic_error in case of incompatible types
*/ */
template <typename T> template <typename T> T get() const {
auto get() const
-> std::conditional_t<details::IsContainer<T>, T, const T &> {
if (!m_values.empty()) { if (!m_values.empty()) {
if constexpr (details::IsContainer<T>) { if constexpr (details::IsContainer<T>) {
return any_cast_container<T>(m_values); return any_cast_container<T>(m_values);
} else { } else {
return *std::any_cast<T>(&m_values.front()); return std::any_cast<T>(m_values.front());
} }
} }
if (m_default_value.has_value()) { if (m_default_value.has_value()) {
return *std::any_cast<T>(&m_default_value); return std::any_cast<T>(m_default_value);
} }
if constexpr (details::IsContainer<T>) { if constexpr (details::IsContainer<T>) {
if (!m_accepts_optional_like_value) { if (!m_accepts_optional_like_value) {
@ -1019,7 +1021,7 @@ private:
T result; T result;
std::transform( std::transform(
std::begin(operand), std::end(operand), std::back_inserter(result), std::begin(operand), std::end(operand), std::back_inserter(result),
[](const auto &value) { return *std::any_cast<ValueType>(&value); }); [](const auto &value) { return std::any_cast<ValueType>(value); });
return result; return result;
} }
@ -1037,11 +1039,12 @@ private:
[](const std::string &value) { return value; }}; [](const std::string &value) { return value; }};
std::vector<std::any> m_values; std::vector<std::any> m_values;
NArgsRange m_num_args_range{1, 1}; NArgsRange m_num_args_range{1, 1};
bool m_accepts_optional_like_value = false; // Bit field of bool values. Set default value in ctor.
bool m_is_optional : true; bool m_accepts_optional_like_value : 1;
bool m_is_required : true; bool m_is_optional : 1;
bool m_is_repeatable : true; bool m_is_required : 1;
bool m_is_used : true; // True if the optional argument is used by user bool m_is_repeatable : 1;
bool m_is_used : 1;
std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars std::string_view m_prefix_chars; // ArgumentParser has the prefix_chars
}; };
@ -1249,9 +1252,7 @@ public:
* @throws std::logic_error if the option has no value * @throws std::logic_error if the option has no value
* @throws std::bad_any_cast if the option is not of type T * @throws std::bad_any_cast if the option is not of type T
*/ */
template <typename T = std::string> template <typename T = std::string> T get(std::string_view arg_name) const {
auto get(std::string_view arg_name) const
-> std::conditional_t<details::IsContainer<T>, T, const T &> {
if (!m_is_parsed) { if (!m_is_parsed) {
throw std::logic_error("Nothing parsed, no arguments are available."); throw std::logic_error("Nothing parsed, no arguments are available.");
} }

View File

@ -33,3 +33,10 @@ TEST_CASE("Implicit argument" * test_suite("ArgumentParser::get")) {
REQUIRE_THROWS_WITH_AS(program.get("--stuff"), REQUIRE_THROWS_WITH_AS(program.get("--stuff"),
"No value provided for '--stuff'.", std::logic_error); "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<int>("--stuff"), std::bad_any_cast);
}