// // object.h // reflection // // Created by Sam Jaffe on 7/3/22. // Copyright © 2022 Sam Jaffe. All rights reserved. // #pragma once #include #include #include #include "reflection/forward.h" namespace reflection { class Object { public: template Object(T const & data, std::string name = "this"); template Object(T & data, std::string name = "this"); template Object(T && data, std::string name = "this"); template Object(Proxy data, std::string name = "this"); template 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 bool is_a() const { return type_ == typeid(T); } template operator T &() const & { return cast(); } template operator T const &() const & { return cast(); } template operator T() const & { return cast(); } template operator T() & { return cast(); } template operator T() && { return const_ ? cast() : std::move(cast()); } 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 ()))>>> Object get(C const & container) const { return get(*this, std::begin(container), std::end(container)); } template static Object get(Object o, It it, It const end) { for (; it != end; ++it) { o = std::move(o).get(*it); } return o; } private: template T cast() const; template Object getter(std::string_view id) const; template Object accessor(std::string_view id) const; template static Object deep(Object const & obj) { return Object(obj.cast(), 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 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 Object Object::getter(std::string_view id) const { return Reflection::getter(id)(cast(), name_ + "." + std::string(id)); } template Object Object::accessor(std::string_view id) const { return Reflection::accessor(id)(cast(), name_ + "." + std::string(id)); } template Object::Object(T const & data, std::string name) : type_(typeid(T)), name_(std::move(name)), const_(true), data_(const_cast(&data), [](auto *) {}), get_(&Object::getter) {} template Object::Object(T & data, std::string name) : type_(typeid(T)), name_(std::move(name)), const_(false), data_(&data, [](auto *) {}), get_(&Object::accessor) {} template Object::Object(T && data, std::string name) : type_(typeid(T)), name_(std::move(name)), const_(true), data_(std::make_shared(std::move(data))), get_(&Object::getter), clone_(&Object::deep) {} template Object::Object(Proxy data, std::string name) : type_(typeid(T)), name_(std::move(name)), proxy_(true), const_(false), data_(std::make_shared>(std::move(data))), get_(&Object::getter) {} template Object & Object::operator=(V const & value) { if (TypeCast::has_cast(*this)) { TypeCast::set(*this, value); } else { cast() = value; } return *this; } template T Object::cast() const { // Why can we decay this away? using V = std::remove_const_t>; // 1) Casting is only allowed on non-reference rvals if constexpr (std::is_same_v) { if (TypeCast::has_cast(*this)) { return TypeCast::get(*this); } } // 2) typeid does not respect const if (!is_a()) { throw std::bad_cast(); } // 3) We guard against using mutable-reference on immutable Object here if (std::is_same_v && const_) { throw std::bad_cast(); } // 4) Proxy always contains mutable data if (proxy_) { return T(*std::static_pointer_cast>(data_)); } // 5) The const will be re-added by type coercion return *std::static_pointer_cast(data_); } }