Ver Fonte

refactor: clean up the implementation of container parsing by using traits

Sam Jaffe há 3 anos atrás
pai
commit
09d96bf6ca
1 ficheiros alterados com 49 adições e 39 exclusões
  1. 49 39
      include/program_args/utilities.h

+ 49 - 39
include/program_args/utilities.h

@@ -15,6 +15,21 @@ struct is_repeatable<F, std::enable_if_t<!std::is_void_v<
 
 template <typename T>
 constexpr bool const is_repeatable_v = is_repeatable<T>::value;
+
+template <typename T, typename = void> struct is_container : std::false_type {};
+template <> struct is_container<std::string> : std::false_type {};
+template <typename T>
+struct is_container<T, std::void_t<typename T::value_type>> : std::true_type {};
+
+template <typename T>
+constexpr bool const is_container_v = is_container<T>::value;
+
+template <typename T, typename = void> struct is_associative : std::false_type {};
+template <typename T>
+struct is_associative<T, std::void_t<typename T::mapped_type>> : std::true_type {};
+
+template <typename T>
+constexpr bool const is_associative_v = is_associative<T>::value;
 }
 
 namespace program {
@@ -38,8 +53,33 @@ template <typename T, typename = void> struct conversion_helper;
  * \return An object of the given type
  */
 template <typename T>
-T convert(std::string const & name, std::string const & data) {
+T convert(std::string const & name, std::string const & data) try {
   return conversion_helper<T>{}(data);
+} catch (std::exception const & ex) {
+  throw ArgumentStructureError(ex.what(), name);
+}
+
+template <typename T, typename V = typename T::mapped_type>
+T convert_associative(std::string const & name, std::vector<std::string> const &args) {
+  T rval;
+  for (std::string const &arg : args) {
+    size_t pos = arg.find('=');
+    if (pos == std::string::npos) {
+      throw ArgumentStructureError("expected argument of the form key=value, got " + arg,
+                                   name);
+    }
+    rval.emplace(arg.substr(0, pos), convert<V>(name, arg.substr(pos + 1)));
+  }
+  return rval;
+}
+
+template <typename T, typename V = typename T::value_type>
+T convert_container(std::string const & name, std::vector<std::string> const &args) {
+  T rval;
+  for (std::string const &arg : args) {
+    rval.insert(rval.end(), convert<V>(name, arg));
+  }
+  return rval;
 }
 
 /**
@@ -54,17 +94,15 @@ T convert(std::string const & name, std::string const & data) {
  */
 template <typename T>
 T convert(std::string const & name, std::vector<std::string> const & data) {
-  conversion_helper<T> helper;
-  try {
-    if constexpr (traits::is_repeatable_v<decltype(helper)>) {
-      return helper(data);
-    } else if (data.size() == 1) {
-      return helper(data.front());
-    }
-  } catch (std::exception const & ex) {
-    throw ArgumentStructureError(ex.what(), name);
+  if constexpr (traits::is_associative_v<T>) {
+    return convert_associative<T>(name, data);
+  } else if constexpr (traits::is_container_v<T> && !std::is_constructible_v<T, std::string>) {
+    return convert_container<T>(name, data);
+  } else if (data.size() == 1) {
+    return convert<T>(name, data[0]);
+  } else {
+    throw ArgumentStructureError("Repeated option not allowed", name);
   }
-  throw ArgumentStructureError("Repeated option not allowed", name);
 }
 
 template <typename T>
@@ -77,34 +115,6 @@ template <> struct conversion_helper<int> {
   int operator()(std::string const & str) const { return std::stoi(str); }
 };
 
-template <typename T>
-struct conversion_helper<std::vector<T>> : conversion_helper<T> {
-  using conversion_helper<T>::operator();
-  std::vector<T> operator()(std::vector<std::string> const & data) const {
-    std::vector<T> rval;
-    for (auto & str : data) {
-      rval.push_back((*this)(str));
-    }
-    return rval;
-  }
-};
-
-template <typename T>
-struct conversion_helper<std::map<std::string, T>> : conversion_helper<T> {
-  using conversion_helper<T>::operator();
-  std::map<std::string, T> operator()(std::vector<std::string> const & data) const {
-    std::map<std::string, T> rval;
-    for (auto & str : data) {
-      size_t pos = str.find('=');
-      if (pos == std::string::npos) {
-        throw std::invalid_argument("expected argument of the form key=value, got " + str);
-      }
-      rval.emplace(str.substr(0, pos), (*this)(str.substr(pos + 1)));
-    }
-    return rval;
-  }
-};
-
 using std::to_string;
 template <typename T> std::string to_string(T const &) { return "?"; }
 inline std::string to_string(char const * str) { return str; }