cast.h 3.3 KB

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