diff --git a/include/argparse.hpp b/include/argparse.hpp index 724fa58..a56ae6e 100644 --- a/include/argparse.hpp +++ b/include/argparse.hpp @@ -64,7 +64,7 @@ struct is_container< decltype(std::declval().begin()), decltype(std::declval().end()), decltype(std::declval().size())>, - void>> : public std::true_type {}; + void>> : std::true_type {}; template static constexpr bool is_container_v = is_container::value; @@ -99,18 +99,28 @@ class Argument { friend auto operator<<(std::ostream &, ArgumentParser const &) -> std::ostream &; -public: - Argument() = default; - - template - explicit Argument(Args... args) - : mNames({std::move(args)...}), mIsOptional((is_optional(args) || ...)) { + template + explicit Argument(std::string(&&a)[N], std::index_sequence) + : mIsOptional((is_optional(a[I]) || ...)), mIsRequired(false), + mIsUsed(false) { + ((void)mNames.push_back(std::move(a[I])), ...); std::sort( mNames.begin(), mNames.end(), [](const auto &lhs, const auto &rhs) { return lhs.size() == rhs.size() ? lhs < rhs : lhs.size() < rhs.size(); }); } +public: + Argument() = default; + + template ...>, + int> = 0> + explicit Argument(Args &&... args) + : Argument({std::string(std::forward(args))...}, + std::make_index_sequence{}) {} + Argument &help(std::string aHelp) { mHelp = std::move(aHelp); return *this; @@ -201,8 +211,8 @@ public: if (mIsOptional) { if (mIsUsed && mValues.size() != mNumArgs && !mDefaultValue.has_value()) { std::stringstream stream; - stream << mUsedName << ": expected " << mNumArgs - << " argument(s). " << mValues.size() << " provided."; + stream << mUsedName << ": expected " << mNumArgs << " argument(s). " + << mValues.size() << " provided."; throw std::runtime_error(stream.str()); } else { // TODO: check if an implicit value was programmed for this argument @@ -220,8 +230,8 @@ public: } else { if (mValues.size() != mNumArgs && !mDefaultValue.has_value()) { std::stringstream stream; - stream << mUsedName << ": expected " << mNumArgs - << " argument(s). " << mValues.size() << " provided."; + stream << mUsedName << ": expected " << mNumArgs << " argument(s). " + << mValues.size() << " provided."; throw std::runtime_error(stream.str()); } } @@ -256,7 +266,8 @@ public: * @throws std::logic_error in case of incompatible types */ template - std::enable_if_t, bool> operator==(const T &aRhs) const { + std::enable_if_t, bool> + operator==(const T &aRhs) const { return get() == aRhs; } @@ -265,17 +276,14 @@ public: * @throws std::logic_error in case of incompatible types */ template - std::enable_if_t, bool> operator==(const T &aRhs) const { + std::enable_if_t, bool> + operator==(const T &aRhs) const { using ValueType = typename T::value_type; auto tLhs = get(); - if (tLhs.size() != aRhs.size()) - return false; - else { - return std::equal(std::begin(tLhs), std::end(tLhs), std::begin(aRhs), - [](const auto &lhs, const auto &rhs) { - return std::any_cast(lhs) == rhs; - }); - } + return std::equal(std::begin(tLhs), std::end(tLhs), std::begin(aRhs), + std::end(aRhs), [](const auto &lhs, const auto &rhs) { + return std::any_cast(lhs) == rhs; + }); } private: @@ -326,7 +334,8 @@ private: * Getter for container types * @throws std::logic_error in case of incompatible types */ - template details::enable_if_container get() const { + template + details::enable_if_container get() const { using ValueType = typename CONTAINER::value_type; CONTAINER tResult; if (!mValues.empty()) { @@ -358,13 +367,11 @@ private: std::in_place_type, [](const std::string &aValue) { return aValue; }}; std::vector mValues; - std::vector mRawValues; size_t mNumArgs = 1; - bool mIsOptional = false; - bool mIsRequired = false; - bool mIsUsed = false; // relevant for optional arguments. True if used by user + bool mIsOptional : 1; + bool mIsRequired : 1; + bool mIsUsed : 1; // True if the optional argument is used by user -public: static constexpr auto mHelpOption = "-h"; static constexpr auto mHelpOptionLong = "--help"; }; @@ -418,7 +425,7 @@ public: // Parameter packed add_parents method // Accepts a variadic number of ArgumentParser objects template void add_parents(const Targs &... Fargs) { - for (auto &tParentParser : {Fargs...}) { + for (const ArgumentParser &tParentParser : {std::ref(Fargs)...}) { for (auto &tArgument : tParentParser.mPositionalArguments) { auto it = mPositionalArguments.insert(cend(mPositionalArguments), tArgument); @@ -456,19 +463,15 @@ public: * @throws std::logic_error in case of an invalid argument name * @throws std::logic_error in case of incompatible types */ - template T get(const std::string &aArgumentName) { - auto tIterator = mArgumentMap.find(aArgumentName); - if (tIterator != mArgumentMap.end()) { - return tIterator->second->get(); - } - throw std::logic_error("No such argument"); + template T get(std::string_view aArgumentName) { + return (*this)[aArgumentName].get(); } /* Indexing operator. Return a reference to an Argument object * Used in conjuction with Argument.operator== e.g., parser["foo"] == true * @throws std::logic_error in case of an invalid argument name */ - Argument &operator[](const std::string &aArgumentName) { + Argument &operator[](std::string_view aArgumentName) { auto tIterator = mArgumentMap.find(aArgumentName); if (tIterator != mArgumentMap.end()) { return *(tIterator->second); diff --git a/test/test_optional_arguments.hpp b/test/test_optional_arguments.hpp index 151f6ad..4f316c7 100644 --- a/test/test_optional_arguments.hpp +++ b/test/test_optional_arguments.hpp @@ -43,4 +43,23 @@ TEST_CASE("Parse multiple toggle arguments with implicit values", "[optional_arg REQUIRE(program.get("-a") == true); REQUIRE(program.get("-u") == false); REQUIRE(program.get("-x") == true); -} \ No newline at end of file +} + +TEST_CASE("Parse arguments of different types", "[optional_arguments]") { + using namespace std::literals; + + argparse::ArgumentParser program("test"); + program.add_argument("--this-argument-is-longer-than-any-sso-buffer-that-" + "makes-sense-unless-your-cache-line-is-this-long"s); + + REQUIRE_NOTHROW(program.parse_args({"test"})); + + program.add_argument("-string"s, "-string-view"sv, "-builtin") + .default_value(false) + .implicit_value(true); + + program.parse_args({"test", "-string-view"}); + REQUIRE(program["-string"sv] == true); + REQUIRE(program["-string-view"] == true); + REQUIRE(program["-builtin"s] == true); +}