object.h 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. namespace reflection {
  14. class Object {
  15. public:
  16. template <typename T> Object(T const &data, std::string name = "this");
  17. template <typename T> Object(T &data, std::string name = "this");
  18. template <typename T> Object(T &&data, std::string name = "this");
  19. template <typename T> Object(Proxy<T> data, std::string name = "this");
  20. Object own() const { return clone_(*this); }
  21. std::string_view name() const {
  22. return std::string_view(name_).substr(name_.rfind('.') + 1);
  23. }
  24. std::string_view path() const { return name_; }
  25. char const *type() const { return type_.name(); }
  26. template <typename T> bool is_a() const { return type_ == typeid(T); }
  27. template <typename T> operator T &() const & { return cast<T &>(); }
  28. template <typename T> operator T const &() const & { return cast<T const &>(); }
  29. template <typename T> operator T () && {
  30. return is_a<T>() ? std::move(cast<T &>()) : cast<T const &>();
  31. }
  32. Object get(std::string_view id) & { return (this->*get_)(id); }
  33. Object get(std::string_view id) const & { return (this->*get_)(id); }
  34. Object get(std::string_view id) && { return (this->*get_)(id).own(); }
  35. template <typename C, typename =
  36. std::enable_if_t<std::is_constructible_v<std::string_view,
  37. decltype(*std::begin(std::declval<C>()))>>>
  38. Object get(C const &container) const {
  39. return get(*this, std::begin(container), std::end(container));
  40. }
  41. private:
  42. template <typename It> static Object get(Object o, It it, It const end) {
  43. for (; it != end; ++it) {
  44. o = std::move(o).get(*it);
  45. }
  46. return o;
  47. }
  48. template <typename T> T cast() const {
  49. // Why can we decay this away?
  50. using V = std::remove_const_t<std::remove_reference_t<T>>;
  51. // 1) typeid does not respect const
  52. if (!is_a<V>()) { throw std::bad_cast(); }
  53. // 2) We guard against using mutable-reference on immutable Object here
  54. if (std::is_same_v<T, V &> && const_) { throw std::bad_cast(); }
  55. // 3) Proxy always contains mutable data
  56. if (proxy_) {
  57. return T(*std::static_pointer_cast<Proxy<V>>(data_));
  58. }
  59. // 4) The const will be re-added by type coercion
  60. return *std::static_pointer_cast<V>(data_);
  61. }
  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 name_; // The property name as a dot-separated path
  70. bool proxy_{false}; // Are we using a proxy class to store non-trivial setter behavior
  71. bool const_; // Is the object immutable (const-ref or rvalue)
  72. std::shared_ptr<void> data_;
  73. Object (Object::*get_)(std::string_view) const;
  74. Object (*clone_)(Object const &) = [](Object const &obj) { return obj; };
  75. };
  76. }
  77. #include "reflection/reflection.h"
  78. namespace reflection {
  79. template <typename T> Object Object::getter(std::string_view id) const {
  80. return Reflection<T>::getter(id)(cast<T const &>(), name_ + "." + std::string(id));
  81. }
  82. template <typename T> Object Object::accessor(std::string_view id) const {
  83. return Reflection<T>::accessor(id)(cast<T &>(), name_ + "." + std::string(id));
  84. }
  85. template <typename T> Object::Object(T const &data, std::string name)
  86. : type_(typeid(T)), name_(std::move(name)), const_(true),
  87. data_(const_cast<T *>(&data), [](auto*){}), get_(&Object::getter<T>) {}
  88. template <typename T> Object::Object(T &data, std::string name)
  89. : type_(typeid(T)), name_(std::move(name)), const_(false), data_(&data, [](auto*){}),
  90. get_(&Object::accessor<T>) {}
  91. template <typename T> Object::Object(T &&data, std::string name)
  92. : type_(typeid(T)), name_(std::move(name)), const_(true),
  93. data_(std::make_shared<T>(std::move(data))), get_(&Object::getter<T>),
  94. clone_(&Object::deep<T>) {}
  95. template <typename T> Object::Object(Proxy<T> data, std::string name)
  96. : type_(typeid(T)), name_(std::move(name)), proxy_(true), const_(false),
  97. data_(std::make_shared<Proxy<T>>(std::move(data))), get_(&Object::getter<T>) {}
  98. }