|
|
@@ -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; }
|