| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- //
- // object.h
- // reflection
- //
- // Created by Sam Jaffe on 7/3/22.
- // Copyright © 2022 Sam Jaffe. All rights reserved.
- //
- #pragma once
- #include <stdexcept>
- #include <typeindex>
- #include <utility>
- #include "reflection/forward.h"
- namespace reflection {
- class Object {
- public:
- template <typename T> Object(T const & data, std::string name = "this");
- template <typename T> Object(T & data, std::string name = "this");
- template <typename T> Object(T && data, std::string name = "this");
- template <typename T> Object(Proxy<T> data, std::string name = "this");
- template <typename V> Object & operator=(V const & value);
- Object own() const { return clone_(*this); }
- std::string_view name() const {
- return std::string_view(name_).substr(name_.rfind('.') + 1);
- }
- std::string_view path() const { return name_; }
- std::type_index type() const { return type_; }
- template <typename T> bool is_a() const { return type_ == typeid(T); }
- template <typename T> operator T &() const & { return cast<T &>(); }
- template <typename T> operator T const &() const & {
- return cast<T const &>();
- }
- template <typename T> operator T() const & { return cast<T>(); }
- template <typename T> operator T() & { return cast<T>(); }
- template <typename T> operator T() && {
- return const_ ? cast<T>() : std::move(cast<T &>());
- }
- Object get(std::string_view id) & { return (this->*get_)(id); }
- Object get(std::string_view id) const & { return (this->*get_)(id); }
- Object get(std::string_view id) && { return (this->*get_)(id).own(); }
- template <typename C,
- typename = std::enable_if_t<std::is_constructible_v<
- std::string_view, decltype(*std::begin(std::declval<C>()))>>>
- Object get(C const & container) const {
- return get(*this, std::begin(container), std::end(container));
- }
- template <typename It> static Object get(Object o, It it, It const end) {
- for (; it != end; ++it) {
- o = std::move(o).get(*it);
- }
- return o;
- }
- private:
- template <typename T> T cast() const;
- template <typename T> Object getter(std::string_view id) const;
- template <typename T> Object accessor(std::string_view id) const;
- template <typename T> static Object deep(Object const & obj) {
- return Object(obj.cast<T>(), obj.name_);
- }
- private:
- std::type_index type_; //
- std::string name_; // The property name as a dot-separated path
- // Are we using a proxy class to store non-trivial setter behavior
- bool proxy_{false};
- bool const_; // Is the object immutable (const-ref or rvalue)
- std::shared_ptr<void> data_;
- Object (Object::*get_)(std::string_view) const;
- Object (*clone_)(Object const &) = [](Object const & obj) { return obj; };
- };
- }
- #include "reflection/reflection.h"
- #include "reflection/typecast.h"
- namespace reflection {
- template <typename T> Object Object::getter(std::string_view id) const {
- return Reflection<T>::getter(id)(cast<T const &>(),
- name_ + "." + std::string(id));
- }
- template <typename T> Object Object::accessor(std::string_view id) const {
- return Reflection<T>::accessor(id)(cast<T &>(),
- name_ + "." + std::string(id));
- }
- template <typename T>
- Object::Object(T const & data, std::string name)
- : type_(typeid(T)), name_(std::move(name)), const_(true),
- data_(const_cast<T *>(&data), [](auto *) {}), get_(&Object::getter<T>) {}
- template <typename T>
- Object::Object(T & data, std::string name)
- : type_(typeid(T)), name_(std::move(name)), const_(false),
- data_(&data, [](auto *) {}), get_(&Object::accessor<T>) {}
- template <typename T>
- Object::Object(T && data, std::string name)
- : type_(typeid(T)), name_(std::move(name)), const_(true),
- data_(std::make_shared<T>(std::move(data))), get_(&Object::getter<T>),
- clone_(&Object::deep<T>) {}
- template <typename T>
- Object::Object(Proxy<T> data, std::string name)
- : type_(typeid(T)), name_(std::move(name)), proxy_(true), const_(false),
- data_(std::make_shared<Proxy<T>>(std::move(data))),
- get_(&Object::getter<T>) {}
- template <typename V> Object & Object::operator=(V const & value) {
- if (TypeCast<V>::has_cast(*this)) {
- TypeCast<V>::set(*this, value);
- } else {
- cast<V &>() = value;
- }
- return *this;
- }
- template <typename T> T Object::cast() const {
- // Why can we decay this away?
- using V = std::remove_const_t<std::remove_reference_t<T>>;
- // 1) Casting is only allowed on non-reference rvals
- if constexpr (std::is_same_v<V, T>) {
- if (TypeCast<V>::has_cast(*this)) { return TypeCast<V>::get(*this); }
- }
- // 2) typeid does not respect const
- if (!is_a<V>()) { throw std::bad_cast(); }
- // 3) We guard against using mutable-reference on immutable Object here
- if (std::is_same_v<T, V &> && const_) { throw std::bad_cast(); }
- // 4) Proxy always contains mutable data
- if (proxy_) { return T(*std::static_pointer_cast<Proxy<V>>(data_)); }
- // 5) The const will be re-added by type coercion
- return *std::static_pointer_cast<V>(data_);
- }
- }
|