From 77f3bd9b8da326c83b6008e15c338e7783aad1df Mon Sep 17 00:00:00 2001 From: Zhihao Yuan Date: Fri, 22 Nov 2019 16:12:44 -0600 Subject: [PATCH] Default --help to print help and exit The change also fixes a rare bug introduced in 9007958: when there are duplicated keys, insert_or_update overrides previously inserted ones, emplace doesn't. fixes: p-ranav/argparse#59 --- include/argparse.hpp | 30 ++++++++++++++++-------------- test/test_help.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 14 deletions(-) diff --git a/include/argparse.hpp b/include/argparse.hpp index de17c04..30c42da 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -30,6 +30,7 @@ SOFTWARE. #pragma once #include #include +#include #include #include #include @@ -372,20 +373,15 @@ private: bool mIsOptional : 1; bool mIsRequired : 1; bool mIsUsed : 1; // True if the optional argument is used by user - - static constexpr auto mHelpOption = "-h"; - static constexpr auto mHelpOptionLong = "--help"; }; class ArgumentParser { public: explicit ArgumentParser(std::string aProgramName = {}) : mProgramName(std::move(aProgramName)) { - add_argument(Argument::mHelpOption, Argument::mHelpOptionLong) + add_argument("-h", "--help") .help("show this help message and exit") - .nargs(0) - .default_value(false) - .implicit_value(true); + .nargs(0); } ArgumentParser(ArgumentParser &&) noexcept = default; @@ -542,10 +538,6 @@ private: auto positionalArgumentIt = std::begin(mPositionalArguments); for (auto it = std::next(std::begin(aArguments)); it != end;) { const auto &tCurrentArgument = *it; - if (tCurrentArgument == Argument::mHelpOption || - tCurrentArgument == Argument::mHelpOptionLong) { - throw std::runtime_error("help called"); - } if (Argument::is_positional(tCurrentArgument)) { if (positionalArgumentIt == std::end(mPositionalArguments)) { throw std::runtime_error( @@ -553,9 +545,19 @@ private: } auto tArgument = positionalArgumentIt++; it = tArgument->consume(it, end); - } else if (auto tIterator = mArgumentMap.find(tCurrentArgument); - tIterator != mArgumentMap.end()) { + continue; + } + + auto tIterator = mArgumentMap.find(tCurrentArgument); + if (tIterator != mArgumentMap.end()) { auto tArgument = tIterator->second; + + // the first optional argument is --help + if (tArgument == mOptionalArguments.begin()) { + std::cout << *this; + std::exit(0); + } + it = tArgument->consume(std::next(it), end, tCurrentArgument); } else if (const auto &tCompoundArgument = tCurrentArgument; tCompoundArgument.size() > 1 && tCompoundArgument[0] == '-' && @@ -607,7 +609,7 @@ private: void index_argument(list_iterator argIt) { for (auto &mName : std::as_const(argIt->mNames)) - mArgumentMap.emplace(mName, argIt); + mArgumentMap.insert_or_assign(mName, argIt); } std::string mProgramName; diff --git a/test/test_help.cpp b/test/test_help.cpp index 3db539a..e1312c7 100644 --- a/test/test_help.cpp +++ b/test/test_help.cpp @@ -17,3 +17,27 @@ TEST_CASE("Users can format help message" * test_suite("help")) { auto msg = program.help().str(); REQUIRE(msg == s.str()); } + +TEST_CASE("Users can override the help options" * test_suite("help")) { + GIVEN("a program that meant to take -h as a normal option") { + argparse::ArgumentParser program("test"); + program.add_argument("input"); + program.add_argument("-h").implicit_value('h').default_value('x'); + + WHEN("provided -h without fulfilling other requirements") { + THEN("validation fails") { + REQUIRE_THROWS_AS(program.parse_args({"test", "-h"}), + std::runtime_error); + } + } + + WHEN("provided arguments to all parameters") { + program.parse_args({"test", "-h", "some input"}); + + THEN("these parameters get their values") { + REQUIRE(program["-h"] == 'h'); + REQUIRE(program.get("input") == "some input"); + } + } + } +}