cast.h 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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 <type_traits>
  13. #include <utility>
  14. #if __has_include(<variant>) && __cplusplus > 201402L
  15. #define STRING_UTIL_CAST_STD_VARIANT
  16. #include <variant>
  17. #endif
  18. #include "any_of.h"
  19. #define CAST_NUMBER_IMPL(type, func, ...) \
  20. inline bool cast(std::string const & str, type & to) { \
  21. char * rval; \
  22. to = func(str.c_str(), &rval, ##__VA_ARGS__); \
  23. return rval == str.c_str() + str.length(); \
  24. }
  25. namespace string_utils { namespace traits {
  26. template <typename T, typename = void> struct is_stringy : std::false_type {};
  27. #if defined(STRING_UTIL_CAST_STD_VARIANT)
  28. template <typename> struct is_variant : std::false_type {};
  29. template <typename... Ts>
  30. struct is_variant<std::variant<Ts...>> : std::true_type {};
  31. template <typename T>
  32. struct is_stringy<
  33. T, std::enable_if_t<std::is_constructible<T, std::string const &>{} &&
  34. !is_variant<T>{}>> : std::true_type {};
  35. #else
  36. template <typename T>
  37. struct is_stringy<
  38. T, std::enable_if_t<std::is_constructible<T, std::string const &>{}>>
  39. : std::true_type {};
  40. #endif
  41. }}
  42. namespace string_utils {
  43. template <typename T> std::pair<T, bool> cast(std::string const & str);
  44. template <typename T> bool cast(std::string const & str, std::optional<T> & to);
  45. #if defined(STRING_UTIL_CAST_STD_VARIANT)
  46. template <typename... Ts>
  47. bool cast(std::string const & str, std::variant<Ts...> & to);
  48. #endif
  49. }
  50. namespace string_utils {
  51. template <typename T, typename = std::enable_if_t<traits::is_stringy<T>{}>>
  52. bool cast(std::string const & str, T & to) {
  53. to = T(str);
  54. return true;
  55. }
  56. CAST_NUMBER_IMPL(long, std::strtol, 10);
  57. CAST_NUMBER_IMPL(long long, std::strtoll, 10);
  58. CAST_NUMBER_IMPL(float, std::strtof);
  59. CAST_NUMBER_IMPL(double, std::strtod);
  60. CAST_NUMBER_IMPL(long double, std::strtold);
  61. inline bool cast(std::string const & str, int & to) {
  62. long tmp;
  63. bool rval = cast(str, tmp);
  64. to = static_cast<int>(tmp);
  65. return rval && tmp == static_cast<long>(to);
  66. }
  67. inline bool cast(std::string const & str, bool & to) {
  68. if (any_of(str, "true", "TRUE", "YES", "1")) {
  69. to = true;
  70. return true;
  71. } else if (any_of(str, "false", "FALSE", "NO", "0")) {
  72. to = false;
  73. return true;
  74. }
  75. return false;
  76. }
  77. }
  78. #if defined(STRING_UTIL_CAST_STD_VARIANT)
  79. namespace string_utils::detail {
  80. template <size_t I, typename... Ts>
  81. bool _cast(std::string const & str, std::variant<Ts...> & to) {
  82. auto [rval, found] = cast<std::tuple_element_t<I, std::tuple<Ts...>>>(str);
  83. if (found) { to = std::move(rval); }
  84. return found;
  85. }
  86. // This is needed due to a compiler bug in Clang that fails to properly compile
  87. // fold-expressions.
  88. template <size_t I, typename... Ts>
  89. bool _cast_chain(std::string const & str, std::variant<Ts...> & to) {
  90. if constexpr (I == sizeof...(Ts)) {
  91. return false;
  92. } else {
  93. return _cast<I>(str, to) || _cast_chain<I + 1>(str, to);
  94. }
  95. }
  96. }
  97. namespace string_utils {
  98. template <typename... Ts>
  99. bool cast(std::string const & str, std::variant<Ts...> & to) {
  100. return detail::_cast_chain<0>(str, to);
  101. }
  102. }
  103. #endif
  104. namespace string_utils {
  105. template <typename T>
  106. bool cast(std::string const & str, std::optional<T> & to) {
  107. auto [value, success] = cast<T>(str);
  108. if (success) { to = std::move(value); }
  109. return true;
  110. }
  111. }
  112. // This should be placed last in the file
  113. namespace string_utils {
  114. template <typename T> std::pair<T, bool> cast(std::string const & str) {
  115. using string_utils::cast;
  116. std::pair<T, bool> rval;
  117. rval.second = cast(str, rval.first);
  118. return rval;
  119. }
  120. }
  121. #undef CAST_NUMBER_IMPL