|
@@ -18,60 +18,106 @@
|
|
|
|
|
|
|
|
#include "any_of.h"
|
|
#include "any_of.h"
|
|
|
|
|
|
|
|
-#define CAST_NUMBER_IMPL(type, func, ...) \
|
|
|
|
|
- inline bool cast(std::string const & str, type & to) { \
|
|
|
|
|
- char * rval; \
|
|
|
|
|
- to = func(str.c_str(), &rval, ##__VA_ARGS__); \
|
|
|
|
|
- return rval == str.c_str() + str.length(); \
|
|
|
|
|
- }
|
|
|
|
|
|
|
+#define SAFE_NUMBER_PARSE(func, ...) \
|
|
|
|
|
+ [](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 {};
|
|
|
|
|
|
|
|
-namespace string_utils { namespace traits {
|
|
|
|
|
-template <typename T, typename = void> struct is_stringy : std::false_type {};
|
|
|
|
|
|
|
+// The main parser
|
|
|
|
|
+template <typename T> std::pair<T, bool> cast(std::string const & str) noexcept;
|
|
|
|
|
+template <typename T> bool cast(std::string const & str, T & to) noexcept;
|
|
|
|
|
|
|
|
-template <typename> struct is_variant : std::false_type {};
|
|
|
|
|
|
|
+// A section of multi-argument parsers
|
|
|
template <typename... Ts>
|
|
template <typename... Ts>
|
|
|
-struct is_variant<std::variant<Ts...>> : std::true_type {};
|
|
|
|
|
|
|
+bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept;
|
|
|
|
|
+template <typename K, typename V>
|
|
|
|
|
+bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+namespace string_utils::detail {
|
|
|
|
|
+template <typename, typename = void> struct has_cast_helper : std::false_type {};
|
|
|
template <typename T>
|
|
template <typename T>
|
|
|
-struct is_stringy<
|
|
|
|
|
- T, std::enable_if_t<std::is_constructible<T, std::string const &>{} &&
|
|
|
|
|
- !is_variant<T>{}>> : std::true_type {};
|
|
|
|
|
|
|
+struct has_cast_helper<T, std::void_t<std::result_of_t<cast_helper<T>(std::string, T)>>>
|
|
|
|
|
+ : std::true_type {};
|
|
|
|
|
|
|
|
-}}
|
|
|
|
|
|
|
+template <typename T> constexpr bool has_cast_helper_v = has_cast_helper<T>{};
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+namespace string_utils::detail {
|
|
|
|
|
+template <typename Tuple>
|
|
|
|
|
+bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept;
|
|
|
|
|
+template <typename T, typename F>
|
|
|
|
|
+bool cast_number(std::string const &str, T & to, F func) noexcept;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
namespace string_utils {
|
|
namespace string_utils {
|
|
|
-template <typename T> std::pair<T, bool> cast(std::string const & str);
|
|
|
|
|
-template <typename T> bool cast(std::string const & str, std::optional<T> & to);
|
|
|
|
|
-template <typename... Ts>
|
|
|
|
|
-bool cast(std::string const & str, std::variant<Ts...> & to);
|
|
|
|
|
|
|
+
|
|
|
|
|
+template <typename T>
|
|
|
|
|
+bool cast(std::string const & str, T & to) noexcept {
|
|
|
|
|
+ if constexpr (detail::has_cast_helper_v<T>) {
|
|
|
|
|
+ return cast_helper<T>{}(str, to);
|
|
|
|
|
+ } else if constexpr (std::is_same_v<T, std::string>) {
|
|
|
|
|
+ to = T(str);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ static_assert(!detail::has_cast_helper_v<T>, "no cast available");
|
|
|
|
|
+ }
|
|
|
|
|
+ return true;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+template <typename V, typename T> bool maybe_cast(std::string const & str, T & to) noexcept {
|
|
|
|
|
+ auto [rval, found] = cast<V>(str);
|
|
|
|
|
+ if (found) { to = std::move(rval); }
|
|
|
|
|
+ return found;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
template <typename... Ts>
|
|
template <typename... Ts>
|
|
|
-bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to);
|
|
|
|
|
-template <typename K, typename V>
|
|
|
|
|
-bool cast(std::vector<std::string> const & str, std::pair<K, V> & to);
|
|
|
|
|
|
|
+struct cast_helper<std::variant<Ts...>> {
|
|
|
|
|
+ bool operator()(std::string const &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 const &str, std::optional<T> & to) const noexcept {
|
|
|
|
|
+ return maybe_cast<T>(str, to) || true;
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
namespace string_utils {
|
|
namespace string_utils {
|
|
|
-template <typename T, typename = std::enable_if_t<traits::is_stringy<T>{}>>
|
|
|
|
|
-bool cast(std::string const & str, T & to) {
|
|
|
|
|
- to = T(str);
|
|
|
|
|
- return true;
|
|
|
|
|
|
|
+
|
|
|
|
|
+inline bool cast(std::string const &str, long & to) noexcept {
|
|
|
|
|
+ return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+inline bool cast(std::string const &str, long long & to) noexcept {
|
|
|
|
|
+ return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+inline bool cast(std::string const &str, float & to) noexcept {
|
|
|
|
|
+ return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+inline bool cast(std::string const &str, double & to) noexcept {
|
|
|
|
|
+ return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-CAST_NUMBER_IMPL(long, std::strtol, 10);
|
|
|
|
|
-CAST_NUMBER_IMPL(long long, std::strtoll, 10);
|
|
|
|
|
-CAST_NUMBER_IMPL(float, std::strtof);
|
|
|
|
|
-CAST_NUMBER_IMPL(double, std::strtod);
|
|
|
|
|
-CAST_NUMBER_IMPL(long double, std::strtold);
|
|
|
|
|
|
|
+inline bool cast(std::string const &str, long double & to) noexcept {
|
|
|
|
|
+ return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-inline bool cast(std::string const & str, int & to) {
|
|
|
|
|
|
|
+inline bool cast(std::string const & str, int & to) noexcept {
|
|
|
long tmp;
|
|
long tmp;
|
|
|
bool rval = cast(str, tmp);
|
|
bool rval = cast(str, tmp);
|
|
|
to = static_cast<int>(tmp);
|
|
to = static_cast<int>(tmp);
|
|
|
return rval && tmp == static_cast<long>(to);
|
|
return rval && tmp == static_cast<long>(to);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-inline bool cast(std::string const & str, bool & to) {
|
|
|
|
|
|
|
+inline bool cast(std::string const & str, bool & to) noexcept {
|
|
|
if (any_of(str, "true", "TRUE", "YES", "1")) {
|
|
if (any_of(str, "true", "TRUE", "YES", "1")) {
|
|
|
to = true;
|
|
to = true;
|
|
|
return true;
|
|
return true;
|
|
@@ -81,16 +127,24 @@ inline bool cast(std::string const & str, bool & to) {
|
|
|
}
|
|
}
|
|
|
return false;
|
|
return false;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+namespace string_utils {
|
|
|
|
|
+
|
|
|
|
|
+template <typename... Ts>
|
|
|
|
|
+bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept {
|
|
|
|
|
+ return detail::cast_tuple(str, to);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+template <typename K, typename V>
|
|
|
|
|
+bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept {
|
|
|
|
|
+ return detail::cast_tuple(str, to);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-namespace string_utils::detail {
|
|
|
|
|
-template <typename T, typename... Ts>
|
|
|
|
|
-bool cast_alternative(std::string const & str, std::variant<Ts...> & to) {
|
|
|
|
|
- auto [rval, found] = cast<T>(str);
|
|
|
|
|
- if (found) { to = std::move(rval); }
|
|
|
|
|
- return found;
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+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,
|
|
|
std::index_sequence<Is...>) {
|
|
std::index_sequence<Is...>) {
|
|
@@ -98,41 +152,22 @@ bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
template <typename Tuple>
|
|
template <typename Tuple>
|
|
|
-bool cast_tuple(std::vector<std::string> const & str, Tuple & to) {
|
|
|
|
|
|
|
+bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept {
|
|
|
constexpr size_t N = std::tuple_size_v<Tuple>;
|
|
constexpr size_t N = std::tuple_size_v<Tuple>;
|
|
|
return str.size() == N && cast_tuple(str, to, std::make_index_sequence<N>{});
|
|
return str.size() == N && cast_tuple(str, to, std::make_index_sequence<N>{});
|
|
|
}
|
|
}
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-namespace string_utils {
|
|
|
|
|
|
|
|
|
|
-template <typename... Ts>
|
|
|
|
|
-bool cast(std::string const & str, std::variant<Ts...> & to) {
|
|
|
|
|
- return (detail::cast_alternative<Ts>(str, to) || ...);
|
|
|
|
|
|
|
+template <typename T, typename F>
|
|
|
|
|
+bool cast_number(std::string const &str, T & to, F func) noexcept {
|
|
|
|
|
+ char *counter = nullptr;
|
|
|
|
|
+ to = func(str.c_str(), &counter);
|
|
|
|
|
+ return counter == str.c_str() + str.length();
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-template <typename T>
|
|
|
|
|
-bool cast(std::string const & str, std::optional<T> & to) {
|
|
|
|
|
- auto [value, success] = cast<T>(str);
|
|
|
|
|
- if (success) { to = std::move(value); }
|
|
|
|
|
- return true;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-template <typename... Ts>
|
|
|
|
|
-bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) {
|
|
|
|
|
- return detail::cast_tuple(str, to);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-template <typename K, typename V>
|
|
|
|
|
-bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) {
|
|
|
|
|
- return detail::cast_tuple(str, to);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 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> std::pair<T, bool> cast(std::string const & str) {
|
|
|
|
|
|
|
+template <typename T> std::pair<T, bool> cast(std::string const & str) noexcept {
|
|
|
using string_utils::cast;
|
|
using string_utils::cast;
|
|
|
std::pair<T, bool> rval;
|
|
std::pair<T, bool> rval;
|
|
|
rval.second = cast(str, rval.first);
|
|
rval.second = cast(str, rval.first);
|