typecast.h 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. //
  2. // typecast.h
  3. // reflection
  4. //
  5. // Created by Sam Jaffe on 7/4/22.
  6. // Copyright © 2022 Sam Jaffe. All rights reserved.
  7. //
  8. #pragma once
  9. #include <functional>
  10. #include <typeindex>
  11. #include <unordered_map>
  12. #include <utility>
  13. #if defined __has_include
  14. #if __has_include(<magic_enum.hpp>)
  15. #include <magic_enum.hpp>
  16. #endif
  17. #endif
  18. #include "reflection/forward.h"
  19. #include "reflection/object.h"
  20. namespace reflection {
  21. template <typename T> class TypeCast {
  22. public:
  23. static bool has_cast(Object const & obj) { return get_.count(obj.type()); }
  24. static T get(Object const & obj) { return get_.at(obj.type())(obj); }
  25. static void set(Object & obj, T const & value) {
  26. set_.at(obj.type())(obj, value);
  27. }
  28. operator bool() const { return true; }
  29. template <typename I, typename O, typename S>
  30. TypeCast & bind(T (*to)(I), O (*from)(S)) {
  31. bind_impl(to, from);
  32. TypeCast<std::decay_t<I>>().template bind_impl(from, to);
  33. return *this;
  34. }
  35. template <typename V> TypeCast & bind() {
  36. #if defined NEARGYE_MAGIC_ENUM_HPP
  37. if constexpr (std::is_same_v<V, std::string> && std::is_enum_v<T>) {
  38. bind<V const &, V, T>([](V const &str) -> T { return *magic_enum::enum_cast<T>(str); },
  39. [](T val) { return std::string(magic_enum::enum_name(val)); });
  40. } else
  41. #endif
  42. if constexpr (!std::is_same_v<V, T>) {
  43. bind_impl<V>();
  44. TypeCast<V>().template bind_impl<T>();
  45. }
  46. return *this;
  47. }
  48. template <typename V1, typename V2, typename... Vs> TypeCast & bind() {
  49. return bind<V1>().template bind<V2, Vs...>();
  50. }
  51. private:
  52. template <typename I, typename O, typename S>
  53. void bind_impl(T (*to)(I), O (*from)(S)) {
  54. static_assert(std::is_same_v<std::decay_t<S>, T>,
  55. "this types must be compatible");
  56. static_assert(std::is_same_v<std::decay_t<I>, std::decay_t<I>>,
  57. "cast types must be compatible");
  58. using V = std::decay_t<I>;
  59. get_.emplace(typeid(V),
  60. [to](auto & obj) { return to(static_cast<V const &>(obj)); });
  61. set_.emplace(typeid(V), [from](auto & obj, T const & val) {
  62. static_cast<V &>(obj) = from(val);
  63. });
  64. }
  65. template <typename V> void bind_impl() {
  66. get_.emplace(typeid(V), [](auto & obj) {
  67. return static_cast<T>(static_cast<V const &>(obj));
  68. });
  69. set_.emplace(typeid(V), [](auto & obj, T val) {
  70. static_cast<V &>(obj) = static_cast<V>(val);
  71. });
  72. }
  73. private:
  74. template <typename S> friend class TypeCast;
  75. static TypeMap<std::function<T(Object const &)>> get_;
  76. static TypeMap<std::function<void(Object &, T const &)>> set_;
  77. };
  78. template <typename T>
  79. TypeMap<std::function<T(Object const &)>> TypeCast<T>::get_;
  80. template <typename T>
  81. TypeMap<std::function<void(Object &, T const &)>> TypeCast<T>::set_;
  82. }
  83. #if defined REFLECTION_TYPE_CAST_IMPLEMENTATION
  84. namespace reflection {
  85. INSTANTIATE_REFLECTION_TYPECAST(bool);
  86. INSTANTIATE_REFLECTION_TYPECAST(int16_t);
  87. INSTANTIATE_REFLECTION_TYPECAST(int32_t);
  88. INSTANTIATE_REFLECTION_TYPECAST(int64_t);
  89. INSTANTIATE_REFLECTION_TYPECAST(uint16_t);
  90. INSTANTIATE_REFLECTION_TYPECAST(uint32_t);
  91. INSTANTIATE_REFLECTION_TYPECAST(uint64_t);
  92. INSTANTIATE_REFLECTION_TYPECAST(float);
  93. INSTANTIATE_REFLECTION_TYPECAST(double);
  94. INSTANTIATE_REFLECTION_TYPECAST(long double);
  95. INSTANTIATE_REFLECTION_TYPECAST(std::string, "string");
  96. // Bind boolean types to all integer types
  97. REFLECTION_TYPE_CAST(bool)
  98. .bind<int16_t, int32_t, int64_t>()
  99. .bind<uint16_t, uint32_t, uint64_t>();
  100. // Bind all integer types (wider than 1byte) to each other.
  101. REFLECTION_TYPE_CAST(int32_t)
  102. .bind<int16_t, int32_t, int64_t>()
  103. .bind<uint16_t, uint32_t, uint64_t>()
  104. .bind<float, double, long double>();
  105. REFLECTION_TYPE_CAST(uint32_t)
  106. .bind<int16_t, int32_t, int64_t>()
  107. .bind<uint16_t, uint32_t, uint64_t>()
  108. .bind<float, double, long double>();
  109. REFLECTION_TYPE_CAST(int16_t)
  110. .bind<int16_t, int32_t, int64_t>()
  111. .bind<uint16_t, uint32_t, uint64_t>()
  112. .bind<float, double, long double>();
  113. REFLECTION_TYPE_CAST(uint16_t)
  114. .bind<int16_t, int32_t, int64_t>()
  115. .bind<uint16_t, uint32_t, uint64_t>()
  116. .bind<float, double, long double>();
  117. REFLECTION_TYPE_CAST(int64_t)
  118. .bind<int16_t, int32_t, int64_t>()
  119. .bind<uint16_t, uint32_t, uint64_t>()
  120. .bind<float, double, long double>();
  121. REFLECTION_TYPE_CAST(uint64_t)
  122. .bind<int16_t, int32_t, int64_t>()
  123. .bind<uint16_t, uint32_t, uint64_t>()
  124. .bind<float, double, long double>();
  125. // Mututally bind all floating point numbers
  126. REFLECTION_TYPE_CAST(float).bind<float, double, long double>();
  127. REFLECTION_TYPE_CAST(double).bind<float, double, long double>();
  128. REFLECTION_TYPE_CAST(long double).bind<float, double, long double>();
  129. }
  130. #endif