|
|
@@ -21,156 +21,135 @@
|
|
|
#include "string_utils/forwards.h"
|
|
|
#include "string_utils/traits.h"
|
|
|
|
|
|
-#define SAFE_NUMBER_PARSE(func, ...) \
|
|
|
- [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
|
|
|
+#define SAFE_NUMBER_PARSE(func, ...) \
|
|
|
+ [](char const * in, char ** out) { return func(in, out, ##__VA_ARGS__); }
|
|
|
|
|
|
namespace string_utils::detail {
|
|
|
-template <typename T, typename F>
|
|
|
-bool cast_number(std::string_view str, T & to, F func) noexcept;
|
|
|
-template <typename S> std::vector<S> keyval(S const &input);
|
|
|
-}
|
|
|
-
|
|
|
-namespace string_utils {
|
|
|
-
|
|
|
-inline bool cast(std::string_view str, std::string & to) noexcept {
|
|
|
- to = std::string(str);
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-inline bool cast(std::string_view str, long & to) noexcept {
|
|
|
- return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
|
|
|
-}
|
|
|
-
|
|
|
-inline bool cast(std::string_view str, long long & to) noexcept {
|
|
|
- return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
|
|
|
-}
|
|
|
-
|
|
|
-inline bool cast(std::string_view str, float & to) noexcept {
|
|
|
- return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
|
|
|
+template <typename Actual, typename Out>
|
|
|
+bool ctor_cast(Out & out, std::string_view str) noexcept {
|
|
|
+ auto [rval, found] = cast<Actual>(str);
|
|
|
+ if (found) { out = Out(std::move(rval)); }
|
|
|
+ return found;
|
|
|
}
|
|
|
|
|
|
-inline bool cast(std::string_view str, double & to) noexcept {
|
|
|
- return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
|
|
|
+template <typename Tuple, size_t... Is>
|
|
|
+bool cast_tuple(std::vector<std::string_view> const & str, Tuple & to,
|
|
|
+ std::index_sequence<Is...>) noexcept {
|
|
|
+ return ((cast(std::get<Is>(to), str[Is])) && ...);
|
|
|
}
|
|
|
|
|
|
-inline bool cast(std::string_view str, long double & to) noexcept {
|
|
|
- return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
|
|
|
+template <typename T, typename F>
|
|
|
+bool cast_number(std::string_view str, T & to, F func) noexcept {
|
|
|
+ char * counter = nullptr;
|
|
|
+ to = func(str.data(), &counter);
|
|
|
+ return counter == str.end();
|
|
|
}
|
|
|
|
|
|
-inline bool cast(std::string_view str, int & to) noexcept {
|
|
|
- auto [tmp, success] = cast<long>(str);
|
|
|
- to = static_cast<int>(tmp);
|
|
|
- return success && tmp == static_cast<long>(to);
|
|
|
+inline std::vector<std::string_view> keyval(std::string_view input) noexcept {
|
|
|
+ if (size_t const pos = input.find('='); pos < input.size()) {
|
|
|
+ return {input.substr(0, pos), input.substr(pos + 1)};
|
|
|
+ }
|
|
|
+ return {input};
|
|
|
}
|
|
|
|
|
|
-inline bool cast(std::string_view str, bool & to) noexcept {
|
|
|
+inline bool cast_bool(bool & out, std::string_view str) noexcept {
|
|
|
if (any_of(str, "true", "TRUE", "YES", "1")) {
|
|
|
- to = true;
|
|
|
+ out = true;
|
|
|
return true;
|
|
|
} else if (any_of(str, "false", "FALSE", "NO", "0")) {
|
|
|
- to = false;
|
|
|
+ out = false;
|
|
|
return true;
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
+// This should be placed last in the file
|
|
|
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));
|
|
|
- }
|
|
|
- }
|
|
|
+template <typename Out> bool cast(Out & out, std::string_view str) noexcept {
|
|
|
+ if constexpr (std::is_same_v<Out, long>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtol, 10));
|
|
|
+ } else if constexpr (std::is_same_v<Out, unsigned long>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoul, 10));
|
|
|
+ } else if constexpr (std::is_same_v<Out, long long>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoll, 10));
|
|
|
+ } else if constexpr (std::is_same_v<Out, unsigned long long>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtoull, 10));
|
|
|
+ } else if constexpr (std::is_same_v<Out, float>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtof));
|
|
|
+ } else if constexpr (std::is_same_v<Out, double>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtod));
|
|
|
+ } else if constexpr (std::is_same_v<Out, long double>) {
|
|
|
+ return detail::cast_number(str, out, SAFE_NUMBER_PARSE(std::strtold));
|
|
|
+ } else if constexpr (std::is_same_v<Out, bool>) {
|
|
|
+ return detail::cast_bool(out, str);
|
|
|
+ } else if constexpr (std::is_same_v<Out, char>) {
|
|
|
+ out = str[0];
|
|
|
+ return str.size() == 1;
|
|
|
+ } else if constexpr (std::is_constructible_v<Out, std::string_view>) {
|
|
|
+ out = Out(str);
|
|
|
return true;
|
|
|
+ } else if constexpr (std::is_integral_v<Out>) {
|
|
|
+ using V = std::conditional_t<std::is_unsigned_v<Out>, unsigned long, long>;
|
|
|
+ auto [tmp, success] = cast<V>(str);
|
|
|
+ out = static_cast<Out>(tmp);
|
|
|
+ return success && tmp == static_cast<V>(out);
|
|
|
+ } else {
|
|
|
+ static_assert(detail::always_false<Out>{}, "No match for cast(string)");
|
|
|
}
|
|
|
-};
|
|
|
-
|
|
|
-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 T>
|
|
|
-struct cast_helper<T, std::enable_if_t<std::is_constructible_v<T, std::string_view>>> {
|
|
|
- bool operator()(std::string_view str, T & to) const noexcept {
|
|
|
- to = str;
|
|
|
- return true;
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-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 {
|
|
|
-template <typename Tuple, size_t... Is>
|
|
|
-bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
|
|
|
- std::index_sequence<Is...>) noexcept {
|
|
|
- return ((cast(str[Is], std::get<Is>(to))) && ...);
|
|
|
+template <typename... Ts>
|
|
|
+bool cast(std::variant<Ts...> & out, std::string_view str) noexcept {
|
|
|
+ return (detail::ctor_cast<Ts>(out, str) || ...);
|
|
|
}
|
|
|
|
|
|
-template <typename T, typename F>
|
|
|
-bool cast_number(std::string_view str, T & to, F func) noexcept {
|
|
|
- char *counter = nullptr;
|
|
|
- to = func(str.data(), &counter);
|
|
|
- return counter == str.end();
|
|
|
+template <typename Out>
|
|
|
+bool cast(std::optional<Out> & out, std::string_view str) noexcept {
|
|
|
+ return detail::ctor_cast<Out>(out, str) || true;
|
|
|
}
|
|
|
|
|
|
-template <typename S> std::vector<S> keyval(S const &input) {
|
|
|
- size_t const pos = input.find('=');
|
|
|
- return pos == S::npos ? std::vector{input}
|
|
|
- : std::vector{input.substr(0, pos), input.substr(pos + 1)};
|
|
|
-}
|
|
|
+template <typename Out>
|
|
|
+bool cast(Out & out, std::vector<std::string_view> const & strs) noexcept {
|
|
|
+ if constexpr (detail::is_associative_v<Out>) {
|
|
|
+ for (auto elem : strs) {
|
|
|
+ auto [tmp, success] =
|
|
|
+ cast<typename Out::value_type>(detail::keyval(elem));
|
|
|
+ if (!success) { return false; }
|
|
|
+ out.insert(std::move(tmp));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } else if constexpr (detail::is_container_v<Out>) {
|
|
|
+ for (auto elem : strs) {
|
|
|
+ auto [tmp, success] = cast<typename Out::value_type>(elem);
|
|
|
+ if (!success) { return false; }
|
|
|
+ out.insert(out.end(), std::move(tmp));
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } else if constexpr (detail::is_tuple_v<Out>) {
|
|
|
+ constexpr size_t N = std::tuple_size_v<Out>;
|
|
|
+ return strs.size() == N &&
|
|
|
+ detail::cast_tuple(strs, out, std::make_index_sequence<N>{});
|
|
|
+ } else {
|
|
|
+ static_assert(detail::always_false<Out>{},
|
|
|
+ "No match for cast(vector<string>)");
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// This should be placed last in the file
|
|
|
-namespace string_utils {
|
|
|
template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
|
|
|
std::pair<detail::decay_t<T>, bool> rval;
|
|
|
- rval.second = cast(str, rval.first);
|
|
|
+ using ::string_utils::cast;
|
|
|
+ rval.second = cast(rval.first, str);
|
|
|
return rval;
|
|
|
}
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
std::pair<T, bool> cast(std::vector<S> const & strs) noexcept {
|
|
|
+ std::vector<std::string_view> tmp{strs.begin(), strs.end()};
|
|
|
std::pair<detail::decay_t<T>, bool> rval;
|
|
|
- rval.second = cast(strs, rval.first);
|
|
|
+ using ::string_utils::cast;
|
|
|
+ rval.second = cast(rval.first, tmp);
|
|
|
return rval;
|
|
|
}
|
|
|
}
|