cast.h 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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 "string_utils/any_of.h"
  18. #include "string_utils/forwards.h"
  19. #include "string_utils/traits.h"
  20. #define SAFE_NUMBER_PARSE(func, ...) \
  21. [](char const *in, char **out) { return func(in, out, ##__VA_ARGS__); }
  22. namespace string_utils::detail {
  23. template <typename T, typename F>
  24. bool cast_number(std::string_view str, T & to, F func) noexcept;
  25. template <typename S> std::vector<S> keyval(S const &input);
  26. }
  27. namespace string_utils {
  28. inline bool cast(std::string_view str, std::string & to) noexcept {
  29. to = std::string(str);
  30. return true;
  31. }
  32. inline bool cast(std::string_view str, long & to) noexcept {
  33. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtol, 10));
  34. }
  35. inline bool cast(std::string_view str, long long & to) noexcept {
  36. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtoll, 10));
  37. }
  38. inline bool cast(std::string_view str, float & to) noexcept {
  39. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtof));
  40. }
  41. inline bool cast(std::string_view str, double & to) noexcept {
  42. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtod));
  43. }
  44. inline bool cast(std::string_view str, long double & to) noexcept {
  45. return detail::cast_number(str, to, SAFE_NUMBER_PARSE(std::strtold));
  46. }
  47. inline bool cast(std::string_view str, int & to) noexcept {
  48. auto [tmp, success] = cast<long>(str);
  49. to = static_cast<int>(tmp);
  50. return success && tmp == static_cast<long>(to);
  51. }
  52. inline bool cast(std::string_view str, bool & to) noexcept {
  53. if (any_of(str, "true", "TRUE", "YES", "1")) {
  54. to = true;
  55. return true;
  56. } else if (any_of(str, "false", "FALSE", "NO", "0")) {
  57. to = false;
  58. return true;
  59. }
  60. return false;
  61. }
  62. }
  63. namespace string_utils {
  64. template <typename V, typename S, typename T> bool maybe_cast(S const & str, T & to) noexcept {
  65. auto [rval, found] = cast<V>(str);
  66. if (found) { to = std::move(rval); }
  67. return found;
  68. }
  69. template <typename T> struct cast_helper<T, std::enable_if_t<detail::is_container<T>{}>> {
  70. template <typename S>
  71. bool operator()(std::vector<S> const &strs, T & to) const noexcept {
  72. for (S const &elem : strs) {
  73. if constexpr (detail::is_associative<T>{}) {
  74. auto [tmp, success] = cast<typename T::value_type>(detail::keyval(elem));
  75. if (!success) { return false; }
  76. to.insert(std::move(tmp));
  77. } else {
  78. auto [tmp, success] = cast<typename T::value_type>(elem);
  79. if (!success) { return false; }
  80. to.insert(to.end(), std::move(tmp));
  81. }
  82. }
  83. return true;
  84. }
  85. };
  86. template <typename T> struct cast_helper<T, std::enable_if_t<detail::is_tuple<T>{}>> {
  87. template <typename S, size_t... Is>
  88. bool cast_tuple(S const & str, T & to, std::index_sequence<Is...>) const noexcept {
  89. return ((cast(str[Is], std::get<Is>(to))) && ...);
  90. }
  91. template <typename S> bool operator()(std::vector<S> const &strs, T & to) const noexcept {
  92. constexpr size_t N = std::tuple_size_v<T>;
  93. return strs.size() == N && cast_tuple(strs, to, std::make_index_sequence<N>{});
  94. }
  95. };
  96. template <typename... Ts>
  97. struct cast_helper<std::variant<Ts...>> {
  98. bool operator()(std::string_view 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_view str, std::optional<T> & to) const noexcept {
  105. return maybe_cast<T>(str, to) || true;
  106. }
  107. };
  108. }
  109. namespace string_utils::detail {
  110. template <typename Tuple, size_t... Is>
  111. bool cast_tuple(std::vector<std::string> const & str, Tuple & to,
  112. std::index_sequence<Is...>) noexcept {
  113. return ((cast(str[Is], std::get<Is>(to))) && ...);
  114. }
  115. template <typename T, typename F>
  116. bool cast_number(std::string_view str, T & to, F func) noexcept {
  117. char *counter = nullptr;
  118. to = func(str.data(), &counter);
  119. return counter == str.end();
  120. }
  121. template <typename S> std::vector<S> keyval(S const &input) {
  122. size_t const pos = input.find('=');
  123. return pos == S::npos ? std::vector{input}
  124. : std::vector{input.substr(0, pos), input.substr(pos + 1)};
  125. }
  126. }
  127. // This should be placed last in the file
  128. namespace string_utils {
  129. template <typename T> std::pair<T, bool> cast(std::string_view str) noexcept {
  130. std::pair<detail::decay_t<T>, bool> rval;
  131. rval.second = cast(str, rval.first);
  132. return rval;
  133. }
  134. template <typename T, typename S>
  135. std::pair<T, bool> cast(std::vector<S> const & strs) noexcept {
  136. std::pair<detail::decay_t<T>, bool> rval;
  137. rval.second = cast(strs, rval.first);
  138. return rval;
  139. }
  140. }
  141. #undef CAST_NUMBER_IMPL