mirror of
https://github.com/KeqingMoe/argparse.git
synced 2025-07-04 15:14:39 +00:00
Merge pull request #254 from skrobinson/fix-maintenance
Various maintenance tasks
This commit is contained in:
commit
e516556733
@ -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}>
|
||||||
|
29
README.md
29
README.md
@ -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.
|
||||||
|
@ -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.");
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user