|
@@ -17,50 +17,14 @@
|
|
|
#include <variant>
|
|
#include <variant>
|
|
|
#include <vector>
|
|
#include <vector>
|
|
|
|
|
|
|
|
-#include "any_of.h"
|
|
|
|
|
|
|
+#include "string_utils/any_of.h"
|
|
|
|
|
+#include "string_utils/forwards.h"
|
|
|
|
|
+#include "string_utils/traits.h"
|
|
|
|
|
|
|
|
#define SAFE_NUMBER_PARSE(func, ...) \
|
|
#define SAFE_NUMBER_PARSE(func, ...) \
|
|
|
[](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
|
|
[](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
|
|
|
|
|
|
|
|
-namespace string_utils {
|
|
|
|
|
-// A helper object for providing partial specializations for casting
|
|
|
|
|
-template <typename, typename = void> struct cast_helper;
|
|
|
|
|
-
|
|
|
|
|
-// The main parser
|
|
|
|
|
-template <typename T, typename S> std::pair<T, bool> cast(S const &str) noexcept;
|
|
|
|
|
-template <typename T> bool cast(std::string_view str, T & to) noexcept;
|
|
|
|
|
-template <typename S, typename T> bool cast(std::vector<S> const &strs, T & to) noexcept;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
namespace string_utils::detail {
|
|
namespace string_utils::detail {
|
|
|
-template <typename> struct always_false : std::false_type {};
|
|
|
|
|
-template <typename, typename, typename = void> struct has_cast_helper : std::false_type {};
|
|
|
|
|
-template <typename T, typename S>
|
|
|
|
|
-struct has_cast_helper<T, S, std::void_t<decltype(cast_helper<T>{}(S{}, T{}))>>
|
|
|
|
|
- : std::true_type {};
|
|
|
|
|
-
|
|
|
|
|
-template <typename, typename = void> struct is_tuple : std::false_type {};
|
|
|
|
|
-template <typename T>
|
|
|
|
|
-struct is_tuple<T, std::void_t<typename std::tuple_size<T>::type>> : std::true_type {};
|
|
|
|
|
-
|
|
|
|
|
-template <typename, 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, typename = void> struct is_container : std::false_type {};
|
|
|
|
|
-template <typename T>
|
|
|
|
|
-struct is_container<T, std::void_t<typename T::value_type>> : std::true_type {};
|
|
|
|
|
-
|
|
|
|
|
-template <typename T> struct decay { using type = std::decay_t<T>; };
|
|
|
|
|
-template <template <typename...> class C, typename... Ts>
|
|
|
|
|
-struct decay<C<Ts...>> { using type = C<std::decay_t<Ts>...>; };
|
|
|
|
|
-template <typename T> using decay_t = typename decay<std::decay_t<T>>::type;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-namespace string_utils::detail {
|
|
|
|
|
-template <typename Tuple, size_t... Is>
|
|
|
|
|
-bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
|
|
|
|
|
- std::index_sequence<Is...>) noexcept;
|
|
|
|
|
template <typename T, typename F>
|
|
template <typename T, typename F>
|
|
|
bool cast_number(std::string_view str, T & to, F func) noexcept;
|
|
bool cast_number(std::string_view str, T & to, F func) noexcept;
|
|
|
template <typename S> std::vector<S> keyval(S const &input);
|
|
template <typename S> std::vector<S> keyval(S const &input);
|
|
@@ -68,56 +32,6 @@ template <typename S> std::vector<S> keyval(S const &input);
|
|
|
|
|
|
|
|
namespace string_utils {
|
|
namespace string_utils {
|
|
|
|
|
|
|
|
-template <typename T>
|
|
|
|
|
-bool cast(std::string_view str, T & to) noexcept { return cast_helper<T>{}(str, to); }
|
|
|
|
|
-
|
|
|
|
|
-template <typename S, typename T>
|
|
|
|
|
-bool cast(std::vector<S> const &strs, T & to) noexcept {
|
|
|
|
|
- if constexpr (detail::is_tuple<T>{}) {
|
|
|
|
|
- constexpr size_t N = std::tuple_size_v<T>;
|
|
|
|
|
- return strs.size() == N && detail::cast_tuple(strs, to, std::make_index_sequence<N>{});
|
|
|
|
|
- } else if constexpr (detail::is_associative<T>{}) {
|
|
|
|
|
- for (S const &elem : strs) {
|
|
|
|
|
- auto [tmp, success] = cast<typename T::value_type>(detail::keyval(elem));
|
|
|
|
|
- if (!success) { return false; }
|
|
|
|
|
- to.insert(std::move(tmp));
|
|
|
|
|
- }
|
|
|
|
|
- } else if constexpr (detail::is_container<T>{}) {
|
|
|
|
|
- for (S const &elem : strs) {
|
|
|
|
|
- auto [tmp, success] = cast<typename T::value_type>(elem);
|
|
|
|
|
- if (!success) { return false; }
|
|
|
|
|
- to.insert(to.end(), std::move(tmp));
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- return cast_helper<T>{}(strs, to);
|
|
|
|
|
- }
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-template <typename V, typename S, typename T> bool maybe_cast(S const & str, T & to) noexcept {
|
|
|
|
|
- auto [rval, found] = cast<V>(str);
|
|
|
|
|
- if (found) { to = std::move(rval); }
|
|
|
|
|
- return found;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-template <typename... Ts>
|
|
|
|
|
-struct cast_helper<std::variant<Ts...>> {
|
|
|
|
|
- bool operator()(std::string_view str, std::variant<Ts...> & to) const noexcept {
|
|
|
|
|
- return (maybe_cast<Ts>(str, to) || ...);
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-template <typename T>
|
|
|
|
|
-struct cast_helper<std::optional<T>> {
|
|
|
|
|
- bool operator()(std::string_view str, std::optional<T> & to) const noexcept {
|
|
|
|
|
- return maybe_cast<T>(str, to) || true;
|
|
|
|
|
- }
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-namespace string_utils {
|
|
|
|
|
-
|
|
|
|
|
inline bool cast(std::string_view str, std::string & to) noexcept {
|
|
inline bool cast(std::string_view str, std::string & to) noexcept {
|
|
|
to = std::string(str);
|
|
to = std::string(str);
|
|
|
return true;
|
|
return true;
|
|
@@ -162,6 +76,60 @@ inline bool cast(std::string_view str, bool & to) noexcept {
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+namespace string_utils {
|
|
|
|
|
+
|
|
|
|
|
+template <typename V, typename S, typename T> bool maybe_cast(S const & str, T & to) noexcept {
|
|
|
|
|
+ auto [rval, found] = cast<V>(str);
|
|
|
|
|
+ if (found) { to = std::move(rval); }
|
|
|
|
|
+ return found;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+template <typename T> struct cast_helper<T, std::enable_if_t<detail::is_container<T>{}>> {
|
|
|
|
|
+ template <typename S>
|
|
|
|
|
+ bool operator()(std::vector<S> const &strs, T & to) const noexcept {
|
|
|
|
|
+ for (S const &elem : strs) {
|
|
|
|
|
+ if constexpr (detail::is_associative<T>{}) {
|
|
|
|
|
+ auto [tmp, success] = cast<typename T::value_type>(detail::keyval(elem));
|
|
|
|
|
+ if (!success) { return false; }
|
|
|
|
|
+ to.insert(std::move(tmp));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ auto [tmp, success] = cast<typename T::value_type>(elem);
|
|
|
|
|
+ if (!success) { return false; }
|
|
|
|
|
+ to.insert(to.end(), std::move(tmp));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+template <typename T> struct cast_helper<T, std::enable_if_t<detail::is_tuple<T>{}>> {
|
|
|
|
|
+ template <typename S, size_t... Is>
|
|
|
|
|
+ bool cast_tuple(S const & str, T & to, std::index_sequence<Is...>) const noexcept {
|
|
|
|
|
+ return ((cast(str[Is], std::get<Is>(to))) && ...);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ template <typename S> bool operator()(std::vector<S> const &strs, T & to) const noexcept {
|
|
|
|
|
+ constexpr size_t N = std::tuple_size_v<T>;
|
|
|
|
|
+ return strs.size() == N && cast_tuple(strs, to, std::make_index_sequence<N>{});
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+template <typename... Ts>
|
|
|
|
|
+struct cast_helper<std::variant<Ts...>> {
|
|
|
|
|
+ bool operator()(std::string_view str, std::variant<Ts...> & to) const noexcept {
|
|
|
|
|
+ return (maybe_cast<Ts>(str, to) || ...);
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+template <typename T>
|
|
|
|
|
+struct cast_helper<std::optional<T>> {
|
|
|
|
|
+ bool operator()(std::string_view str, std::optional<T> & to) const noexcept {
|
|
|
|
|
+ return maybe_cast<T>(str, to) || true;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
namespace string_utils::detail {
|
|
namespace string_utils::detail {
|
|
|
template <typename Tuple, size_t... Is>
|
|
template <typename Tuple, size_t... Is>
|
|
|
bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
|
|
bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
|
|
@@ -185,12 +153,18 @@ template <typename S> std::vector<S> keyval(S const &input) {
|
|
|
|
|
|
|
|
// This should be placed last in the file
|
|
// This should be placed last in the file
|
|
|
namespace string_utils {
|
|
namespace string_utils {
|
|
|
-template <typename T, typename S> std::pair<T, bool> cast(S const & str) noexcept {
|
|
|
|
|
|
|
+template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
|
|
|
std::pair<detail::decay_t<T>, bool> rval;
|
|
std::pair<detail::decay_t<T>, bool> rval;
|
|
|
rval.second = cast(str, rval.first);
|
|
rval.second = cast(str, rval.first);
|
|
|
return rval;
|
|
return rval;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+template <typename T, typename S>
|
|
|
|
|
+std::pair<T, bool> cast(std::vector<S> const & strs) noexcept {
|
|
|
|
|
+ std::pair<detail::decay_t<T>, bool> rval;
|
|
|
|
|
+ rval.second = cast(strs, rval.first);
|
|
|
|
|
+ return rval;
|
|
|
|
|
+}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
#undef CAST_NUMBER_IMPL
|
|
#undef CAST_NUMBER_IMPL
|