From 1b9b9823d4619ef62783e25768b9460dcde55a74 Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Wed, 11 May 2016 18:10:53 -0600 Subject: add mapping flag, throw maperror, ensure throwing works properly --- README.md | 4 +++- args.hxx | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ test.cxx | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a1abbcf..18ac5e4 100644 --- a/README.md +++ b/README.md @@ -492,7 +492,7 @@ Argument 'numbers' received invalid value type 'a' ```cpp #include #include -#include + std::istream& operator>>(std::istream& is, std::tuple& ints) { is >> std::get<0>(ints); @@ -501,6 +501,8 @@ std::istream& operator>>(std::istream& is, std::tuple& ints) return is; } +#include + void DoublesReader(const std::string &name, const std::string &value, std::tuple &destination) { size_t commapos = 0; diff --git a/args.hxx b/args.hxx index 9198afc..aade975 100644 --- a/args.hxx +++ b/args.hxx @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -174,6 +175,13 @@ namespace args virtual ~UsageError() {}; }; + class MapError : public Error + { + public: + MapError(const std::string &problem) : Error(problem) {} + virtual ~MapError() {}; + }; + /** An exception that indicates that the user has requested help */ class Help : public Error @@ -1419,6 +1427,52 @@ namespace args } }; + /** A mapping value flag class + * + * \tparam K the type to extract the argument as + * \tparam T the type to store the result map as + * \tparam Reader The function used to read the argument, taking the name, value, and destination reference + */ + template , typename Map = std::unordered_map> + class MapFlag : public ValueFlagBase + { + private: + const Map map; + T value; + + public: + + MapFlag(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const T &defaultValue = T()): ValueFlagBase(name, help, std::move(matcher)), map(map), value(defaultValue) + { + group.Add(*this); + } + + virtual ~MapFlag() {} + + virtual void ParseValue(const std::string &value) override + { + K key; + Reader(name, value, key); + auto it = map.find(key); + if (it == std::end(map)) + { + std::ostringstream problem; + problem << "Could not find key '" << key << "' in map for arg '" << name << "'"; + throw MapError(problem.str()); + } else + { + this->value = it->second; + } + } + + /** Get the value + */ + T &Get() noexcept + { + return value; + } + }; + /** An argument-accepting flag class that pushes the found values into a list * * \tparam T the type to extract the argument as diff --git a/test.cxx b/test.cxx index 4d4e6ef..bcccdf4 100644 --- a/test.cxx +++ b/test.cxx @@ -341,3 +341,56 @@ TEST_CASE("Required argument separation being violated throws an error", "[args] REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"-b", "test"}), args::ParseError); REQUIRE_NOTHROW(parser.ParseArgs(std::vector{"--bar", "test"})); } + +enum class MappingEnum +{ + def, + foo, + bar, + red, + yellow, + green +}; + +#include +#include +#include + +void ToLowerReader(const std::string &name, const std::string &value, std::string &destination) +{ + destination = value; + std::transform(destination.begin(), destination.end(), destination.begin(), ::tolower); +} + +TEST_CASE("Mapping types work as needed", "[args]") +{ + std::unordered_map map{ + {"default", MappingEnum::def}, + {"foo", MappingEnum::foo}, + {"bar", MappingEnum::bar}, + {"red", MappingEnum::red}, + {"yellow", MappingEnum::yellow}, + {"green", MappingEnum::green}}; + args::ArgumentParser parser("This is a test program.", "This goes after the options."); + args::MapFlag dmf(parser, "DMF", "Maps string to an enum", args::Matcher{"dmf"}, map); + args::MapFlag mf(parser, "MF", "Maps string to an enum", args::Matcher{"mf"}, map); + args::MapFlag cimf(parser, "CIMF", "Maps string to an enum case-insensitively", args::Matcher{"cimf"}, map); + //args::MapFlagList mfl(parser, "MFL", "Maps string to an enum list", args::Matcher{"mfl"}, map); + //args::MapPositional mp(parser, "MP", "Maps string to an enum", args::Matcher{"mp"}, map); + //args::MapPositionalList mpl(parser, "MPL", "Maps string to an enum list", args::Matcher{"mpl"}, map); + //parser.ParseArgs(std::vector{"--mf=red", "--cimf=YeLLoW", "--mfl=bar", "foo", "--mfl=green", "red", "--mfl", "bar", "default"}); + parser.ParseArgs(std::vector{"--mf=red", "--cimf=YeLLoW"}); + REQUIRE_FALSE(dmf); + REQUIRE(args::get(dmf) == MappingEnum::def); + REQUIRE(mf); + REQUIRE(args::get(mf) == MappingEnum::red); + REQUIRE(cimf); + REQUIRE(args::get(cimf) == MappingEnum::yellow); + //REQUIRE(mfl); + //REQUIRE(args::get(mfl) == std::vector{MappingEnum::bar, MappingEnum::green, MappingEnum::bar}); + //REQUIRE(mp); + //REQUIRE(args::get(mp) == MappingEnum::foo); + //REQUIRE(mpl); + //REQUIRE(args::get(mpl) == std::vector{MappingEnum::red, MappingEnum::def}); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector{"--mf=YeLLoW"}), args::MapError); +} -- cgit v1.2.1