Sam Jaffe 4 роки тому
батько
коміт
071849333a
2 змінених файлів з 89 додано та 16 видалено
  1. 23 3
      include/program_args/arguments.h
  2. 66 13
      include/program_args/arguments_impl.hpp

+ 23 - 3
include/program_args/arguments.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include <map>
+#include <set>
 #include <string>
 #include <vector>
 
@@ -9,6 +10,7 @@ namespace program {
 template <typename Impl> class Arguments {
 private:
   struct Argument;
+  struct Flag;
   struct Option;
   template <typename, typename> struct WithDefault;
 
@@ -17,11 +19,14 @@ public:
   Arguments(int argc, char const * const * const argv);
 
 protected:
+  auto argument(size_t index, std::string const & name,
+                std::string const & description = "");
+  auto flag(std::string const & name, std::string const & description = "");
+  auto flag(std::string const & name, char abbrev,
+            std::string const & description = "");
   auto option(std::string const & name, std::string const & description = "");
   auto option(std::string const & name, char abbrev,
               std::string const & description = "");
-  auto argument(size_t index, std::string const & name,
-                std::string const & description = "");
 
 private:
   void usage() const;
@@ -32,13 +37,15 @@ private:
   std::map<std::string, std::string> argument_descriptions;
   std::map<size_t, std::string> argument_names;
   std::map<std::string, std::string> option_descriptions;
-  std::map<std::string, std::string> option_name_mapping;
+  std::map<std::string, std::string> option_names;
+  std::set<std::string> flag_names;
 
   // Data/Output variables
   std::string program;
   size_t optional_from{std::numeric_limits<size_t>::max()};
   std::vector<std::string> arguments;
   std::map<std::string, std::vector<std::string>> options;
+  std::map<std::string, int> flags;
 };
 
 template <typename Impl> struct Arguments<Impl>::Argument {
@@ -54,6 +61,19 @@ template <typename Impl> struct Arguments<Impl>::Argument {
   explicit operator bool() const;
 };
 
+template <typename Impl> struct Arguments<Impl>::Flag {
+  Arguments<Impl> * self;
+  std::string name;
+  char abbrev;
+  std::string description;
+  bool default_value;
+
+  auto operator=(bool && value);
+  operator bool() const;
+  template <typename T> operator T() const;
+  bool primed(bool inv) const;
+};
+
 template <typename Impl> struct Arguments<Impl>::Option {
   Arguments<Impl> * self;
   std::string name;

+ 66 - 13
include/program_args/arguments_impl.hpp

@@ -48,6 +48,40 @@ template <typename Impl> bool Arguments<Impl>::Argument::primed() const {
 
 namespace program {
 
+template <typename Impl>
+template <typename T>
+Arguments<Impl>::Flag::operator T() const {
+  return primed(false) ? static_cast<T>(self->flags.at(name)) : T();
+}
+
+template <typename Impl> Arguments<Impl>::Flag::operator bool() const {
+  return primed(true) ? static_cast<bool>(self->flags.at(name)) : default_value;
+}
+
+template <typename Impl> auto Arguments<Impl>::Flag::operator=(bool && value) {
+  default_value = value;
+  return *this;
+}
+
+template <typename Impl> bool Arguments<Impl>::Flag::primed(bool inv) const {
+  if (self->primed_) { return self->flags.count(name); }
+  std::vector<std::string> aliases{"--" + name};
+  if (abbrev) { aliases.emplace_back(std::string{'-', abbrev}); }
+  if (inv) { aliases.emplace_back("--no-" + name); };
+  for (auto & alias : aliases) {
+    if (!self->option_names.emplace(alias, name).second) {
+      throw ArgumentStructureError("Duplicate option string", alias);
+    }
+  }
+  self->option_descriptions.emplace(join("/", aliases), description);
+  self->flag_names.emplace(name);
+  return false;
+}
+
+}
+
+namespace program {
+
 template <typename Impl>
 template <typename T>
 Arguments<Impl>::Option::operator T() const {
@@ -69,7 +103,7 @@ template <typename Impl> bool Arguments<Impl>::Option::primed() const {
   std::vector<std::string> aliases{"--" + name};
   if (abbrev) { aliases.emplace_back(std::string{'-', abbrev}); }
   for (auto & alias : aliases) {
-    if (!self->option_name_mapping.emplace(alias, name).second) {
+    if (!self->option_names.emplace(alias, name).second) {
       throw ArgumentStructureError("Duplicate option string", alias);
     }
   }
@@ -80,6 +114,7 @@ template <typename Impl> bool Arguments<Impl>::Option::primed() const {
 }
 
 #define arg_equals(str) !strncmp(argv[i], str, sizeof(str))
+#define arg_starts_with(str) !strncmp(argv[i], str, strlen(str))
 namespace program {
 
 template <typename Impl>
@@ -102,14 +137,19 @@ Arguments<Impl>::Arguments(int argc, char const * const * const argv) {
       arguments.insert(arguments.end(), &argv[i + 1], &argv[argc]);
       break;
     } else if (argv[i][0] == '-') {
-      if (auto it = option_name_mapping.find(argv[i]);
-          it != option_name_mapping.end()) {
-        options[it->second].emplace_back(argv[i + 1]);
+      auto it = option_names.find(argv[i]);
+      if (it == option_names.end()) { throw NotAnArgumentError(argv[i]); }
+      auto & name = it->second;
+      // TODO: Flag handling for e.g. -v2 -vv
+      if (!flag_names.count(name)) {
+        options[name].emplace_back(argv[i + 1]);
+        // TODO: Arity
+        ++i;
+      } else if (arg_starts_with("--no-")) {
+        flags[name] = 0;
       } else {
-        throw NotAnArgumentError(argv[i]);
+        ++flags[name];
       }
-      // TODO: Arity
-      ++i;
     } else {
       arguments.emplace_back(argv[i]);
     }
@@ -132,6 +172,24 @@ template <typename Impl> void Arguments<Impl>::usage() const {
   }
 }
 
+template <typename Impl>
+auto Arguments<Impl>::argument(size_t index, std::string const & name,
+                               std::string const & description) {
+  return Argument{this, index, false, name, description};
+}
+
+template <typename Impl>
+auto Arguments<Impl>::flag(std::string const & name,
+                           std::string const & description) {
+  return Flag{this, name, 0, description, false};
+}
+
+template <typename Impl>
+auto Arguments<Impl>::flag(std::string const & name, char abbrev,
+                           std::string const & description) {
+  return Flag{this, name, abbrev, description, false};
+}
+
 template <typename Impl>
 auto Arguments<Impl>::option(std::string const & name,
                              std::string const & description) {
@@ -144,11 +202,6 @@ auto Arguments<Impl>::option(std::string const & name, char abbrev,
   return Option{this, name, abbrev, description};
 }
 
-template <typename Impl>
-auto Arguments<Impl>::argument(size_t index, std::string const & name,
-                               std::string const & description) {
-  return Argument{this, index, false, name, description};
-}
-
 }
 #undef arg_equals
+#undef arg_starts_with