cast.h 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. //
  2. // cast.h
  3. // string-utils
  4. //
  5. // Created by Sam Jaffe on 2/13/21.
  6. // Copyright © 2021 Sam Jaffe. All rights reserved.
  7. //
  8. #pragma once
  9. #include <cstdlib>
  10. #include <optional>
  11. #include <string>
  12. #include <string_view>
  13. #include <type_traits>
  14. #include <utility>
  15. #include <variant>
  16. #include <vector>
  17. #include "any_of.h"
  18. #define SAFE_NUMBER_PARSE(func, ...) \
  19. [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
  20. namespace string_utils {
  21. // A helper object for providing partial specializations for casting
  22. template <typename, typename = void> struct cast_helper {};
  23. // The main parser
  24. template <typename T, typename S> std::pair<T, bool> cast(S const &str) noexcept;
  25. template <typename T> bool cast(std::string_view str, T & to) noexcept;
  26. template <typename S, typename T> bool cast(std::vector<S> const &strs, T & to) noexcept;
  27. }
  28. namespace string_utils::detail {
  29. template <typename, typename, typename = void> struct has_cast_helper : std::false_type {};
  30. template <typename T, typename S>
  31. struct has_cast_helper<T, S, std::void_t<std::result_of_t<cast_helper<T>(S, T)>>>
  32. : std::true_type {};
  33. template <typename, typename = void> struct is_tuple : std::false_type {};
  34. template <typename T>
  35. struct is_tuple<T, std::void_t<typename std::tuple_size<T>::type>> : std::true_type {};
  36. template <typename, typename = void> struct is_associative : std::false_type {};
  37. template <typename T>
  38. struct is_associative<T, std::void_t<typename T::mapped_type>> : std::true_type {};
  39. template <typename, typename = void> struct is_container : std::false_type {};
  40. template <typename T>
  41. struct is_container<T, std::void_t<typename T::value_type>> : std::true_type {};
  42. template <typename T> struct decay { using type = std::decay_t<T>; };
  43. template <template <typename...> class C, typename... Ts>
  44. struct decay<C<Ts...>> { using type = C<std::decay_t<Ts>...>; };
  45. template <typename T> using decay_t = typename decay<std::decay_t<T>>::type;
  46. }
  47. namespace string_utils::detail {
  48. template <typename Tuple, size_t... Is>
  49. bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
  50. std::index_sequence<Is...>) noexcept;
  51. template <typename T, typename F>
  52. bool cast_number(std::string_view str, T & to, F func) noexcept;
  53. template <typename S> std::vector<S> keyval(S const &input);
  54. }
  55. namespace string_utils {
  56. template <typename T>
  57. bool cast(std::string_view str, T & to) noexcept {
  58. if constexpr (detail::has_cast_helper<T, std::string>{}) {
  59. return cast_helper<T>{}(str, to);
  60. } else if constexpr (std::is_same_v<T, std::string>) {
  61. to = T(str);
  62. } else {
  63. static_assert(!detail::has_cast_helper<T, std::string>{}, "no cast available");
  64. }
  65. return true;
  66. }
  67. template <typename S, typename T>
  68. bool cast(std::vector<S> const &strs, T & to) noexcept {
  69. if constexpr(detail::has_cast_helper<T, std::vector<S>>{}) {
  70. return cast_helper<T>{}(strs, to);
  71. } else if constexpr (detail::is_tuple<T>{}) {
  72. constexpr size_t N = std::tuple_size_v<T>;
  73. return strs.size() == N && detail::cast_tuple(strs, to, std::make_index_sequence<N>{});
  74. } else if constexpr (detail::is_associative<T>{}) {
  75. for (S const &elem : strs) {
  76. auto [tmp, success] = cast<typename T::value_type>(detail::keyval(elem));
  77. if (!success) { return false; }
  78. to.insert(std::move(tmp));
  79. }
  80. } else if constexpr (detail::is_container<T>{}) {
  81. for (S const &elem : strs) {
  82. auto [tmp, success] = cast<typename T::value_type>(elem);
  83. if (!success) { return false; }
  84. to.insert(to.end(), std::move(tmp));
  85. }
  86. } else {
  87. static_assert(!detail::has_cast_helper<T, std::vector<S>>{}, "no cast available");
  88. }
  89. return true;
  90. }
  91. template <typename V, typename T> bool maybe_cast(std::string const & str, T & to) noexcept {
  92. auto [rval, found] = cast<V>(str);
  93. if (found) { to = std::move(rval); }
  94. return found;
  95. }
  96. template <typename... Ts>
  97. struct cast_helper<std::variant<Ts...>> {
  98. bool operator()(std::string const &str, std::variant<Ts...> & to) const noexcept {
  99. return (maybe_cast<Ts>(str, to) || ...);
  100. }
  101. };
  102. template <typename T>
  103. struct cast_helper<std::optional<T>> {
  104. bool operator()(std::string const &str, std::optional<T> & to) const noexcept {
  105. return maybe_cast<T>(str, to) || true;
  106. }
  107. };
  108. }
  109. namespace string_utils {
  110. inline bool cast(std::string_view str, long & to) noexcept {
  111. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
  112. }
  113. inline bool cast(std::string_view str, long long & to) noexcept {
  114. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
  115. }
  116. inline bool cast(std::string_view str, float & to) noexcept {
  117. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
  118. }
  119. inline bool cast(std::string_view str, double & to) noexcept {
  120. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
  121. }
  122. inline bool cast(std::string_view str, long double & to) noexcept {
  123. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
  124. }
  125. inline bool cast(std::string_view str, int & to) noexcept {
  126. long tmp;
  127. bool rval = cast(str, tmp);
  128. to = static_cast<int>(tmp);
  129. return rval && tmp == static_cast<long>(to);
  130. }
  131. inline bool cast(std::string_view str, bool & to) noexcept {
  132. if (any_of(str, "true", "TRUE", "YES", "1")) {
  133. to = true;
  134. return true;
  135. } else if (any_of(str, "false", "FALSE", "NO", "0")) {
  136. to = false;
  137. return true;
  138. }
  139. return false;
  140. }
  141. }
  142. namespace string_utils::detail {
  143. template <typename Tuple, size_t... Is>
  144. bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
  145. std::index_sequence<Is...>) noexcept {
  146. return ((cast(str[Is], std::get<Is>(to))) && ...);
  147. }
  148. template <typename T, typename F>
  149. bool cast_number(std::string_view str, T & to, F func) noexcept {
  150. char *counter = nullptr;
  151. to = func(str.data(), &counter);
  152. return counter == str.end();
  153. }
  154. template <typename S> std::vector<S> keyval(S const &input) {
  155. size_t const pos = input.find('=');
  156. return pos == S::npos ? std::vector{input}
  157. : std::vector{input.substr(0, pos), input.substr(pos + 1)};
  158. }
  159. }
  160. // This should be placed last in the file
  161. namespace string_utils {
  162. template <typename T, typename S> std::pair<T, bool> cast(S const & str) noexcept {
  163. using string_utils::cast;
  164. std::pair<detail::decay_t<T>, bool> rval;
  165. rval.second = cast(str, rval.first);
  166. return rval;
  167. }
  168. }
  169. #undef CAST_NUMBER_IMPL