From 0ae3c7d919a47bfb1afa7410a3dbaa47df2ed0da Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Wed, 22 Mar 2023 09:07:45 -0700 Subject: [PATCH 1/2] Add exit_on_default_arguments parameter to ArgumentParser Allows users to opt-out of std::exit call in default arguments without needing to replace with new --help and --version arguments. Signed-off-by: Sean Robinson --- README.md | 12 ++++++++++-- include/argparse/argparse.hpp | 13 ++++++++++--- test/test_default_args.cpp | 12 ++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3736fc59..8bc2c0d6 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ argparse::ArgumentParser program("program_name"); **NOTE:** There is an optional second argument to the `ArgumentParser` which is the program version. Example: `argparse::ArgumentParser program("libfoo", "1.9.0");` -**NOTE:** There is an optional third argument to the `ArgumentParser` which controls default arguments. Example: `argparse::ArgumentParser program("libfoo", "1.9.0", default_arguments::none);` See [Default Arguments](#default-arguments), below. +**NOTE:** There are optional third and fourth arguments to the `ArgumentParser` which control default arguments. Example: `argparse::ArgumentParser program("libfoo", "1.9.0", default_arguments::help, false);` See [Default Arguments](#default-arguments), below. To add a new argument, simply call ```.add_argument(...)```. You can provide a variadic list of argument names that you want to group together, e.g., ```-v``` and ```--verbose``` @@ -559,7 +559,9 @@ The grammar follows `std::from_chars`, but does not exactly duplicate it. For ex ### Default Arguments -`argparse` provides predefined arguments and actions for `-h`/`--help` and `-v`/`--version`. These default actions **exit** the program after displaying a help or version message, respectively. These defaults arguments can be disabled during `ArgumentParser` creation so that you can handle these arguments in your own way. (Note that a program name and version must be included when choosing default arguments.) +`argparse` provides predefined arguments and actions for `-h`/`--help` and `-v`/`--version`. By default, these actions will **exit** the program after displaying a help or version message, respectively. This exit does not call destructors, skipping clean-up of taken resources. + +These default arguments can be disabled during `ArgumentParser` creation so that you can handle these arguments in your own way. (Note that a program name and version must be included when choosing default arguments.) ```cpp argparse::ArgumentParser program("test", "1.0", default_arguments::none); @@ -578,6 +580,12 @@ The above code snippet outputs a help message and continues to run. It does not The default is `default_arguments::all` for included arguments. No default arguments will be added with `default_arguments::none`. `default_arguments::help` and `default_arguments::version` will individually add `--help` and `--version`. +The default arguments can be used while disabling the default exit with these arguments. This forth argument to `ArgumentParser` (`exit_on_default_arguments`) is a bool flag with a default **true** value. The following call will retain `--help` and `--version`, but will not exit when those arguments are used. + +```cpp +argparse::ArgumentParser program("test", "1.0", default_arguments::all, false) +``` + ### Gathering Remaining Arguments `argparse` supports gathering "remaining" arguments at the end of the command, e.g., for use in a compiler: diff --git a/include/argparse/argparse.hpp b/include/argparse/argparse.hpp index 485964da..9dc2d3fc 100644 --- a/include/argparse/argparse.hpp +++ b/include/argparse/argparse.hpp @@ -1052,14 +1052,18 @@ class ArgumentParser { public: explicit ArgumentParser(std::string program_name = {}, std::string version = "1.0", - default_arguments add_args = default_arguments::all) + default_arguments add_args = default_arguments::all, + bool exit_on_default_arguments = true) : m_program_name(std::move(program_name)), m_version(std::move(version)), + m_exit_on_default_arguments(exit_on_default_arguments), m_parser_path(m_program_name) { if ((add_args & default_arguments::help) == default_arguments::help) { add_argument("-h", "--help") .action([&](const auto & /*unused*/) { std::cout << help().str(); - std::exit(0); + if (m_exit_on_default_arguments) { + std::exit(0); + } }) .default_value(false) .help("shows help message and exits") @@ -1070,7 +1074,9 @@ class ArgumentParser { add_argument("-v", "--version") .action([&](const auto & /*unused*/) { std::cout << m_version << std::endl; - std::exit(0); + if (m_exit_on_default_arguments) { + std::exit(0); + } }) .default_value(false) .help("prints version information and exits") @@ -1676,6 +1682,7 @@ class ArgumentParser { std::string m_version; std::string m_description; std::string m_epilog; + bool m_exit_on_default_arguments = true; std::string m_prefix_chars{"-"}; std::string m_assign_chars{"="}; bool m_is_parsed = false; diff --git a/test/test_default_args.cpp b/test/test_default_args.cpp index 7b5e581f..d0cdd253 100644 --- a/test/test_default_args.cpp +++ b/test/test_default_args.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include using doctest::test_suite; @@ -17,3 +19,13 @@ TEST_CASE("Do not include default arguments" * test_suite("default_args")) { REQUIRE_THROWS_AS(parser.get("--help"), std::logic_error); REQUIRE_THROWS_AS(parser.get("--version"), std::logic_error); } + +TEST_CASE("Do not exit on default arguments" * test_suite("default_args")) { + argparse::ArgumentParser parser("test", "1.0", + argparse::default_arguments::all, false); + std::stringstream buf; + std::streambuf* saved_cout_buf = std::cout.rdbuf(buf.rdbuf()); + parser.parse_args({"test", "--help"}); + std::cout.rdbuf(saved_cout_buf); + REQUIRE(parser.is_used("--help")); +} From 9377e0d3b2eeba165921d0a31c2bcce141157461 Mon Sep 17 00:00:00 2001 From: Sean Robinson Date: Wed, 29 Mar 2023 08:42:12 -0700 Subject: [PATCH 2/2] Use return in place of exit() in README and samples Only those places in the README where an error is explicitly found in the main function have been updated. Other uses of exit are left untouched as there is not enough context to know if return will work in that location. Signed-off-by: Sean Robinson --- README.md | 10 +++++----- samples/compound_arguments.cpp | 2 +- samples/custom_assignment_characters.cpp | 2 +- samples/custom_prefix_characters.cpp | 2 +- samples/gathering_remaining_arguments.cpp | 2 +- samples/is_used.cpp | 2 +- samples/joining_repeated_optional_arguments.cpp | 2 +- samples/list_of_arguments.cpp | 2 +- samples/negative_numbers.cpp | 2 +- samples/optional_flag_argument.cpp | 2 +- samples/positional_argument.cpp | 2 +- samples/required_optional_argument.cpp | 2 +- samples/subcommands.cpp | 2 +- 13 files changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 8bc2c0d6..77a2bb4f 100644 --- a/README.md +++ b/README.md @@ -99,7 +99,7 @@ int main(int argc, char *argv[]) { catch (const std::runtime_error& err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } auto input = program.get("square"); @@ -781,7 +781,7 @@ int main(int argc, char *argv[]) { catch (const std::runtime_error& err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } // Use arguments @@ -908,7 +908,7 @@ int main(int argc, char *argv[]) { catch (const std::runtime_error& err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program.is_used("+f")) { @@ -956,7 +956,7 @@ int main(int argc, char *argv[]) { catch (const std::runtime_error& err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program.is_used("--foo")) { @@ -1104,7 +1104,7 @@ int main(int argc, char *argv[]) { catch (const std::runtime_error& err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program.is_used("--foo")) { diff --git a/samples/compound_arguments.cpp b/samples/compound_arguments.cpp index 66d5c920..4cfc037f 100644 --- a/samples/compound_arguments.cpp +++ b/samples/compound_arguments.cpp @@ -19,7 +19,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } auto a = program.get("-a"); // true diff --git a/samples/custom_assignment_characters.cpp b/samples/custom_assignment_characters.cpp index d5deff12..7e35ae0b 100644 --- a/samples/custom_assignment_characters.cpp +++ b/samples/custom_assignment_characters.cpp @@ -16,7 +16,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program.is_used("--foo")) { diff --git a/samples/custom_prefix_characters.cpp b/samples/custom_prefix_characters.cpp index 01e248a6..9f8917a3 100644 --- a/samples/custom_prefix_characters.cpp +++ b/samples/custom_prefix_characters.cpp @@ -16,7 +16,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program.is_used("+f")) { diff --git a/samples/gathering_remaining_arguments.cpp b/samples/gathering_remaining_arguments.cpp index 4f404544..be13f10f 100644 --- a/samples/gathering_remaining_arguments.cpp +++ b/samples/gathering_remaining_arguments.cpp @@ -12,7 +12,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } try { diff --git a/samples/is_used.cpp b/samples/is_used.cpp index 27e0373b..ffc05f88 100644 --- a/samples/is_used.cpp +++ b/samples/is_used.cpp @@ -16,7 +16,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } auto color = program.get("--color"); // "orange" diff --git a/samples/joining_repeated_optional_arguments.cpp b/samples/joining_repeated_optional_arguments.cpp index eebbdc61..0f6ab815 100644 --- a/samples/joining_repeated_optional_arguments.cpp +++ b/samples/joining_repeated_optional_arguments.cpp @@ -16,7 +16,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } auto colors = program.get>( diff --git a/samples/list_of_arguments.cpp b/samples/list_of_arguments.cpp index e533e477..996e7480 100644 --- a/samples/list_of_arguments.cpp +++ b/samples/list_of_arguments.cpp @@ -16,7 +16,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } auto files = program.get>( diff --git a/samples/negative_numbers.cpp b/samples/negative_numbers.cpp index ac4284fb..319245fe 100644 --- a/samples/negative_numbers.cpp +++ b/samples/negative_numbers.cpp @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program.is_used("integer")) { diff --git a/samples/optional_flag_argument.cpp b/samples/optional_flag_argument.cpp index 2f859540..f935ecf0 100644 --- a/samples/optional_flag_argument.cpp +++ b/samples/optional_flag_argument.cpp @@ -15,7 +15,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } if (program["--verbose"] == true) { diff --git a/samples/positional_argument.cpp b/samples/positional_argument.cpp index 03ffb0a6..4343863c 100644 --- a/samples/positional_argument.cpp +++ b/samples/positional_argument.cpp @@ -16,7 +16,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } int input = program.get("square"); diff --git a/samples/required_optional_argument.cpp b/samples/required_optional_argument.cpp index efea5231..0271f500 100644 --- a/samples/required_optional_argument.cpp +++ b/samples/required_optional_argument.cpp @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } std::cout << "Output written to " << program.get("-o") << "\n"; diff --git a/samples/subcommands.cpp b/samples/subcommands.cpp index 74d610e1..ba1059df 100644 --- a/samples/subcommands.cpp +++ b/samples/subcommands.cpp @@ -60,7 +60,7 @@ int main(int argc, char *argv[]) { } catch (const std::runtime_error &err) { std::cerr << err.what() << std::endl; std::cerr << program; - std::exit(1); + return 1; } // Use arguments