aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTaylor C. Richberger <Taywee@gmx.com>2016-05-11 20:40:18 -0600
committerTaylor C. Richberger <Taywee@gmx.com>2016-05-11 20:40:18 -0600
commitb8870fac3ab825d7d3b42483321f6ba32b67ec8f (patch)
tree5ef1c18abcfddabea96f4a3fab6a8c593d123514
parentadd mapping flag, throw maperror, ensure throwing works properly (diff)
downloadargs.hxx-b8870fac3ab825d7d3b42483321f6ba32b67ec8f.tar.xz
finish mapping types
-rw-r--r--Doxyfile2
-rw-r--r--README.md48
-rw-r--r--args.hxx220
-rw-r--r--test.cxx21
4 files changed, 250 insertions, 41 deletions
diff --git a/Doxyfile b/Doxyfile
index 871141a..171f0eb 100644
--- a/Doxyfile
+++ b/Doxyfile
@@ -38,7 +38,7 @@ PROJECT_NAME = "args"
# could be handy for archiving the generated documentation or if some version
# control system is used.
-PROJECT_NUMBER = 4.0.2
+PROJECT_NUMBER = 4.1.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
diff --git a/README.md b/README.md
index 18ac5e4..f4435d0 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,10 @@ The code can be downloaded at https://github.com/Taywee/args
There are also somewhat extensive examples below.
+You can find the complete test cases at
+https://github.com/Taywee/args/blob/master/test.cxx, which should very well
+describe the usage, as it's built to push the boundaries.
+
# What does it do?
It:
@@ -867,3 +871,47 @@ int main(int argc, char **argv)
This goes after the options.
%
```
+
+# Mapping positionals
+
+I haven't written out a long example for this, but here's the test case you should be able to discern the meaning from:
+
+```cpp
+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", map);
+ args::MapPositionalList<std::string, MappingEnum> mpl(parser, "MPL", "Maps string to an enum list", map);
+ parser.ParseArgs(std::vector<std::string>{"--mf=red", "--cimf=YeLLoW", "--mfl=bar", "foo", "--mfl=green", "red", "--mfl", "bar", "default"});
+ 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);
+}
+```
diff --git a/args.hxx b/args.hxx
index aade975..9dfe9d5 100644
--- a/args.hxx
+++ b/args.hxx
@@ -386,7 +386,7 @@ namespace args
virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefi, const std::string &shortSeparator, const std::string &longSeparator) const override
{
std::tuple<std::string, std::string> description;
- std::get<0>(description) = name;
+ std::get<0>(description) = Name();
std::get<1>(description) = help;
return description;
}
@@ -459,7 +459,7 @@ namespace args
virtual std::tuple<std::string, std::string> GetDescription(const std::string &shortPrefix, const std::string &longPrefix, const std::string &shortSeparator, const std::string &longSeparator) const override
{
std::tuple<std::string, std::string> description;
- const std::vector<std::string> flagStrings(matcher.GetFlagStrings(shortPrefix, longPrefix, name, shortSeparator, longSeparator));
+ const std::vector<std::string> flagStrings(matcher.GetFlagStrings(shortPrefix, longPrefix, Name(), shortSeparator, longSeparator));
std::ostringstream flagstream;
for (auto it = std::begin(flagStrings); it != std::end(flagStrings); ++it)
{
@@ -477,14 +477,14 @@ namespace args
/** Base class for positional options
*/
- class PosBase : public NamedBase
+ class PositionalBase : public NamedBase
{
protected:
bool ready;
public:
- PosBase(const std::string &name, const std::string &help) : NamedBase(name, help), ready(true) {}
- virtual ~PosBase() {}
+ PositionalBase(const std::string &name, const std::string &help) : NamedBase(name, help), ready(true) {}
+ virtual ~PositionalBase() {}
bool Ready()
{
@@ -572,13 +572,13 @@ namespace args
/** Get the next ready positional, or nullptr if there is none
*
- * \return the first ready PosBase pointer, or nullptr if there is no match
+ * \return the first ready PositionalBase pointer, or nullptr if there is no match
*/
- PosBase *GetNextPositional()
+ PositionalBase *GetNextPositional()
{
for (Base *child: children)
{
- PosBase *next = dynamic_cast<PosBase *>(child);
+ PositionalBase *next = dynamic_cast<PositionalBase *>(child);
Group *group = dynamic_cast<Group *>(child);
if (group)
{
@@ -695,7 +695,7 @@ namespace args
for (const auto &child: children)
{
const Group *group = dynamic_cast<Group *>(child);
- const PosBase *pos = dynamic_cast<PosBase *>(child);
+ const PositionalBase *pos = dynamic_cast<PositionalBase *>(child);
if (group)
{
std::vector<std::string> groupNames(group->GetPosNames());
@@ -1216,7 +1216,7 @@ namespace args
}
} else
{
- PosBase *pos = GetNextPositional();
+ PositionalBase *pos = GetNextPositional();
if (pos)
{
pos->ParseValue(chunk);
@@ -1427,11 +1427,55 @@ namespace args
}
};
+ /** An argument-accepting flag class that pushes the found values into a list
+ *
+ * \tparam T the type to extract the argument as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The function used to read the argument, taking the name, value, and destination reference
+ */
+ template <
+ typename T,
+ typename List = std::vector<T>,
+ void (*Reader)(const std::string &, const std::string &, T&) = ValueReader<T>>
+ class ValueFlagList : public ValueFlagBase
+ {
+ private:
+ List values;
+
+ public:
+
+ ValueFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), values(defaultValues)
+ {
+ group.Add(*this);
+ }
+
+ virtual ~ValueFlagList() {}
+
+ virtual void ParseValue(const std::string &value) override
+ {
+ values.emplace_back();
+ Reader(name, value, values.back());
+ }
+
+ /** Get the values
+ */
+ List &Get() noexcept
+ {
+ return values;
+ }
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
+ };
+
/** 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
+ * \tparam T the type to store the result as
+ * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
*/
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
@@ -1473,42 +1517,57 @@ namespace args
}
};
- /** An argument-accepting flag class that pushes the found values into a list
+ /** A mapping value flag class
*
- * \tparam T the type to extract the argument as
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
* \tparam List the list type that houses the values
- * \tparam Reader The function used to read the argument, taking the name, value, and destination reference
+ * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
*/
- template <
- typename T,
- typename List = std::vector<T>,
- void (*Reader)(const std::string &, const std::string &, T&) = ValueReader<T>>
- class ValueFlagList : public ValueFlagBase
+ template <typename K, typename T, typename List = std::vector<T>, void (*Reader)(const std::string &, const std::string &, K&) = ValueReader<K>, typename Map = std::unordered_map<K, T>>
+ class MapFlagList : public ValueFlagBase
{
private:
+ const Map map;
List values;
public:
- ValueFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), values(defaultValues)
+ MapFlagList(Group &group, const std::string &name, const std::string &help, Matcher &&matcher, const Map &map, const List &defaultValues = List()): ValueFlagBase(name, help, std::move(matcher)), map(map), values(defaultValues)
{
group.Add(*this);
}
- virtual ~ValueFlagList() {}
+ virtual ~MapFlagList() {}
virtual void ParseValue(const std::string &value) override
{
- values.emplace_back();
- Reader(name, value, values.back());
+ 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->values.emplace_back(it->second);
+ }
}
- /** Get the values
+ /** Get the value
*/
List &Get() noexcept
{
return values;
}
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
};
/** A positional argument class
@@ -1517,12 +1576,12 @@ namespace args
* \tparam Reader The function used to read the argument, taking the name, value, and destination reference
*/
template <typename T, void (*Reader)(const std::string &, const std::string &, T&) = ValueReader<T>>
- class Positional : public PosBase
+ class Positional : public PositionalBase
{
private:
T value;
public:
- Positional(Group &group, const std::string &name, const std::string &help, const T &defaultValue = T()): PosBase(name, help), value(defaultValue)
+ Positional(Group &group, const std::string &name, const std::string &help, const T &defaultValue = T()): PositionalBase(name, help), value(defaultValue)
{
group.Add(*this);
}
@@ -1554,13 +1613,13 @@ namespace args
typename T,
typename List = std::vector<T>,
void (*Reader)(const std::string &, const std::string &, T&) = ValueReader<T>>
- class PositionalList : public PosBase
+ class PositionalList : public PositionalBase
{
private:
List values;
public:
- PositionalList(Group &group, const std::string &name, const std::string &help, const List &defaultValues = List()): PosBase(name, help), values(defaultValues)
+ PositionalList(Group &group, const std::string &name, const std::string &help, const List &defaultValues = List()): PositionalBase(name, help), values(defaultValues)
{
group.Add(*this);
}
@@ -1586,4 +1645,107 @@ namespace args
return values;
}
};
+
+ /** A positional argument mapping class
+ *
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
+ * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
+ */
+ template <typename K, typename T, void (*Reader)(const std::string &, const std::string &, K&) = ValueReader<K>, typename Map = std::unordered_map<K, T>>
+ class MapPositional : public PositionalBase
+ {
+ private:
+ const Map map;
+ T value;
+
+ public:
+
+ MapPositional(Group &group, const std::string &name, const std::string &help, const Map &map, const T &defaultValue = T()): PositionalBase(name, help), map(map), value(defaultValue)
+ {
+ group.Add(*this);
+ }
+
+ virtual ~MapPositional() {}
+
+ 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;
+ ready = false;
+ matched = true;
+ }
+ }
+
+ /** Get the value
+ */
+ T &Get() noexcept
+ {
+ return value;
+ }
+ };
+
+ /** A mapping value flag class
+ *
+ * \tparam K the type to extract the argument as
+ * \tparam T the type to store the result as
+ * \tparam List the list type that houses the values
+ * \tparam Reader The function used to read the argument into the key type, taking the name, value, and destination reference
+ * \tparam Map The Map type. Should operate like std::map or std::unordered_map
+ */
+ template <typename K, typename T, typename List = std::vector<T>, void (*Reader)(const std::string &, const std::string &, K&) = ValueReader<K>, typename Map = std::unordered_map<K, T>>
+ class MapPositionalList : public PositionalBase
+ {
+ private:
+ const Map map;
+ List values;
+
+ public:
+
+ MapPositionalList(Group &group, const std::string &name, const std::string &help, const Map &map, const List &defaultValues = List()): PositionalBase(name, help), map(map), values(defaultValues)
+ {
+ group.Add(*this);
+ }
+
+ virtual ~MapPositionalList() {}
+
+ 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->values.emplace_back(it->second);
+ matched = true;
+ }
+ }
+
+ /** Get the value
+ */
+ List &Get() noexcept
+ {
+ return values;
+ }
+
+ virtual std::string Name() const override
+ {
+ return name + std::string("...");
+ }
+ };
}
diff --git a/test.cxx b/test.cxx
index bcccdf4..b28a378 100644
--- a/test.cxx
+++ b/test.cxx
@@ -375,22 +375,21 @@ TEST_CASE("Mapping types work as needed", "[args]")
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"});
+ 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", map);
+ args::MapPositionalList<std::string, MappingEnum> mpl(parser, "MPL", "Maps string to an enum list", map);
+ parser.ParseArgs(std::vector<std::string>{"--mf=red", "--cimf=YeLLoW", "--mfl=bar", "foo", "--mfl=green", "red", "--mfl", "bar", "default"});
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(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);
}