cast.h 5.5 KB

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