object.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. //
  2. // object.h
  3. // reflection
  4. //
  5. // Created by Sam Jaffe on 7/3/22.
  6. // Copyright © 2022 Sam Jaffe. All rights reserved.
  7. //
  8. #pragma once
  9. #include <stdexcept>
  10. #include <typeindex>
  11. #include <utility>
  12. #include "reflection/forward.h"
  13. #include "reflection/property.h"
  14. namespace reflection {
  15. class Object {
  16. public:
  17. template <typename T> Object(T const & data, std::string name = "this");
  18. template <typename T> Object(T & data, std::string name = "this");
  19. template <typename T> Object(T && data, std::string name = "this");
  20. template <typename T> Object(Proxy<T> data, std::string name = "this");
  21. template <typename V> Object & operator=(V const & value);
  22. Object own() const { return clone_(*this); }
  23. std::string_view name() const {
  24. return std::string_view(name_).substr(name_.rfind('.') + 1);
  25. }
  26. std::string_view path() const { return name_; }
  27. std::type_index type() const { return type_; }
  28. std::string_view type_name() const {
  29. return type_name_.empty() ? type_.name() : type_name_;
  30. }
  31. template <typename T> bool is_a() const { return type_ == typeid(T); }
  32. template <typename T> explicit operator T &() const & { return cast<T &>(); }
  33. template <typename T> explicit operator T const &() const & {
  34. return cast<T const &>();
  35. }
  36. template <typename T> operator T() const & { return cast<T>(); }
  37. template <typename T> operator T() & { return cast<T>(); }
  38. template <typename T> operator T() && {
  39. return const_ ? cast<T>() : std::move(cast<T &>());
  40. }
  41. Object get(std::string_view id) & { return (this->*get_)(id); }
  42. Object get(std::string_view id) const & { return (this->*get_)(id); }
  43. Object get(std::string_view id) && { return (this->*get_)(id).own(); }
  44. Object get(Property const & scope) const {
  45. if (type_name() != scope.type_name() && !scope.type_name().empty()) {
  46. throw std::bad_cast();
  47. }
  48. return get(*this, scope.begin(), scope.end());
  49. }
  50. template <typename C, typename = std::enable_if_t<IS_SCOPE_CONTAINER(C)>>
  51. Object get(C const & container) const {
  52. return get(*this, std::begin(container), std::end(container));
  53. }
  54. template <typename It> static Object get(Object o, It it, It const end) {
  55. for (; it != end; ++it) {
  56. o = std::move(o).get(*it);
  57. }
  58. return o;
  59. }
  60. private:
  61. template <typename T> T cast() const;
  62. template <typename T> Object getter(std::string_view id) const;
  63. template <typename T> Object accessor(std::string_view id) const;
  64. template <typename T> static Object deep(Object const & obj) {
  65. return Object(obj.cast<T>(), obj.name_);
  66. }
  67. private:
  68. std::type_index type_;
  69. std::string_view type_name_; // A more human-readable version of type_
  70. std::string name_; // The property name as a dot-separated path
  71. // Are we using a proxy class to store non-trivial setter behavior
  72. bool proxy_{false};
  73. bool const_; // Is the object immutable (const-ref or rvalue)
  74. std::shared_ptr<void> data_;
  75. Object (Object::*get_)(std::string_view) const;
  76. Object (*clone_)(Object const &) = [](Object const & obj) { return obj; };
  77. };
  78. }
  79. #include "reflection/reflection.h"
  80. #include "reflection/typecast.h"
  81. namespace reflection {
  82. template <typename T> Object Object::getter(std::string_view id) const {
  83. return Reflection<T>::getter(id)(cast<T const &>(),
  84. name_ + "." + std::string(id));
  85. }
  86. template <typename T> Object Object::accessor(std::string_view id) const {
  87. return Reflection<T>::accessor(id)(cast<T &>(),
  88. name_ + "." + std::string(id));
  89. }
  90. template <typename T>
  91. Object::Object(T const & data, std::string name)
  92. : type_(typeid(T)), type_name_(Name<T>), name_(std::move(name)), const_(true),
  93. data_(const_cast<T *>(&data), [](auto *) {}), get_(&Object::getter<T>) {}
  94. template <typename T>
  95. Object::Object(T & data, std::string name)
  96. : type_(typeid(T)), type_name_(Name<T>), name_(std::move(name)), const_(false),
  97. data_(&data, [](auto *) {}), get_(&Object::accessor<T>) {}
  98. template <typename T>
  99. Object::Object(T && data, std::string name)
  100. : type_(typeid(T)), type_name_(Name<T>), name_(std::move(name)), const_(true),
  101. data_(std::make_shared<T>(std::move(data))), get_(&Object::getter<T>),
  102. clone_(&Object::deep<T>) {}
  103. template <typename T>
  104. Object::Object(Proxy<T> data, std::string name)
  105. : type_(typeid(T)), type_name_(Name<T>), name_(std::move(name)), proxy_(true), const_(false),
  106. data_(std::make_shared<Proxy<T>>(std::move(data))),
  107. get_(&Object::getter<T>) {}
  108. template <typename V> Object & Object::operator=(V const & value) {
  109. if (TypeCast<V>::has_cast(*this)) {
  110. TypeCast<V>::set(*this, value);
  111. } else {
  112. cast<V &>() = value;
  113. }
  114. return *this;
  115. }
  116. template <typename T> T Object::cast() const {
  117. // Why can we decay this away?
  118. using V = std::remove_const_t<std::remove_reference_t<T>>;
  119. // 1) Casting is only allowed on non-reference rvals
  120. if constexpr (std::is_same_v<V, T>) {
  121. if (TypeCast<V>::has_cast(*this)) { return TypeCast<V>::get(*this); }
  122. }
  123. // 2) typeid does not respect const
  124. if (!is_a<V>()) { throw std::bad_cast(); }
  125. // 3) We guard against using mutable-reference on immutable Object here
  126. if (std::is_same_v<T, V &> && const_) { throw std::bad_cast(); }
  127. // 4) Proxy always contains mutable data
  128. if (proxy_) { return T(*std::static_pointer_cast<Proxy<V>>(data_)); }
  129. // 5) The const will be re-added by type coercion
  130. return *std::static_pointer_cast<V>(data_);
  131. }
  132. }