diff options
author | Taylor C. Richberger <Taywee@gmx.com> | 2016-05-11 18:10:53 -0600 |
---|---|---|
committer | Taylor C. Richberger <Taywee@gmx.com> | 2016-05-11 18:10:53 -0600 |
commit | 1b9b9823d4619ef62783e25768b9460dcde55a74 (patch) | |
tree | 37c60846dfdc469cc591b82390aac8728bed9ccf | |
parent | bump version (diff) | |
download | args.hxx-1b9b9823d4619ef62783e25768b9460dcde55a74.tar.xz |
add mapping flag, throw maperror, ensure throwing works properly
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | args.hxx | 54 | ||||
-rw-r--r-- | test.cxx | 53 |
3 files changed, 110 insertions, 1 deletions
@@ -492,7 +492,7 @@ Argument 'numbers' received invalid value type 'a' ```cpp #include <iostream> #include <tuple> -#include <args.hxx> + std::istream& operator>>(std::istream& is, std::tuple<int, int>& ints) { is >> std::get<0>(ints); @@ -501,6 +501,8 @@ std::istream& operator>>(std::istream& is, std::tuple<int, int>& ints) return is; } +#include <args.hxx> + void DoublesReader(const std::string &name, const std::string &value, std::tuple<double, double> &destination) { size_t commapos = 0; @@ -32,6 +32,7 @@ #include <string> #include <tuple> #include <vector> +#include <unordered_map> #include <unordered_set> #include <type_traits> @@ -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 K, typename T, void (*Reader)(const std::string &, const std::string &, K&) = ValueReader<K>, typename Map = std::unordered_map<K, T>> + 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 @@ -341,3 +341,56 @@ TEST_CASE("Required argument separation being violated throws an error", "[args] REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"-b", "test"}), args::ParseError); REQUIRE_NOTHROW(parser.ParseArgs(std::vector<std::string>{"--bar", "test"})); } + +enum class MappingEnum +{ + def, + foo, + bar, + red, + yellow, + green +}; + +#include <unordered_map> +#include <algorithm> +#include <string> + +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<std::string, MappingEnum> 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<std::string, MappingEnum> dmf(parser, "DMF", "Maps string to an enum", args::Matcher{"dmf"}, map); + args::MapFlag<std::string, MappingEnum> mf(parser, "MF", "Maps string to an enum", args::Matcher{"mf"}, map); + args::MapFlag<std::string, MappingEnum, ToLowerReader> cimf(parser, "CIMF", "Maps string to an enum case-insensitively", args::Matcher{"cimf"}, map); + //args::MapFlagList<std::string, MappingEnum> mfl(parser, "MFL", "Maps string to an enum list", args::Matcher{"mfl"}, map); + //args::MapPositional<std::string, MappingEnum> mp(parser, "MP", "Maps string to an enum", args::Matcher{"mp"}, map); + //args::MapPositionalList<std::string, MappingEnum> mpl(parser, "MPL", "Maps string to an enum list", args::Matcher{"mpl"}, map); + //parser.ParseArgs(std::vector<std::string>{"--mf=red", "--cimf=YeLLoW", "--mfl=bar", "foo", "--mfl=green", "red", "--mfl", "bar", "default"}); + parser.ParseArgs(std::vector<std::string>{"--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>{MappingEnum::bar, MappingEnum::green, MappingEnum::bar}); + //REQUIRE(mp); + //REQUIRE(args::get(mp) == MappingEnum::foo); + //REQUIRE(mpl); + //REQUIRE(args::get(mpl) == std::vector<MappingEnum>{MappingEnum::red, MappingEnum::def}); + REQUIRE_THROWS_AS(parser.ParseArgs(std::vector<std::string>{"--mf=YeLLoW"}), args::MapError); +} |