cast.h 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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> std::pair<T, bool> cast(std::string_view str) noexcept;
  25. template <typename T> bool cast(std::string_view str, T & to) noexcept;
  26. // A section of multi-argument parsers
  27. template <typename... Ts>
  28. bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept;
  29. template <typename K, typename V>
  30. bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept;
  31. }
  32. namespace string_utils::detail {
  33. template <typename, typename = void> struct has_cast_helper : std::false_type {};
  34. template <typename T>
  35. struct has_cast_helper<T, std::void_t<std::result_of_t<cast_helper<T>(std::string, T)>>>
  36. : std::true_type {};
  37. template <typename T> constexpr bool has_cast_helper_v = has_cast_helper<T>{};
  38. }
  39. namespace string_utils::detail {
  40. template <typename Tuple>
  41. bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept;
  42. template <typename T, typename F>
  43. bool cast_number(std::string_view str, T & to, F func) noexcept;
  44. }
  45. namespace string_utils {
  46. template <typename T>
  47. bool cast(std::string_view str, T & to) noexcept {
  48. if constexpr (detail::has_cast_helper_v<T>) {
  49. return cast_helper<T>{}(str, to);
  50. } else if constexpr (std::is_same_v<T, std::string>) {
  51. to = T(str);
  52. } else {
  53. static_assert(!detail::has_cast_helper_v<T>, "no cast available");
  54. }
  55. return true;
  56. }
  57. template <typename V, typename T> bool maybe_cast(std::string const & str, T & to) noexcept {
  58. auto [rval, found] = cast<V>(str);
  59. if (found) { to = std::move(rval); }
  60. return found;
  61. }
  62. template <typename... Ts>
  63. struct cast_helper<std::variant<Ts...>> {
  64. bool operator()(std::string const &str, std::variant<Ts...> & to) const noexcept {
  65. return (maybe_cast<Ts>(str, to) || ...);
  66. }
  67. };
  68. template <typename T>
  69. struct cast_helper<std::optional<T>> {
  70. bool operator()(std::string const &str, std::optional<T> & to) const noexcept {
  71. return maybe_cast<T>(str, to) || true;
  72. }
  73. };
  74. }
  75. namespace string_utils {
  76. inline bool cast(std::string_view str, long & to) noexcept {
  77. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
  78. }
  79. inline bool cast(std::string_view str, long long & to) noexcept {
  80. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
  81. }
  82. inline bool cast(std::string_view str, float & to) noexcept {
  83. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
  84. }
  85. inline bool cast(std::string_view str, double & to) noexcept {
  86. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
  87. }
  88. inline bool cast(std::string_view str, long double & to) noexcept {
  89. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
  90. }
  91. inline bool cast(std::string_view str, int & to) noexcept {
  92. long tmp;
  93. bool rval = cast(str, tmp);
  94. to = static_cast<int>(tmp);
  95. return rval && tmp == static_cast<long>(to);
  96. }
  97. inline bool cast(std::string_view str, bool & to) noexcept {
  98. if (any_of(str, "true", "TRUE", "YES", "1")) {
  99. to = true;
  100. return true;
  101. } else if (any_of(str, "false", "FALSE", "NO", "0")) {
  102. to = false;
  103. return true;
  104. }
  105. return false;
  106. }
  107. }
  108. namespace string_utils {
  109. template <typename... Ts>
  110. bool cast(std::vector<std::string> const & str, std::tuple<Ts...> & to) noexcept {
  111. return detail::cast_tuple(str, to);
  112. }
  113. template <typename K, typename V>
  114. bool cast(std::vector<std::string> const & str, std::pair<K, V> & to) noexcept {
  115. return detail::cast_tuple(str, to);
  116. }
  117. }
  118. namespace string_utils::detail {
  119. template <typename Tuple, size_t... Is>
  120. bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
  121. std::index_sequence<Is...>) {
  122. return ((cast(str[Is], std::get<Is>(to))) && ...);
  123. }
  124. template <typename Tuple>
  125. bool cast_tuple(std::vector<std::string> const & str, Tuple & to) noexcept {
  126. constexpr size_t N = std::tuple_size_v<Tuple>;
  127. return str.size() == N && cast_tuple(str, to, std::make_index_sequence<N>{});
  128. }
  129. template <typename T, typename F>
  130. bool cast_number(std::string_view str, T & to, F func) noexcept {
  131. char *counter = nullptr;
  132. to = func(str.data(), &counter);
  133. return counter == str.end();
  134. }
  135. }
  136. // This should be placed last in the file
  137. namespace string_utils {
  138. template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
  139. using string_utils::cast;
  140. std::pair<T, bool> rval;
  141. rval.second = cast(str, rval.first);
  142. return rval;
  143. }
  144. }
  145. #undef CAST_NUMBER_IMPL