Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow check if ArgumentParser has parsed values #218

Merged
merged 1 commit into from
Oct 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
* [Parent Parsers](#parent-parsers)
* [Subcommands](#subcommands)
* [Parse Known Args](#parse-known-args)
* [ArgumentParser in bool Context](#argumentparser-in-bool-context)
* [Custom Prefix Characters](#custom-prefix-characters)
* [Custom Assignment Characters](#custom-assignment-characters)
* [Further Examples](#further-examples)
Expand Down Expand Up @@ -848,7 +849,13 @@ int main(int argc, char *argv[]) {
}
```

### Custom Prefix Characters
### ArgumentParser in bool Context

An `ArgumentParser` is `false` until it (or one of its subparsers) have extracted
known value(s) with `.parse_args` or `.parse_known_args`. When using `.parse_known_args`,
unknown arguments will not make a parser `true`.

### Custom Prefix Characters

Most command-line options will use `-` as the prefix, e.g. `-f/--foo`. Parsers that need to support different or additional prefix characters, e.g. for options like `+f` or `/foo`, may specify them using the `set_prefix_chars()`.

Expand Down
15 changes: 15 additions & 0 deletions include/argparse/argparse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,21 @@ class ArgumentParser {
return *this;
}

explicit operator bool() const {
auto arg_used = std::any_of(m_argument_map.cbegin(),
m_argument_map.cend(),
[](auto &it) {
return it.second->m_is_used;
});
auto subparser_used = std::any_of(m_subparser_used.cbegin(),
m_subparser_used.cend(),
[](auto &it) {
return it.second;
});

return m_is_parsed && (arg_used || subparser_used);
Copy link
Contributor

@SergiusTheBest SergiusTheBest Nov 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@skrobinson Why don't we use just m_is_parsed? I have a subcommand without arguments and operator bool() returns false for it.

}

// Parameter packing
// Call add_argument with variadic number of string arguments
template <typename... Targs> Argument &add_argument(Targs... f_args) {
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ file(GLOB ARGPARSE_TEST_SOURCES
main.cpp
test_actions.cpp
test_append.cpp
test_bool_operator.cpp
test_compound_arguments.cpp
test_container_arguments.cpp
test_const_correct.cpp
Expand Down
85 changes: 85 additions & 0 deletions test/test_bool_operator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <argparse/argparse.hpp>
skrobinson marked this conversation as resolved.
Show resolved Hide resolved
#include <doctest.hpp>

using doctest::test_suite;

TEST_CASE("ArgumentParser in bool context" *
test_suite("argument_parser")) {
argparse::ArgumentParser program("test");
program.add_argument("cases").remaining();

program.parse_args({"test"});
REQUIRE_FALSE(program);

program.parse_args({"test", "one", "two"});
REQUIRE(program);
}

TEST_CASE("With subparsers in bool context" * test_suite("argument_parser")) {
argparse::ArgumentParser program("test");

argparse::ArgumentParser cmd_fly("fly");
cmd_fly.add_argument("plane");

argparse::ArgumentParser cmd_soar("soar");
cmd_soar.add_argument("direction");

program.add_subparser(cmd_fly);
program.add_subparser(cmd_soar);

program.parse_args({"test", "fly", "glider"});
REQUIRE(program);
REQUIRE(cmd_fly);
REQUIRE_FALSE(cmd_soar);
}

TEST_CASE("Parsers remain false with unknown arguments" *
test_suite("argument_parser")) {
argparse::ArgumentParser program("test");

argparse::ArgumentParser cmd_build("build");
cmd_build.add_argument("--file").nargs(1);

argparse::ArgumentParser cmd_run("run");
cmd_run.add_argument("--file").nargs(1);

program.add_subparser(cmd_build);
program.add_subparser(cmd_run);

auto unknowns =
program.parse_known_args({"test", "badger", "--add-meal", "grubs"});
REQUIRE_FALSE(program);
REQUIRE_FALSE(cmd_build);
REQUIRE_FALSE(cmd_run);
}

TEST_CASE("Multi-level parsers match subparser bool" *
test_suite("argument_parser")) {
argparse::ArgumentParser program("test");

argparse::ArgumentParser cmd_cook("cook");
cmd_cook.add_argument("--temperature");

argparse::ArgumentParser cmd_cook_boil("boil");
cmd_cook_boil.add_argument("--rate");

argparse::ArgumentParser cmd_cook_boil_stir("stir");
cmd_cook_boil_stir.add_argument("--rate");

argparse::ArgumentParser cmd_wash("wash");

program.add_subparser(cmd_cook);
cmd_cook.add_subparser(cmd_cook_boil);
cmd_cook_boil.add_subparser(cmd_cook_boil_stir);

program.add_subparser(cmd_wash);

auto unknowns = program.parse_known_args(
{"test", "cook", "boil", "stir", "--rate", "fast"});

REQUIRE(program);
REQUIRE(cmd_cook);
REQUIRE(cmd_cook_boil);
REQUIRE(cmd_cook_boil_stir);
REQUIRE_FALSE(cmd_wash);
}