aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md4
-rw-r--r--args.hxx54
-rw-r--r--test.cxx53
3 files changed, 110 insertions, 1 deletions
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 <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;
diff --git a/args.hxx b/args.hxx
index 9198afc..aade975 100644
--- a/args.hxx
+++ b/args.hxx
@@ -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
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<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);
+}