#pragma once #include #include #include #include namespace cru { template class SelfResolvable; template class ObjectResolver { friend SelfResolvable; template friend class ObjectResolver; private: template using Accessor_ = std::function&)>; using ThisAccessor_ = Accessor_; explicit ObjectResolver(T* o) : shared_object_ptr_(new void*(o)), accessor_([](const std::shared_ptr& ptr) { return static_cast(*ptr); }) {} explicit ObjectResolver(std::shared_ptr ptr, ThisAccessor_ accessor) : shared_object_ptr_(std::move(ptr)), accessor_(std::move(accessor)) {} template static ThisAccessor_ CreateAccessor(Accessor_ parent_accessor) { return [parent_accessor = std::move(parent_accessor)](const std::shared_ptr& ptr) { return static_cast(parent_accessor(ptr)); }; } public: template >> ObjectResolver(const ObjectResolver& other) : shared_object_ptr_(other.shared_object_ptr_), accessor_(CreateAccessor(other.accessor_)) {} template >> ObjectResolver(ObjectResolver&& other) : shared_object_ptr_(std::move(other.shared_object_ptr_)), accessor_(CreateAccessor(std::move(other.accessor_))) {} ObjectResolver(const ObjectResolver&) = default; ObjectResolver& operator=(const ObjectResolver&) = default; ObjectResolver(ObjectResolver&&) = default; ObjectResolver& operator=(ObjectResolver&&) = default; ~ObjectResolver() = default; template >> ObjectResolver& operator=(const ObjectResolver& other) { if (this != &other) { this->shared_object_ptr_ = other.shared_object_ptr_; this->accessor_ = CreateAccessor(other.accessor_); } return *this; } template >> ObjectResolver& operator=(ObjectResolver&& other) { if (this != &other) { this->shared_object_ptr_ = std::move(other.shared_object_ptr_); this->accessor_ = CreateAccessor(std::move(other.shared_object_ptr_)); } return *this; } bool IsValid() const { return this->shared_object_ptr_ != nullptr; } T* Resolve() const { assert(IsValid()); return this->accessor_(this->shared_object_ptr_); } /** * @remarks So this class can be used as a functor. */ T* operator()() const { return Resolve(); } template >> operator ObjectResolver() const { return ObjectResolver(*this); } private: void SetResolvedObject(T* o) { assert(IsValid()); *this->shared_object_ptr_ = o; } private: std::shared_ptr shared_object_ptr_; std::function&)> accessor_; }; /** * @remarks * This class is not copyable and movable since subclass is polymorphic and * copying is then nonsense. However, you can even delete move capability in * subclass because it may also be nonsense for subclass. The move capability is * optional. * * Whether this class needs to be thread-safe still has to be considered. */ template class SelfResolvable { public: SelfResolvable() : resolver_(CastToSubClass()) {} SelfResolvable(const SelfResolvable&) = delete; SelfResolvable& operator=(const SelfResolvable&) = delete; // Resolvers to old object will resolve to new object. SelfResolvable(SelfResolvable&& other) : resolver_(std::move(other.resolver_)) { this->resolver_.SetResolvedObject(CastToSubClass()); } // Old resolvers for this object will resolve to nullptr. // Other's resolvers will now resolve to this. SelfResolvable& operator=(SelfResolvable&& other) { if (this != &other) { this->resolver_ = std::move(other.resolver_); this->resolver_.SetResolvedObject(CastToSubClass()); } return *this; } virtual ~SelfResolvable() { if (this->resolver_.IsValid()) { this->resolver_.SetResolvedObject(nullptr); } } ObjectResolver CreateResolver() { return resolver_; } private: T* CastToSubClass() { return static_cast(this); } private: ObjectResolver resolver_; }; } // namespace cru