From 53cfb87a038c55f8892eeda9615c234e8a814cbb Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Fri, 21 Aug 2026 17:35:07 +0500 Subject: [PATCH 1/2] apply new erased logic to ObjPtr --- aether/obj/domain.cpp | 4 +- aether/obj/domain.h | 23 +--- aether/obj/obj_ptr.h | 230 +++++++++++++++++++++--------------- aether/obj/obj_ptr_base.cpp | 110 +++++++++++++++-- aether/obj/obj_ptr_base.h | 23 +++- aether/ptr/ptr.h | 4 +- 6 files changed, 263 insertions(+), 131 deletions(-) diff --git a/aether/obj/domain.cpp b/aether/obj/domain.cpp index cd367440..54bdff6b 100644 --- a/aether/obj/domain.cpp +++ b/aether/obj/domain.cpp @@ -25,7 +25,7 @@ namespace ae { DomainGraph::DomainGraph(Domain* domain) : domain(domain) { assert(domain); } -Ptr DomainGraph::LoadRootImpl(ObjId obj_id) { +Ptr DomainGraph::LoadRoot(ObjId obj_id) { if (!obj_id.is_valid()) { return {}; } @@ -65,7 +65,7 @@ Ptr DomainGraph::LoadCopyImpl(ObjId ref_id, ObjId copy_id) { return ptr; } -void DomainGraph::SaveRootImpl(Ptr const& ptr, ObjId obj_id) { +void DomainGraph::SaveRoot(Ptr const& ptr, ObjId obj_id) { if (!ptr) { return; } diff --git a/aether/obj/domain.h b/aether/obj/domain.h index e25de5ff..dd989da2 100644 --- a/aether/obj/domain.h +++ b/aether/obj/domain.h @@ -106,19 +106,15 @@ class DomainGraph { public: explicit DomainGraph(Domain* domain); - // Load saved state of object - template - Ptr LoadPtr(ObjId obj_id); - // Save state of object - template - void SavePtr(Ptr const& ptr, ObjId obj_id); + // Load saved state of object. + Ptr LoadRoot(ObjId obj_id); + // Save state of object. + void SaveRoot(Ptr const& ptr, ObjId obj_id); // Load a copy of object template Ptr LoadCopy(ObjId ref_id, ObjId copy_id); - Ptr LoadRootImpl(ObjId obj_id); Ptr LoadCopyImpl(ObjId ref_id, ObjId copy_id); - void SaveRootImpl(Ptr const& ptr, ObjId obj_id); template seri::SeriResult Load(T& obj, ObjId obj_id); @@ -166,17 +162,6 @@ class Domain { std::map> id_objects_; }; -template -Ptr DomainGraph::LoadPtr(ObjId obj_id) { - Ptr ptr = LoadRootImpl(obj_id); - return ptr; -} - -template -void DomainGraph::SavePtr(Ptr const& ptr, ObjId obj_id) { - SaveRootImpl(ptr, obj_id); -} - template Ptr DomainGraph::LoadCopy(ObjId ref_id, ObjId copy_id) { return Ptr{LoadCopyImpl(ref_id, copy_id)}; diff --git a/aether/obj/obj_ptr.h b/aether/obj/obj_ptr.h index 89167782..39e8d530 100644 --- a/aether/obj/obj_ptr.h +++ b/aether/obj/obj_ptr.h @@ -68,6 +68,47 @@ struct CreateWith { std::optional flags; }; +/** + * \brief A typed, non-owning view of an ObjectPtrBase cache. + * + * The referenced cache and its ObjPtr owner must outlive this view and must not + * be reset or replaced while the view is used. + */ +template +class ProxyPtr { + public: + explicit ProxyPtr(Ptr const& ptr) noexcept : ptr_{ptr} {} + ProxyPtr(Ptr&&) = delete; + ProxyPtr(Ptr const&&) = delete; + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(ptr_); + } + + [[nodiscard]] T* get() const noexcept { return static_cast(ptr_.get()); } + + [[nodiscard]] T* operator->() const noexcept { + assert(ptr_ && "Dereferencing uninitialized ProxyPtr"); + return get(); + } + + [[nodiscard]] T& operator*() const noexcept { + assert(ptr_ && "Dereferencing uninitialized ProxyPtr"); + return *get(); + } + + // Materializing Ptr is intentional when the caller needs ownership. + template + requires(std::is_convertible_v) + // NOLINTNEXTLINE(*explicit*) + operator Ptr() const noexcept { + return Ptr{ptr_}; + } + + private: + Ptr const& ptr_; +}; + template class ObjPtr : public ObjectPtrBase { template @@ -92,19 +133,17 @@ class ObjPtr : public ObjectPtrBase { */ static ObjPtr MakeFromThis(T* self); - ObjPtr() noexcept : ObjectPtrBase{}, ptr_{} {} - - ~ObjPtr() { - if (ptr_) { - ptr_.Reset(); - } - } + ObjPtr() noexcept : ObjectPtrBase{} {} ObjPtr(Domain* domain, ObjId obj_id, ObjFlags flags) : ObjectPtrBase{domain, obj_id, flags} {} - ObjPtr(Domain* domain, ObjId obj_id, ObjFlags flags, Ptr ptr) noexcept - : ObjectPtrBase{domain, obj_id, flags}, ptr_{std::move(ptr)} {} + ObjPtr(Domain* domain, ObjId obj_id, ObjFlags flags, Ptr&& ptr) noexcept + : ObjectPtrBase{domain, obj_id, flags, std::move(ptr)} {} + + ObjPtr(Domain* domain, ObjId obj_id, ObjFlags flags, + Ptr const& ptr) noexcept + : ObjectPtrBase{domain, obj_id, flags, ptr} {} ObjPtr(ObjPtr const& ptr) noexcept = default; ObjPtr(ObjPtr&& ptr) noexcept = default; @@ -112,38 +151,43 @@ class ObjPtr : public ObjectPtrBase { template requires(IsAbleToCast::value) ObjPtr(ObjPtr ptr) noexcept // NOLINT(*explicit*) - : ObjectPtrBase{static_cast(ptr)}, - ptr_{std::move(ptr.ptr_)} {} + : ObjectPtrBase{std::move(static_cast(ptr))} {} ObjPtr& operator=(ObjPtr const& ptr) noexcept = default; ObjPtr& operator=(ObjPtr&& ptr) noexcept = default; - template ::value, int> = 0> + template + requires(IsAbleToCast::value) ObjPtr& operator=(ObjPtr ptr) noexcept { - ptr_.Reset(); - ObjectPtrBase::operator=(static_cast(ptr)); - ptr_ = std::move(ptr.ptr_); + ObjectPtrBase::operator=(std::move(static_cast(ptr))); return *this; } - Ptr const& operator->(); - Ptr const& operator->() const; + using ObjectPtrBase::domain; + using ObjectPtrBase::flags; + using ObjectPtrBase::id; + using ObjectPtrBase::is_loaded; + using ObjectPtrBase::is_valid; + using ObjectPtrBase::Reset; + using ObjectPtrBase::Save; + using ObjectPtrBase::SetFlags; + + T* operator->() &; + T* operator->() const&; + T* operator->() &&; + T* operator->() const&&; T& operator*(); - T const& operator*() const; + T& operator*() const; - bool is_valid() const; - bool is_loaded() const; explicit operator bool() const { return is_valid() && is_loaded(); } /** * \brief Load current object to Ptr */ - Ptr const& Load(); - Ptr const& Load() const; - /** - * \brief Save current object state - */ - void Save() const; + ProxyPtr Load() &; // NOLINT(*shadowing*) + ProxyPtr Load() const&; // NOLINT(*shadowing*) + Ptr Load() &&; // NOLINT(*shadowing*) + Ptr Load() const&&; // NOLINT(*shadowing*) /** * \brief Clone current object into new object * \param obj_id Optional object ID to use for the new object, if not provided @@ -156,11 +200,6 @@ class ObjPtr : public ObjectPtrBase { auto WithLoaded(Func&& func) -> decltype(auto); template auto WithLoaded(Func&& func) const -> decltype(auto); - - void Reset(); - - private: - Ptr ptr_; }; template @@ -203,70 +242,65 @@ ObjPtr ObjPtr::MakeFromThis(T* self) { } template -Ptr const& ObjPtr::operator->() { - auto const& ptr = Load(); +T* ObjPtr::operator->() & { + auto ptr = Load(); assert(ptr && "Dereferencing invalid object"); - return ptr; + return ptr.get(); } template -Ptr const& ObjPtr::operator->() const { - auto const& ptr = Load(); +T* ObjPtr::operator->() const& { + auto ptr = Load(); assert(ptr && "Dereferencing invalid object"); - return ptr; + return ptr.get(); } template -T& ObjPtr::operator*() { - auto const& ptr = Load(); +T* ObjPtr::operator->() && { + auto ptr = std::move(*this).Load(); assert(ptr && "Dereferencing invalid object"); - return *ptr; + return ptr.get(); } template -T const& ObjPtr::operator*() const { - auto const& ptr = Load(); +T* ObjPtr::operator->() const&& { + auto ptr = std::move(*this).Load(); + assert(ptr && "Dereferencing invalid object"); + return ptr.get(); +} + +template +T& ObjPtr::operator*() { + auto ptr = Load(); assert(ptr && "Dereferencing invalid object"); return *ptr; } template -bool ObjPtr::is_valid() const { - return id().is_valid(); +T& ObjPtr::operator*() const { + auto ptr = Load(); + assert(ptr && "Dereferencing invalid object"); + return *ptr; } + template -bool ObjPtr::is_loaded() const { - return static_cast(ptr_); +ProxyPtr ObjPtr::Load() & { + return ProxyPtr{ObjectPtrBase::LoadCached()}; } template -Ptr const& ObjPtr::Load() { - // already loaded - if (ptr_) { - return ptr_; - } - // return empty for invalid object - if (!is_valid()) { - return ptr_; - } - ptr_ = DomainGraph{domain()}.template LoadPtr(id()); - if (ptr_) { - flags_ = flags() & ~ObjFlags::kUnloaded; - } - return ptr_; +ProxyPtr ObjPtr::Load() const& { + return ProxyPtr{ObjectPtrBase::LoadCached()}; } template -Ptr const& ObjPtr::Load() const { - return const_cast*>(this)->Load(); // NOLINT(*const-cast) +Ptr ObjPtr::Load() && { + return Ptr{ObjectPtrBase::LoadCached()}; } template -void ObjPtr::Save() const { - if (!ptr_) { - return; - } - DomainGraph{domain()}.SavePtr(ptr_, id()); +Ptr ObjPtr::Load() const&& { + return Ptr{ObjectPtrBase::LoadCached()}; } template @@ -286,11 +320,10 @@ ObjPtr ObjPtr::Clone(ObjId obj_id) const { template auto WithLoadedImpl(U&& obj_ptr, Func&& func) -> decltype(auto) { - using InvokeRes = - std::invoke_result_t(obj_ptr).Load())>; + auto ptr = std::forward(obj_ptr).Load(); + using InvokeRes = decltype(std::invoke(std::forward(func), ptr)); using ReturnType = std::conditional_t, bool, std::optional>; - auto const& ptr = std::forward(obj_ptr).Load(); if (!ptr) { return ReturnType{}; } @@ -314,12 +347,6 @@ auto ObjPtr::WithLoaded(Func&& func) const -> decltype(auto) { return WithLoadedImpl(*this, std::forward(func)); } -template -void ObjPtr::Reset() { - ptr_.Reset(); - flags_ = flags() & ObjFlags::kUnloaded; -} - } // namespace ae namespace ae::seri { @@ -328,50 +355,57 @@ struct Serializer, ObjPtr> { using Archive = BinaryArchive; SeriResult Seri(Archive& archive, Meta const> meta) const { - TRY_RESULT((archive.Save(static_cast(meta.value)))); - if (meta.value.ptr_) { - archive.buffer().domain_graph->SavePtr(meta.value.ptr_, meta.value.id()); - } - return Ok{seri::good}; + return archive.Save(static_cast(meta.value)); } SeriResult Deseri(Archive& archive, Meta> meta) const { - TRY_RESULT((archive.Load(static_cast(meta.value)))); - - if (!(meta.value.flags() & ObjFlags::kUnloadedByDefault) && - !(meta.value.flags() & ObjFlags::kUnloaded)) { - // Load the object only if it's valid and unloaded flag is not set - meta.value.ptr_ = - archive.buffer().domain_graph->LoadPtr(meta.value.id()); - } - return Ok{seri::good}; + return archive.Load(static_cast(meta.value)); } }; } // namespace ae::seri namespace ae::domain_visitor { template -struct NodeVisitor> : NodeVisitor> { +struct NodeVisitor> : NodeVisitor> { using Policy = PolicyMatch; - using Base = NodeVisitor>; + using Base = NodeVisitor>; + void Visit(ae::ObjPtr& obj_ptr, CycleDetector& cycle_detector, + PtrRefDnv const& visitor) const { + Base::Visit(obj_ptr.cached(), cycle_detector, visitor); + } + + void Visit(ae::ObjPtr const& obj_ptr, CycleDetector& cycle_detector, + PtrRefDnv const& visitor) const { + Base::Visit(obj_ptr.cached(), cycle_detector, visitor); + } template + requires(!std::is_same_v>) void Visit(ae::ObjPtr& obj_ptr, CycleDetector& cycle_detector, Visitor&& visitor) const { - if (obj_ptr.is_loaded()) { - Base::Visit(obj_ptr.Load(), cycle_detector, - std::forward(visitor)); + auto proxy = ae::ProxyPtr{obj_ptr.cached()}; + if (proxy) { + ApplyVisit(*proxy, cycle_detector, std::forward(visitor)); } } template + requires(!std::is_same_v>) void Visit(ae::ObjPtr const& obj_ptr, CycleDetector& cycle_detector, Visitor&& visitor) const { - if (obj_ptr.is_loaded()) { - Base::Visit(obj_ptr.Load(), cycle_detector, - std::forward(visitor)); + auto proxy = ae::ProxyPtr{obj_ptr.cached()}; + if (proxy) { + ApplyVisit(*proxy, cycle_detector, std::forward(visitor)); } } + + private: + template + void ApplyVisit(U&& obj, CycleDetector& cycle_detector, + Visitor&& visitor) const { + domain_visitor::ApplyVisitor(std::forward(obj), cycle_detector, + std::forward(visitor)); + } }; } // namespace ae::domain_visitor diff --git a/aether/obj/obj_ptr_base.cpp b/aether/obj/obj_ptr_base.cpp index 646de5f2..feede2e9 100644 --- a/aether/obj/obj_ptr_base.cpp +++ b/aether/obj/obj_ptr_base.cpp @@ -17,17 +17,36 @@ #include "aether/obj/obj_ptr_base.h" #include "aether/obj/domain.h" +#include "aether/obj/obj.h" #include "aether/obj/obj_id.h" namespace ae { ObjectPtrBase::ObjectPtrBase() - : domain_{nullptr}, id_{}, flags_{ObjFlags::kUnloaded} {} + : domain_{nullptr}, flags_{ObjFlags::kUnloaded} {} ObjectPtrBase::ObjectPtrBase(Domain* domain, ObjId obj_id, ObjFlags flags) : domain_{domain}, id_{obj_id}, flags_{flags} {} -ObjectPtrBase::ObjectPtrBase(ObjectPtrBase const& ptr) noexcept = default; +ObjectPtrBase::ObjectPtrBase(Domain* domain, ObjId obj_id, ObjFlags flags, + Ptr ptr) noexcept + : domain_{domain}, id_{obj_id}, flags_{flags}, cached_{std::move(ptr)} {} + +ObjectPtrBase::ObjectPtrBase(ObjectPtrBase const& ptr) noexcept + : domain_{ptr.domain_}, id_{ptr.id_}, flags_{ptr.flags_} { + if (ptr.cached_) { + cached_ = ptr.cached_; + } +} + +ObjectPtrBase::ObjectPtrBase(ObjectPtrBase&& ptr) noexcept + : domain_{ptr.domain_}, id_{ptr.id_}, flags_{ptr.flags_} { + if (ptr.cached_) { + cached_ = std::move(ptr.cached_); + } +} + +ObjectPtrBase::~ObjectPtrBase() = default; ObjId ObjectPtrBase::id() const { return id_; } ObjFlags ObjectPtrBase::flags() const { return flags_; } @@ -35,24 +54,99 @@ Domain* ObjectPtrBase::domain() const { return domain_; } void ObjectPtrBase::SetFlags(ObjFlags flags) { flags_ = flags; } -ObjectPtrBase& ObjectPtrBase::operator=(ObjectPtrBase const& ptr) noexcept = - default; +bool ObjectPtrBase::is_valid() const { return id_.is_valid(); } + +bool ObjectPtrBase::is_loaded() const { return static_cast(cached_); } + +Ptr const& ObjectPtrBase::LoadCached() { + if (cached_) { + return cached_; + } + if (!is_valid()) { + return cached_; + } + cached_ = DomainGraph{domain_}.LoadRoot(id_); + if (cached_) { + flags_ = flags_ & ~ObjFlags::kUnloaded; + } + return cached_; +} + +Ptr const& ObjectPtrBase::LoadCached() const { + return const_cast(this)->LoadCached(); // NOLINT(*const-cast) +} + +void ObjectPtrBase::Save() const { + if (cached_) { + DomainGraph{domain_}.SaveRoot(cached_, id_); + } +} + +void ObjectPtrBase::Reset() { + cached_.Reset(); + flags_ = flags_ & ObjFlags::kUnloaded; +} + +Ptr const& ObjectPtrBase::cached() const { return cached_; } +Ptr& ObjectPtrBase::cached() { return cached_; } + +ObjectPtrBase& ObjectPtrBase::operator=(ObjectPtrBase const& ptr) noexcept { + if (this != &ptr) { + domain_ = ptr.domain_; + id_ = ptr.id_; + flags_ = ptr.flags_; + if (ptr.cached_) { + cached_ = ptr.cached_; + } else { + cached_.Reset(); + } + } + return *this; +} + +ObjectPtrBase& ObjectPtrBase::operator=(ObjectPtrBase&& ptr) noexcept { + if (this != &ptr) { + domain_ = ptr.domain_; + id_ = ptr.id_; + flags_ = ptr.flags_; + + // Ptr move assignment swaps storage after resetting its destination. Move + // through a temporary and clear this cache first so ptr cannot receive the + // cache previously held by this ObjectPtrBase. + auto cache = std::move(ptr.cached_); + cached_ = Ptr{}; + cached_ = std::move(cache); + } + return *this; +} namespace seri { using ObjPtrBaseSerializer = Serializer, ObjectPtrBase>; SeriResult ObjPtrBaseSerializer::Seri(Archive& archive, - Meta meta) const { + Meta meta) { TRY_RESULT((archive.buffer().Write(DataTag{meta.value.id_}))); - return archive.buffer().Write(DataTag{meta.value.flags_}); + TRY_RESULT((archive.buffer().Write(DataTag{meta.value.flags_}))); + if (meta.value.cached_) { + archive.buffer().domain_graph->SaveRoot(meta.value.cached_, meta.value.id_); + } + return Ok{seri::good}; } SeriResult ObjPtrBaseSerializer::Deseri(Archive& archive, - Meta meta) const { + Meta meta) { meta.value.domain_ = archive.buffer().domain_graph->domain; TRY_RESULT((archive.buffer().Read(DataTag{meta.value.id_}))); - return archive.buffer().Read(DataTag{meta.value.flags_}); + TRY_RESULT((archive.buffer().Read(DataTag{meta.value.flags_}))); + meta.value.cached_.Reset(); + if (meta.value.is_valid() && + (meta.value.flags_ & ObjFlags::kUnloadedByDefault) == 0 && + (meta.value.flags_ & ObjFlags::kUnloaded) == 0) { + meta.value.cached_ = + archive.buffer().domain_graph->LoadRoot(meta.value.id_); + } + return Ok{seri::good}; } } // namespace seri } // namespace ae diff --git a/aether/obj/obj_ptr_base.h b/aether/obj/obj_ptr_base.h index f648c11b..ae4b063b 100644 --- a/aether/obj/obj_ptr_base.h +++ b/aether/obj/obj_ptr_base.h @@ -21,9 +21,12 @@ #include "aether/obj/domain.h" #include "aether/obj/obj_id.h" +#include "aether/ptr/ptr.h" namespace ae { +class Obj; + class ObjectPtrBase { friend struct seri::Serializer, ObjectPtrBase>; @@ -31,9 +34,14 @@ class ObjectPtrBase { public: ObjectPtrBase(); ObjectPtrBase(Domain* domain, ObjId obj_id, ObjFlags flags); + ObjectPtrBase(Domain* domain, ObjId obj_id, ObjFlags flags, + Ptr ptr) noexcept; ObjectPtrBase(ObjectPtrBase const& ptr) noexcept; + ObjectPtrBase(ObjectPtrBase&& ptr) noexcept; ObjectPtrBase& operator=(ObjectPtrBase const& ptr) noexcept; + ObjectPtrBase& operator=(ObjectPtrBase&& ptr) noexcept; + ~ObjectPtrBase(); ObjId id() const; ObjFlags flags() const; @@ -41,10 +49,21 @@ class ObjectPtrBase { void SetFlags(ObjFlags flags); + bool is_valid() const; + bool is_loaded() const; + Ptr const& LoadCached(); + Ptr const& LoadCached() const; + void Save() const; + void Reset(); + + Ptr const& cached() const; + Ptr& cached(); + protected: Domain* domain_; ObjId id_; ObjFlags flags_; + Ptr cached_; }; namespace seri { @@ -52,9 +71,9 @@ template <> struct Serializer, ObjectPtrBase> { using Archive = BinaryArchive; - SeriResult Seri(Archive& archive, Meta meta) const; + static SeriResult Seri(Archive& archive, Meta meta); - SeriResult Deseri(Archive& archive, Meta meta) const; + static SeriResult Deseri(Archive& archive, Meta meta); }; } // namespace seri } // namespace ae diff --git a/aether/ptr/ptr.h b/aether/ptr/ptr.h index c5aad133..b705324a 100644 --- a/aether/ptr/ptr.h +++ b/aether/ptr/ptr.h @@ -114,7 +114,7 @@ class Ptr : public PtrBase { : PtrBase{other.ptr_storage_}, ptr_{other.ptr_} {} template - Ptr(Ptr const& other) noexcept // NOLINT(google-explicit-constructor) + Ptr(Ptr const& other) noexcept // NOLINT(*explicit-constructor) // intentional implicit pointer-like upcast : PtrBase{other.ptr_storage_}, ptr_{static_cast(other.ptr_)} {} @@ -137,7 +137,7 @@ class Ptr : public PtrBase { Ptr(Ptr&& other) noexcept : PtrBase{std::move(other)}, ptr_{other.ptr_} {} template - Ptr(Ptr&& other) noexcept // NOLINT(google-explicit-constructor) + Ptr(Ptr&& other) noexcept // NOLINT(*explicit-constructor) // intentional implicit pointer-like upcast : PtrBase{std::move(other)}, ptr_{static_cast(other.ptr_)} {} From 12d806767e4be615ad5e3c8ce15534e09e50f444 Mon Sep 17 00:00:00 2001 From: BartolomeyKant Date: Fri, 21 Aug 2026 17:35:24 +0500 Subject: [PATCH 2/2] apply new logic and fix testgs --- aether/access_points/ethernet_access_point.h | 6 +- .../lora_module_access_point.cpp | 4 +- .../access_points/lora_module_access_point.h | 10 +- aether/access_points/modem_access_point.cpp | 3 +- aether/access_points/modem_access_point.h | 10 +- aether/access_points/wifi_access_point.cpp | 4 +- aether/access_points/wifi_access_point.h | 8 +- aether/adapters/lora_module_adapter.cpp | 4 +- aether/adapters/modem_adapter.cpp | 8 +- aether/adapters/parent_lora_module.h | 4 +- aether/adapters/parent_modem.h | 4 +- aether/ae_actions/select_client.h | 9 +- aether/aether.h | 18 +- aether/channels/channels_types.h | 1 - aether/channels/ethernet_channel.cpp | 11 +- aether/channels/ethernet_channel.h | 3 +- aether/channels/lora_module_channel.cpp | 6 +- aether/channels/lora_module_channel.h | 4 +- aether/channels/modem_channel.cpp | 8 +- aether/channels/modem_channel.h | 6 +- aether/channels/wifi_channel.cpp | 4 +- aether/channels/wifi_channel.h | 4 +- aether/client.cpp | 10 +- aether/client.h | 2 +- .../client_cloud_manager.cpp | 15 +- .../connection_manager/client_cloud_manager.h | 4 +- aether/dns/dns_c_ares.cpp | 7 +- aether/dns/dns_c_ares.h | 2 +- aether/dns/esp32_dns_resolve.cpp | 4 +- aether/dns/esp32_dns_resolve.h | 4 +- aether/registration_cloud.cpp | 13 +- aether/registration_cloud.h | 2 +- tests/test-object-system/CMakeLists.txt | 1 + tests/test-object-system/test-obj-create.cpp | 252 +++++++++++++++++- .../test-obj-ptr-forward-decl.cpp | 30 +++ 35 files changed, 375 insertions(+), 110 deletions(-) create mode 100644 tests/test-object-system/test-obj-ptr-forward-decl.cpp diff --git a/aether/access_points/ethernet_access_point.h b/aether/access_points/ethernet_access_point.h index 85fbef56..5e1f19c7 100644 --- a/aether/access_points/ethernet_access_point.h +++ b/aether/access_points/ethernet_access_point.h @@ -42,9 +42,9 @@ class EthernetAccessPoint : public AccessPoint { ObjPtr const& server) override; private: - Obj::ptr aether_; - Obj::ptr poller_; - Obj::ptr dns_resolver_; + ObjPtr aether_; + ObjPtr poller_; + ObjPtr dns_resolver_; }; } // namespace ae diff --git a/aether/access_points/lora_module_access_point.cpp b/aether/access_points/lora_module_access_point.cpp index 37397118..e5746d02 100644 --- a/aether/access_points/lora_module_access_point.cpp +++ b/aether/access_points/lora_module_access_point.cpp @@ -20,8 +20,8 @@ # include "aether/aether.h" # include "aether/lora_modules/ilora_module_driver.h" -# include "aether/channels/lora_module_channel.h" # include "aether/access_points/filter_endpoints.h" +# include "aether/channels/lora_module_channel.h" namespace ae { LoraModuleConnectAction::LoraModuleConnectAction( @@ -80,7 +80,7 @@ ActionPtr LoraModuleAccessPoint::Connect() { // reuse connect action if it's in progress if (!connect_action_) { connect_action_ = ActionPtr{ - *aether_.as(), lora_module_adapter_->lora_module_driver()}; + *aether_, lora_module_adapter_->lora_module_driver()}; connect_sub_ = connect_action_->FinishedEvent().Subscribe( [this]() { connect_action_.reset(); }); } diff --git a/aether/access_points/lora_module_access_point.h b/aether/access_points/lora_module_access_point.h index 46b0d506..1cd3cc15 100644 --- a/aether/access_points/lora_module_access_point.h +++ b/aether/access_points/lora_module_access_point.h @@ -20,14 +20,14 @@ #include "aether/config.h" #if AE_SUPPORT_LORA -# include "aether/obj/obj.h" +# include "aether/access_points/access_point.h" # include "aether/actions/action.h" # include "aether/actions/action_ptr.h" -# include "aether/types/state_machine.h" -# include "aether/lora_modules/ilora_module_driver.h" # include "aether/adapters/lora_module_adapter.h" # include "aether/events/event_subscription.h" -# include "aether/access_points/access_point.h" +# include "aether/lora_modules/ilora_module_driver.h" +# include "aether/obj/obj.h" +# include "aether/types/state_machine.h" namespace ae { class Aether; @@ -71,7 +71,7 @@ class LoraModuleAccessPoint final : public AccessPoint { std::vector const& endpoints) override; private: - Obj::ptr aether_; + ObjPtr aether_; LoraModuleAdapter::ptr lora_module_adapter_; ActionPtr connect_action_; Subscription connect_sub_; diff --git a/aether/access_points/modem_access_point.cpp b/aether/access_points/modem_access_point.cpp index e5bead1f..f68cc93c 100644 --- a/aether/access_points/modem_access_point.cpp +++ b/aether/access_points/modem_access_point.cpp @@ -85,8 +85,7 @@ ModemConnectAction& ModemAccessPoint::Connect() { // reuse connect action if it's in progress if (!connect_action_ || connect_action_->is_finished()) { - connect_action_.emplace(*aether_.Load().as(), - modem_adapter_->modem_driver()); + connect_action_.emplace(*aether_, modem_adapter_->modem_driver()); } return *connect_action_; } diff --git a/aether/access_points/modem_access_point.h b/aether/access_points/modem_access_point.h index 0c5672d3..d042645d 100644 --- a/aether/access_points/modem_access_point.h +++ b/aether/access_points/modem_access_point.h @@ -20,13 +20,13 @@ #include "aether/config.h" #if AE_SUPPORT_MODEMS -# include "aether/obj/obj.h" -# include "aether/ae_context.h" -# include "aether/events/events.h" +# include "aether/access_points/access_point.h" # include "aether/actions/action.h" # include "aether/adapters/modem_adapter.h" +# include "aether/ae_context.h" # include "aether/events/event_subscription.h" -# include "aether/access_points/access_point.h" +# include "aether/events/events.h" +# include "aether/obj/obj.h" namespace ae { class Aether; @@ -66,7 +66,7 @@ class ModemAccessPoint final : public AccessPoint { ObjPtr const& server) override; private: - Obj::ptr aether_; + ObjPtr aether_; ModemAdapter::ptr modem_adapter_; std::optional connect_action_; Subscription connect_sub_; diff --git a/aether/access_points/wifi_access_point.cpp b/aether/access_points/wifi_access_point.cpp index b791504f..3af71b2a 100644 --- a/aether/access_points/wifi_access_point.cpp +++ b/aether/access_points/wifi_access_point.cpp @@ -138,8 +138,8 @@ WifiConnectAction& WifiAccessPoint::Connect() { [](auto const& a) { return &a->driver(); }); assert(driver.has_value()); - connect_action_.emplace(*aether_.Load().as(), *this, **driver, - wifi_ap_, psp_, base_station_); + connect_action_.emplace(*aether_, *this, **driver, wifi_ap_, psp_, + base_station_); } return *connect_action_; } diff --git a/aether/access_points/wifi_access_point.h b/aether/access_points/wifi_access_point.h index 05765df2..fc5902cb 100644 --- a/aether/access_points/wifi_access_point.h +++ b/aether/access_points/wifi_access_point.h @@ -22,13 +22,13 @@ #include "aether/config.h" #if AE_SUPPORT_WIFIS +# include "aether/actions/action.h" # include "aether/ae_context.h" -# include "aether/obj/obj_ptr.h" # include "aether/events/events.h" -# include "aether/actions/action.h" +# include "aether/obj/obj_ptr.h" -# include "aether/wifi/wifi_driver.h" # include "aether/access_points/access_point.h" +# include "aether/wifi/wifi_driver.h" namespace ae { class Aether; @@ -91,7 +91,7 @@ class WifiAccessPoint final : public AccessPoint { private: ObjPtr aether_; - Obj::ptr adapter_; + ObjPtr adapter_; ObjPtr poller_; ObjPtr resolver_; WiFiAp wifi_ap_{}; diff --git a/aether/adapters/lora_module_adapter.cpp b/aether/adapters/lora_module_adapter.cpp index bcb4d97a..417813d9 100644 --- a/aether/adapters/lora_module_adapter.cpp +++ b/aether/adapters/lora_module_adapter.cpp @@ -17,9 +17,9 @@ #include "aether/adapters/lora_module_adapter.h" #if AE_SUPPORT_LORA +# include "aether/access_points/lora_module_access_point.h" # include "aether/aether.h" # include "aether/lora_modules/lora_module_factory.h" -# include "aether/access_points/lora_module_access_point.h" # include "aether/adapters/adapter_tele.h" @@ -56,7 +56,7 @@ std::vector LoraModuleAdapter::access_points() { ILoraModuleDriver& LoraModuleAdapter::lora_module_driver() { if (!lora_module_driver_) { lora_module_driver_ = LoraModuleDriverFactory::CreateLoraModule( - *aether_.as(), poller_, lora_module_init_); + *aether_, poller_, lora_module_init_); } return *lora_module_driver_; } diff --git a/aether/adapters/modem_adapter.cpp b/aether/adapters/modem_adapter.cpp index b806377d..65135994 100644 --- a/aether/adapters/modem_adapter.cpp +++ b/aether/adapters/modem_adapter.cpp @@ -17,11 +17,11 @@ #include "aether/adapters/modem_adapter.h" #if AE_SUPPORT_MODEMS +# include "aether/access_points/modem_access_point.h" # include "aether/aether.h" -# include "aether/poller/poller.h" # include "aether/modems/imodem_driver.h" # include "aether/modems/modem_factory.h" -# include "aether/access_points/modem_access_point.h" +# include "aether/poller/poller.h" # include "aether/adapters/adapter_tele.h" @@ -58,8 +58,8 @@ std::vector ModemAdapter::access_points() { IModemDriver& ModemAdapter::modem_driver() { if (!modem_driver_) { - modem_driver_ = ModemDriverFactory::CreateModem( - *aether_.Load().as(), poller_, modem_init_); + modem_driver_ = + ModemDriverFactory::CreateModem(*aether_, poller_, modem_init_); } return *modem_driver_; } diff --git a/aether/adapters/parent_lora_module.h b/aether/adapters/parent_lora_module.h index a8c835b7..594baf74 100644 --- a/aether/adapters/parent_lora_module.h +++ b/aether/adapters/parent_lora_module.h @@ -20,8 +20,8 @@ #include "aether/config.h" #if AE_SUPPORT_LORA -# include "aether/aether.h" # include "aether/adapters/adapter.h" +# include "aether/aether.h" # include "aether/lora_modules/lora_module_driver_types.h" namespace ae { @@ -41,7 +41,7 @@ class ParentLoraModuleAdapter : public Adapter { AE_OBJECT_REFLECT(AE_MMBRS(aether_, poller_, lora_module_init_)) - Obj::ptr aether_; + ObjPtr aether_; IPoller::ptr poller_; LoraModuleInit lora_module_init_; diff --git a/aether/adapters/parent_modem.h b/aether/adapters/parent_modem.h index 79777ce0..2fa14471 100644 --- a/aether/adapters/parent_modem.h +++ b/aether/adapters/parent_modem.h @@ -42,8 +42,8 @@ class ParentModemAdapter : public Adapter { AE_OBJECT_REFLECT(AE_MMBRS(aether_, poller_, modem_init_)) - Obj::ptr aether_; - Obj::ptr poller_; + ObjPtr aether_; + ObjPtr poller_; ModemInit modem_init_{}; }; diff --git a/aether/ae_actions/select_client.h b/aether/ae_actions/select_client.h index 9633df68..79efc796 100644 --- a/aether/ae_actions/select_client.h +++ b/aether/ae_actions/select_client.h @@ -17,13 +17,14 @@ #ifndef AETHER_AE_ACTIONS_SELECT_CLIENT_H_ #define AETHER_AE_ACTIONS_SELECT_CLIENT_H_ -#include "aether/config.h" -#include "aether/ae_context.h" -#include "aether/obj/obj_ptr.h" #include "aether-miscpp/types/result.h" -#include "aether/events/events.h" + #include "aether/actions/action.h" +#include "aether/ae_context.h" +#include "aether/config.h" #include "aether/events/event_subscription.h" +#include "aether/events/events.h" +#include "aether/obj/obj_ptr.h" namespace ae { class Aether; diff --git a/aether/aether.h b/aether/aether.h index 1aaaf89f..9fee1b5a 100644 --- a/aether/aether.h +++ b/aether/aether.h @@ -82,16 +82,16 @@ class Aether : public Obj { void StoreServer(ObjPtr s); ObjPtr GetServer(ServerId server_id); - Obj::ptr client_prefab; - Obj::ptr registration_cloud; + ObjPtr client_prefab; + ObjPtr registration_cloud; - Obj::ptr crypto; - Obj::ptr poller; - Obj::ptr dns_resolver; + ObjPtr crypto; + ObjPtr poller; + ObjPtr dns_resolver; - Obj::ptr adapter_registry; + ObjPtr adapter_registry; - Obj::ptr tele_statistics; + ObjPtr tele_statistics; std::unique_ptr task_scheduler; @@ -113,8 +113,8 @@ class Aether : public Obj { std::map> registrations_; #endif - std::map clients_; - std::map servers_; + std::map> clients_; + std::map> servers_; std::map> select_client_actions_; diff --git a/aether/channels/channels_types.h b/aether/channels/channels_types.h index fed6411a..b0f2b148 100644 --- a/aether/channels/channels_types.h +++ b/aether/channels/channels_types.h @@ -17,7 +17,6 @@ #ifndef AETHER_CHANNELS_CHANNELS_TYPES_H_ #define AETHER_CHANNELS_CHANNELS_TYPES_H_ -#include #include #include "aether-miscpp/reflect/reflect.h" diff --git a/aether/channels/ethernet_channel.cpp b/aether/channels/ethernet_channel.cpp index 17d93980..f5dd2460 100644 --- a/aether/channels/ethernet_channel.cpp +++ b/aether/channels/ethernet_channel.cpp @@ -36,11 +36,10 @@ namespace ethernet_access_point_internal { using ResolveSender = ex::AnySender), ex::set_error_t(int)>; -ResolveSender ResolveAddress( - [[maybe_unused]] Ptr const& resolver, - [[maybe_unused]] NamedAddr const& addr, - [[maybe_unused]] std::uint16_t port, - [[maybe_unused]] Protocol protocol) { +ResolveSender ResolveAddress([[maybe_unused]] Ptr const& resolver, + [[maybe_unused]] NamedAddr const& addr, + [[maybe_unused]] std::uint16_t port, + [[maybe_unused]] Protocol protocol) { #if AE_SUPPORT_CLOUD_DNS return resolver->Resolve(addr, port, protocol); #else @@ -172,7 +171,7 @@ TransportBuildSender EthernetChannel::TransportBuilder() { AE_TELED_DEBUG("Make transport builder for {}", address); return ethernet_access_point_internal::MakeTransportBuilder( - AeContext{*aether_.Load().as()}, resolver, poller, address); + AeContext{*aether_}, resolver, poller, address); } } // namespace ae diff --git a/aether/channels/ethernet_channel.h b/aether/channels/ethernet_channel.h index 36e8f6f1..e2e02d85 100644 --- a/aether/channels/ethernet_channel.h +++ b/aether/channels/ethernet_channel.h @@ -17,9 +17,8 @@ #ifndef AETHER_CHANNELS_ETHERNET_CHANNEL_H_ #define AETHER_CHANNELS_ETHERNET_CHANNEL_H_ -#include "aether/memory.h" -#include "aether/types/address.h" #include "aether/channels/channel.h" +#include "aether/types/address.h" namespace ae { class Aether; diff --git a/aether/channels/lora_module_channel.cpp b/aether/channels/lora_module_channel.cpp index 3b96ba6d..813dd68d 100644 --- a/aether/channels/lora_module_channel.cpp +++ b/aether/channels/lora_module_channel.cpp @@ -19,10 +19,10 @@ # include -# include "aether/memory.h" # include "aether/aether.h" -# include "aether/types/state_machine.h" +# include "aether/memory.h" # include "aether/transport/lora_modules/lora_module_transport.h" +# include "aether/types/state_machine.h" namespace ae { namespace lora_module_channel_internal { @@ -169,7 +169,7 @@ ActionPtr LoraModuleChannel::TransportBuilder() { } return ActionPtr< lora_module_channel_internal::LoraModuleTransportBuilderAction>{ - *aether_.as(), *this, *access_point_, address}; + *aether_, *this, *access_point_, address}; } Duration LoraModuleChannel::TransportBuildTimeout() const { diff --git a/aether/channels/lora_module_channel.h b/aether/channels/lora_module_channel.h index b8812007..e8be61b7 100644 --- a/aether/channels/lora_module_channel.h +++ b/aether/channels/lora_module_channel.h @@ -20,8 +20,8 @@ #include "aether/config.h" #if AE_SUPPORT_LORA -# include "aether/channels/channel.h" # include "aether/access_points/lora_module_access_point.h" +# include "aether/channels/channel.h" namespace ae { class Aether; @@ -42,7 +42,7 @@ class LoraModuleChannel final : public Channel { Duration TransportBuildTimeout() const override; private: - Obj::ptr aether_; + ObjPtr aether_; LoraModuleAccessPoint::ptr access_point_; }; } // namespace ae diff --git a/aether/channels/modem_channel.cpp b/aether/channels/modem_channel.cpp index 418db58a..be1b67fc 100644 --- a/aether/channels/modem_channel.cpp +++ b/aether/channels/modem_channel.cpp @@ -19,10 +19,10 @@ # include -# include "aether/config.h" -# include "aether/memory.h" # include "aether/aether.h" +# include "aether/config.h" # include "aether/executors/executors.h" +# include "aether/memory.h" # include "aether/transport/modems/modem_transport.h" namespace ae { @@ -112,8 +112,8 @@ ModemChannel::ModemChannel(ObjProp prop, ObjPtr aether, TransportBuildSender ModemChannel::TransportBuilder() { auto ap = access_point_.Load(); assert(ap && "Access point is not loaded"); - return modem_channel_internal::MakeTransportBuilderSender( - *aether_.Load().as(), *ap, address); + return modem_channel_internal::MakeTransportBuilderSender(*aether_, *ap, + address); } Duration ModemChannel::TransportBuildTimeout() const { diff --git a/aether/channels/modem_channel.h b/aether/channels/modem_channel.h index b1fd1be9..343e6953 100644 --- a/aether/channels/modem_channel.h +++ b/aether/channels/modem_channel.h @@ -20,9 +20,9 @@ #include "aether/config.h" #if AE_SUPPORT_MODEMS -# include "aether/types/address.h" -# include "aether/channels/channel.h" # include "aether/access_points/modem_access_point.h" +# include "aether/channels/channel.h" +# include "aether/types/address.h" namespace ae { class Aether; @@ -44,7 +44,7 @@ class ModemChannel final : public Channel { Endpoint address; private: - Obj::ptr aether_; + ObjPtr aether_; ModemAccessPoint::ptr access_point_; }; } // namespace ae diff --git a/aether/channels/wifi_channel.cpp b/aether/channels/wifi_channel.cpp index 5b23bde3..7eb095a7 100644 --- a/aether/channels/wifi_channel.cpp +++ b/aether/channels/wifi_channel.cpp @@ -208,8 +208,8 @@ TransportBuildSender WifiChannel::TransportBuilder() { AE_TELED_DEBUG("Make transport builder for {}", address); - return wifi_channel_internal::MakeTransportBuilder( - *aether_.Load().as(), resolver, poller, access_point, address); + return wifi_channel_internal::MakeTransportBuilder(*aether_, resolver, poller, + access_point, address); } } // namespace ae diff --git a/aether/channels/wifi_channel.h b/aether/channels/wifi_channel.h index 40b57158..ac732a73 100644 --- a/aether/channels/wifi_channel.h +++ b/aether/channels/wifi_channel.h @@ -21,9 +21,9 @@ #if AE_SUPPORT_WIFIS -# include "aether/types/address.h" -# include "aether/channels/channel.h" # include "aether/access_points/wifi_access_point.h" +# include "aether/channels/channel.h" +# include "aether/types/address.h" namespace ae { class Aether; diff --git a/aether/client.cpp b/aether/client.cpp index fcd116f7..a23012d6 100644 --- a/aether/client.cpp +++ b/aether/client.cpp @@ -66,20 +66,18 @@ ServerConnectionManager& Client::server_connection_manager() { CloudServerConnections& Client::cloud_connection() { if (!cloud_connection_) { cloud_connection_ = std::make_unique( - *aether_.Load().as(), cloud_.Load(), + *aether_, cloud_.Load(), server_connection_manager().GetServerConnectionFactory(), AE_CLOUD_MAX_SERVER_CONNECTIONS); #if AE_ENABLE_PING ping_cloud_servers_ = std::make_unique( - *aether_.Load().as(), *cloud_connection_, - *connectivity_policy().Load()); + *aether_, *cloud_connection_, *connectivity_policy().Load()); #endif #if TELEMETRY_ENABLED // also create telemetry - telemetry_ = std::make_unique(*aether_.Load().as(), - *cloud_connection_); + telemetry_ = std::make_unique(*aether_, *cloud_connection_); #endif } @@ -94,7 +92,7 @@ ClientConnectivityPolicy::ptr const& Client::connectivity_policy() { P2pMessageStreamManager& Client::message_stream_manager() { if (!message_stream_manager_) { message_stream_manager_ = std::make_unique( - *aether_.Load().as(), MakePtrFromThis(this)); + *aether_, MakePtrFromThis(this)); } return *message_stream_manager_; } diff --git a/aether/client.h b/aether/client.h index 998259c1..fe8206c5 100644 --- a/aether/client.h +++ b/aether/client.h @@ -74,7 +74,7 @@ class Client : public Obj { void SendTelemetry(); private: - Obj::ptr aether_; + ObjPtr aether_; // configuration std::string client_id_; // User-defined client id Uid parent_uid_; // Parent aethernet client uid diff --git a/aether/connection_manager/client_cloud_manager.cpp b/aether/connection_manager/client_cloud_manager.cpp index 5c1cd5ae..e5dd3303 100644 --- a/aether/connection_manager/client_cloud_manager.cpp +++ b/aether/connection_manager/client_cloud_manager.cpp @@ -153,14 +153,13 @@ ClientCloudManager::ClientCloudManager(ObjProp prop, ObjPtr aether, // save cloud cache for current client [[maybe_unused]] auto const cache_initialized = client_.WithLoaded([&](auto const& obj) { - auto* c = obj.template as(); - cloud_cache_.emplace(c->uid(), - client_cloud_manager_internal::CloudCache{ - .version_confirmed = true, - .subject_uid = c->uid(), - .version = 0, - .cloud = c->cloud(), - }); + cloud_cache_.emplace(obj->uid(), + client_cloud_manager_internal::CloudCache{ + .version_confirmed = true, + .subject_uid = obj->uid(), + .version = 0, + .cloud = obj->cloud(), + }); }); assert(cache_initialized && "Client did not load"); diff --git a/aether/connection_manager/client_cloud_manager.h b/aether/connection_manager/client_cloud_manager.h index 571fd182..1364e90f 100644 --- a/aether/connection_manager/client_cloud_manager.h +++ b/aether/connection_manager/client_cloud_manager.h @@ -108,8 +108,8 @@ class ClientCloudManager : public Obj { GetCloudActionPool& get_cloud_action_pool(); - Obj::ptr aether_; - Obj::ptr client_; + ObjPtr aether_; + ObjPtr client_; std::map cloud_cache_; CloudUpdateEvent cloud_update_event_; diff --git a/aether/dns/dns_c_ares.cpp b/aether/dns/dns_c_ares.cpp index 41820cbd..aa2d6fc7 100644 --- a/aether/dns/dns_c_ares.cpp +++ b/aether/dns/dns_c_ares.cpp @@ -19,8 +19,8 @@ #if defined DNS_RESOLVE_ARES_ENABLED # include -# include # include +# include # include "ares.h" @@ -30,8 +30,8 @@ # include "aether/warning_disable.h" # include "aether/aether.h" -# include "aether/socket_initializer.h" # include "aether/events/multi_subscription.h" +# include "aether/socket_initializer.h" # include "aether/executors/executors.h" @@ -214,8 +214,7 @@ ResolveSender DnsResolverCares::Resolve(NamedAddr const& name_address, } return ares_impl_->Query(name_address, port_hint, protocol_hint) | - ex::continues_on( - ex::SchedulerOnTasks{AeContext{*aether_.Load().as()}}); + ex::continues_on(ex::SchedulerOnTasks{AeContext{*aether_.Load()}}); } } // namespace ae diff --git a/aether/dns/dns_c_ares.h b/aether/dns/dns_c_ares.h index b738bfe2..2eeeaa25 100644 --- a/aether/dns/dns_c_ares.h +++ b/aether/dns/dns_c_ares.h @@ -51,7 +51,7 @@ class DnsResolverCares : public DnsResolver { Protocol protocol_hint) override; private: - Obj::ptr aether_; + ObjPtr aether_; std::unique_ptr ares_impl_; }; } // namespace ae diff --git a/aether/dns/esp32_dns_resolve.cpp b/aether/dns/esp32_dns_resolve.cpp index 9382b70e..4131568f 100644 --- a/aether/dns/esp32_dns_resolve.cpp +++ b/aether/dns/esp32_dns_resolve.cpp @@ -25,8 +25,8 @@ // # include "freertos/task.h" // # include "freertos/event_groups.h" -# include "lwip/err.h" # include "lwip/dns.h" +# include "lwip/err.h" # include "lwip/tcpip.h" # include "aether/aether.h" @@ -156,7 +156,7 @@ ResolveSender Esp32DnsResolver::Resolve(NamedAddr const& name_address, std::uint16_t port_hint, Protocol protocol_hint) { return GHbyNameResolveSender{name_address, port_hint, protocol_hint} | - ex::continues_on(ex::SchedulerOnTasks{AeContext{*aether_.Load().as()}}); + ex::continues_on(ex::SchedulerOnTasks{AeContext{*aether_.Load()}}); } } // namespace ae diff --git a/aether/dns/esp32_dns_resolve.h b/aether/dns/esp32_dns_resolve.h index db60c800..a0f32726 100644 --- a/aether/dns/esp32_dns_resolve.h +++ b/aether/dns/esp32_dns_resolve.h @@ -24,8 +24,8 @@ # if (defined(ESP_PLATFORM)) # define ESP32_DNS_RESOLVER_ENABLED 1 -# include "aether/obj/obj.h" # include "aether/dns/dns_resolve.h" +# include "aether/obj/obj.h" namespace ae { class Aether; @@ -45,7 +45,7 @@ class Esp32DnsResolver : public DnsResolver { Protocol protocol_hint) override; private: - Obj::ptr aether_; + ObjPtr aether_; }; } // namespace ae diff --git a/aether/registration_cloud.cpp b/aether/registration_cloud.cpp index c9455423..5c539bd0 100644 --- a/aether/registration_cloud.cpp +++ b/aether/registration_cloud.cpp @@ -19,8 +19,8 @@ #if AE_SUPPORT_REGISTRATION # include -# include "aether/server.h" # include "aether/aether.h" +# include "aether/server.h" namespace ae { @@ -31,10 +31,13 @@ RegistrationCloud::RegistrationCloud(ObjProp prop, ObjPtr aether) void RegistrationCloud::AddServerSettings(Endpoint address) { // don't care about server id for registration - auto server = - Server::ptr::Create(domain, ServerId{0}, std::vector{std::move(address)}, - aether_.Load().as()->adapter_registry); - AddServer(server); + auto server = aether_.WithLoaded([&](auto const& aether) { + return Server::ptr::Create(domain, ServerId{0}, + std::vector{std::move(address)}, + aether->adapter_registry); + }); + assert(server.has_value() && "Server must be created"); + AddServer(server.value()); } } // namespace ae diff --git a/aether/registration_cloud.h b/aether/registration_cloud.h index fcba15e6..4802820b 100644 --- a/aether/registration_cloud.h +++ b/aether/registration_cloud.h @@ -42,7 +42,7 @@ class RegistrationCloud : public Cloud { void AddServerSettings(Endpoint address); private: - Obj::ptr aether_; + ObjPtr aether_; }; } // namespace ae #else diff --git a/tests/test-object-system/CMakeLists.txt b/tests/test-object-system/CMakeLists.txt index f1d24cc3..3ffffd85 100644 --- a/tests/test-object-system/CMakeLists.txt +++ b/tests/test-object-system/CMakeLists.txt @@ -28,6 +28,7 @@ list(APPEND test_obj_srcs list(APPEND test_srcs main.cpp test-obj-create.cpp + test-obj-ptr-forward-decl.cpp test-update-objects.cpp test-version-iterator.cpp map_domain_storage.cpp diff --git a/tests/test-object-system/test-obj-create.cpp b/tests/test-object-system/test-obj-create.cpp index 9c23277e..21aecde9 100644 --- a/tests/test-object-system/test-obj-create.cpp +++ b/tests/test-object-system/test-obj-create.cpp @@ -16,6 +16,9 @@ #include +#include +#include + #include "aether/obj/domain.h" #include "aether/obj/obj_ptr.h" #include "aether/obj/registry.h" @@ -30,6 +33,222 @@ namespace ae::test_obj_create { +namespace test_obj_create_internal { + +void AssertEmptyPtr(Foo::ptr const& ptr) { + TEST_ASSERT_FALSE(ptr.is_valid()); + TEST_ASSERT_FALSE(ptr.is_loaded()); + TEST_ASSERT_FALSE(ptr.Load()); +} + +void AssertCopiedPtr(Foo::ptr const& foo, Foo::ptr const& copy, + Foo::ptr const& assigned) { + TEST_ASSERT(copy.is_loaded()); + TEST_ASSERT(copy.Load().get() == foo.Load().get()); + TEST_ASSERT(assigned.Load().get() == foo.Load().get()); +} + +void AssertMovedPtr(Foo::ptr const& foo, Foo::ptr const& moved) { + TEST_ASSERT(moved.Load().get() == foo.Load().get()); +} + +void AssertMovedFromPtr(Foo::ptr const& foo, Foo::ptr const& ptr) { + TEST_ASSERT_FALSE(ptr.is_loaded()); + TEST_ASSERT(ptr.is_valid()); + TEST_ASSERT_EQUAL(foo.id().id(), ptr.id().id()); + TEST_ASSERT_EQUAL_PTR(foo.domain(), ptr.domain()); + TEST_ASSERT_EQUAL(static_cast(foo.flags()), + static_cast(ptr.flags())); +} + +void TestEmptyAndUnloadedPtrOwnership(Domain& domain) { + Foo::ptr const empty; + AssertEmptyPtr(empty); + auto empty_copy = empty; + Foo::ptr empty_assigned; + empty_assigned = empty_copy; + auto empty_moved = std::move(empty_copy); + empty_assigned = std::move(empty_moved); + AssertEmptyPtr(empty_assigned); + + constexpr auto kUnloadedObjectId = 10; + auto unloaded = + Foo::ptr::Declare(CreateWith{domain}.with_id(kUnloadedObjectId)); + auto unloaded_copy = unloaded; + Foo::ptr unloaded_assigned; + unloaded_assigned = unloaded_copy; + auto unloaded_moved = std::move(unloaded_copy); + unloaded_assigned = std::move(unloaded_moved); + TEST_ASSERT(unloaded_assigned.is_valid()); + TEST_ASSERT_FALSE(unloaded_assigned.is_loaded()); +} + +Foo::ptr CreateCachedFoo(Domain& domain) { + return Foo::ptr::Create(CreateWith{domain}.with_id(1)); +} + +void AssertCachedBaseView(Foo::ptr const& foo) { + auto const& base = static_cast(foo); + TEST_ASSERT(base.is_valid()); + TEST_ASSERT(base.is_loaded()); + TEST_ASSERT(base.LoadCached().get() == foo.Load().get()); +} + +void AssertCachedCopyAndMoveOwnership(Foo::ptr const& foo, Foo::ptr& moved) { + auto copy = foo; + + Foo::ptr assigned; + assigned = copy; + AssertCopiedPtr(foo, copy, assigned); + + moved = std::move(copy); + AssertMovedPtr(foo, moved); + // ObjectPtr preserves identity while releasing its cached object after move. + // NOLINTNEXTLINE(*use-after-move) -- Verify the moved-from contract. + AssertMovedFromPtr(foo, copy); + + moved = std::move(moved); + AssertMovedPtr(foo, moved); +} + +void AssertCachedMoveAssignmentOwnership(Domain& domain, Foo::ptr& foo, + Foo::ptr& moved) { + auto replacement = Foo::ptr::Create(CreateWith{domain}.with_id(2)); + Ptr const retained_replacement = replacement.Load(); + replacement = std::move(moved); + TEST_ASSERT(replacement.Load().get() == foo.Load().get()); + TEST_ASSERT(retained_replacement); + // Move assignment transfers the source cache, never the destination cache. + // NOLINTNEXTLINE(*use-after-move) -- Verify the moved-from contract. + AssertMovedFromPtr(foo, moved); + // NOLINTNEXTLINE(*use-after-move) -- Verify the moved-from contract. + TEST_ASSERT(moved.Load().get() == foo.Load().get()); + + Ptr const retained = replacement.Load(); + auto& base = static_cast(foo); + base.Reset(); + TEST_ASSERT_FALSE(foo.is_loaded()); + replacement.Reset(); + TEST_ASSERT(retained); +} + +void TestCachedPtrOwnership(Domain& domain) { + auto foo = CreateCachedFoo(domain); + AssertCachedBaseView(foo); + auto moved = Foo::ptr{}; + AssertCachedCopyAndMoveOwnership(foo, moved); + AssertCachedMoveAssignmentOwnership(domain, foo, moved); +} + +void TestEmptyAndUnloadedTypedViewOwnership(Domain& domain) { + Child::ptr empty_child; + Father::ptr const empty_father = std::move(empty_child); + TEST_ASSERT_FALSE(empty_father.is_valid()); + TEST_ASSERT_FALSE(empty_father.is_loaded()); + + Child::ptr unloaded_child = + Child::ptr::Declare(CreateWith{domain}.with_id(2)); + Father::ptr const unloaded_father = std::move(unloaded_child); + TEST_ASSERT(unloaded_father.is_valid()); + TEST_ASSERT_FALSE(unloaded_father.is_loaded()); +} + +void AssertLoadedTypedViewMoveOwnership(Domain& domain) { + Child::ptr child = Child::ptr::Create(CreateWith{domain}.with_id(1)); + Ptr const retained_child = child.Load(); + Father::ptr father = std::move(child); + Ptr const retained_father = father.Load(); + + // NOLINTNEXTLINE(*use-after-move) -- Verify the moved-from contract. + TEST_ASSERT(child.is_valid()); + // NOLINTNEXTLINE(*use-after-move) -- Verify the moved-from contract. + TEST_ASSERT_FALSE(child.is_loaded()); + TEST_ASSERT(retained_father); + TEST_ASSERT(retained_father.get() == retained_child.get()); + father.Reset(); + TEST_ASSERT(retained_child); + TEST_ASSERT(retained_father); +} + +void AssertLoadedTypedViewMoveAssignment(Domain& domain) { + auto assigned_child = Child::ptr::Create(CreateWith{domain}.with_id(3)); + Father::ptr assigned_father; + assigned_father = std::move(assigned_child); + TEST_ASSERT(assigned_father.is_loaded()); + // NOLINTNEXTLINE(*use-after-move) -- Verify the moved-from contract. + TEST_ASSERT_FALSE(assigned_child.is_loaded()); +} + +void TestLoadedTypedViewOwnership(Domain& domain) { + AssertLoadedTypedViewMoveOwnership(domain); + AssertLoadedTypedViewMoveAssignment(domain); +} + +void TestProxyPtrTypedUpcast(Domain& domain) { + static_assert(std::is_convertible_v, Ptr>); + static_assert(!std::is_convertible_v, Ptr>); + + auto child = Child::ptr::Create(CreateWith{domain}.with_id(4)); + ProxyPtr const child_proxy = child.Load(); + Ptr const father = child_proxy; + TEST_ASSERT(father); + TEST_ASSERT(father.get() == child_proxy.get()); +} + +template +void SetFooA(O&& obj_ptr, int value) { + std::forward(obj_ptr)->a = value; +} + +void TestProxyPtr(Domain& domain) { + static_assert(std::is_same_v().Load()), + ProxyPtr>); + static_assert(std::is_same_v().Load()), + ProxyPtr>); + static_assert( + std::is_same_v().operator->()), + Foo*>); + static_assert( + std::is_same_v().Load()), Ptr>); + + auto foo = CreateCachedFoo(domain); + auto proxy = foo.Load(); + TEST_ASSERT_EQUAL_PTR(foo.cached().get(), proxy.get()); + + SetFooA(foo, 3); + SetFooA(std::move(foo), 4); + TEST_ASSERT_EQUAL(4, proxy->a); + + auto generic_result = + // Verify rvalue access preserves cache. + // NOLINTNEXTLINE(*use-after-move*) + foo.WithLoaded([](auto const& loaded) { return loaded->a; }); + TEST_ASSERT(generic_result.has_value()); + TEST_ASSERT_EQUAL(4, *generic_result); + + auto explicit_result = + foo.WithLoaded([](Ptr const& loaded) { return loaded->a; }); + TEST_ASSERT(explicit_result.has_value()); + TEST_ASSERT_EQUAL(4, *explicit_result); + Foo::ptr const& const_foo = foo; + TEST_ASSERT( + const_foo.WithLoaded([](ProxyPtr loaded) { loaded->a = 5; })); + + Ptr const owned = foo.Load(); + foo.Reset(); + TEST_ASSERT(owned); + TEST_ASSERT_EQUAL(5, owned->a); + + Foo::ptr empty; + TEST_ASSERT_FALSE(empty.Load()); + Ptr const empty_owned = std::move(empty).Load(); + TEST_ASSERT_FALSE(empty_owned); + + TestProxyPtrTypedUpcast(domain); +} + +} // namespace test_obj_create_internal + void test_createFoo() { // create objects auto facility = MapDomainStorage{}; @@ -71,6 +290,23 @@ void test_createFoo() { } } +void test_ObjPtrCachedOwnership() { + auto facility = MapDomainStorage{}; + Domain domain{ae::Now(), facility}; + + test_obj_create_internal::TestEmptyAndUnloadedPtrOwnership(domain); + test_obj_create_internal::TestCachedPtrOwnership(domain); + test_obj_create_internal::TestProxyPtr(domain); +} + +void test_ObjPtrTypedViewOwnership() { + auto facility = MapDomainStorage{}; + Domain domain{ae::Now(), facility}; + + test_obj_create_internal::TestEmptyAndUnloadedTypedViewOwnership(domain); + test_obj_create_internal::TestLoadedTypedViewOwnership(domain); +} + void test_createBob() { auto facility = MapDomainStorage{}; Domain domain{ae::Now(), facility}; @@ -94,14 +330,14 @@ void test_createBob() { TEST_ASSERT(foo2->bar); // it's different copies TEST_ASSERT(foo2.id() != foo.id()); - TEST_ASSERT(foo2.Load() != foo.Load()); + TEST_ASSERT(foo2.Load().get() != foo.Load().get()); // but internal the same - TEST_ASSERT(foo2->bar.Load() == foo->bar.Load()); + TEST_ASSERT(foo2->bar.Load().get() == foo->bar.Load().get()); // foo is registered and same id loads same object Foo::ptr foo3 = Foo::ptr::Declare(CreateWith{domain}.with_id(foo.id())); foo3.Load(); TEST_ASSERT(foo3); - TEST_ASSERT(foo3.Load() == foo.Load()); + TEST_ASSERT(foo3.Load().get() == foo.Load().get()); } void test_cloneFoo() { @@ -243,9 +479,9 @@ void test_cyclePoopaLoopa() { TEST_ASSERT(poopa); TEST_ASSERT(poopa->loopa); - TEST_ASSERT(poopa->loopa.Load() == loopa.Load()); + TEST_ASSERT(poopa->loopa.Load().get() == loopa.Load().get()); for (auto& p : loopa->poopas) { - TEST_ASSERT(poopa.Load() == p.Load()); + TEST_ASSERT(poopa.Load().get() == p.Load().get()); } } @@ -279,8 +515,8 @@ void test_cyclePoopaLoopaReverse() { for (auto& p : loopa->poopas) { TEST_ASSERT(p); - auto poopa = static_cast>(p); - TEST_ASSERT(poopa->loopa.Load() == loopa.Load()); + auto poopa = Poopa::ptr{p}; + TEST_ASSERT(poopa->loopa.Load().get() == loopa.Load().get()); } } @@ -312,6 +548,8 @@ void test_Family() { int run_test_object_create() { UNITY_BEGIN(); RUN_TEST(ae::test_obj_create::test_createFoo); + RUN_TEST(ae::test_obj_create::test_ObjPtrCachedOwnership); + RUN_TEST(ae::test_obj_create::test_ObjPtrTypedViewOwnership); RUN_TEST(ae::test_obj_create::test_createBob); RUN_TEST(ae::test_obj_create::test_cloneFoo); RUN_TEST(ae::test_obj_create::test_createBobsMother); diff --git a/tests/test-object-system/test-obj-ptr-forward-decl.cpp b/tests/test-object-system/test-obj-ptr-forward-decl.cpp new file mode 100644 index 00000000..525f12ab --- /dev/null +++ b/tests/test-object-system/test-obj-ptr-forward-decl.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2026 Aethernet Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "aether/obj/obj_ptr.h" + +namespace ae { +class ForwardDeclaredObj; + +void test_ObjPtrForwardDeclarationCompile() { + ObjPtr empty; + auto copy = empty; + auto moved = std::move(copy); + empty = std::move(moved); +} +} // namespace ae