From f4708e78442f4185585d18954fed9659f5bc4e3b Mon Sep 17 00:00:00 2001 From: Rainer Kartmann <rainer.kartmann@kit.edu> Date: Wed, 11 Aug 2021 10:45:46 +0200 Subject: [PATCH] Revise get/find* interface of memory base classes. Remove EntityContainerBase --- .../RobotAPI/libraries/armem/CMakeLists.txt | 4 +- .../armem/core/base/CoreSegmentBase.h | 195 ++++++++----- .../libraries/armem/core/base/EntityBase.h | 129 +++++---- .../armem/core/base/EntityInstanceBase.h | 7 +- .../armem/core/base/EntitySnapshotBase.h | 91 ++++-- .../libraries/armem/core/base/MemoryBase.h | 149 ++++------ .../armem/core/base/ProviderSegmentBase.h | 106 ++++--- .../core/base/detail/EntityContainerBase.cpp | 1 - .../core/base/detail/EntityContainerBase.h | 124 --------- .../armem/core/base/detail/lookup_mixins.cpp | 63 +++++ .../armem/core/base/detail/lookup_mixins.h | 260 ++++++++++++++++++ 11 files changed, 726 insertions(+), 403 deletions(-) delete mode 100644 source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp delete mode 100644 source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h create mode 100644 source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp create mode 100644 source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h diff --git a/source/RobotAPI/libraries/armem/CMakeLists.txt b/source/RobotAPI/libraries/armem/CMakeLists.txt index b08141a6e..81a6ad2cf 100644 --- a/source/RobotAPI/libraries/armem/CMakeLists.txt +++ b/source/RobotAPI/libraries/armem/CMakeLists.txt @@ -34,9 +34,9 @@ set(LIB_FILES core/base/detail/MemoryItem.cpp core/base/detail/MemoryContainerBase.cpp - core/base/detail/EntityContainerBase.cpp core/base/detail/AronTyped.cpp core/base/detail/iteration_mixins.cpp + core/base/detail/lookup_mixins.cpp core/base/detail/negative_index_semantics.cpp # core/base/CoreSegmentBase.cpp @@ -140,9 +140,9 @@ set(LIB_HEADERS core/base/detail/MemoryItem.h core/base/detail/MemoryContainerBase.h - core/base/detail/EntityContainerBase.h core/base/detail/AronTyped.h core/base/detail/iteration_mixins.h + core/base/detail/lookup_mixins.h core/base/detail/negative_index_semantics.h core/base/CoreSegmentBase.h diff --git a/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h b/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h index 0de79cccd..06833c954 100644 --- a/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h +++ b/source/RobotAPI/libraries/armem/core/base/CoreSegmentBase.h @@ -5,8 +5,9 @@ #include "ProviderSegmentBase.h" #include "detail/AronTyped.h" -#include "detail/EntityContainerBase.h" +#include "detail/MemoryContainerBase.h" #include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base @@ -17,13 +18,16 @@ namespace armarx::armem::base */ template <class _ProviderSegmentT, class _Derived> class CoreSegmentBase : - public detail::EntityContainerBase<_ProviderSegmentT, typename _ProviderSegmentT::EntityT, _Derived>, - public detail::AronTyped, - public detail::ForEachEntityInstanceMixin<_Derived>, - public detail::ForEachEntitySnapshotMixin<_Derived>, - public detail::ForEachEntityMixin<_Derived> + public detail::MemoryContainerBase<std::map<std::string, _ProviderSegmentT>, _Derived> + , public detail::AronTyped + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::ForEachEntitySnapshotMixin<_Derived> + , public detail::ForEachEntityMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> + , public detail::GetFindSnapshotMixin<_Derived> + , public detail::GetFindEntityMixin<_Derived> { - using Base = detail::EntityContainerBase<_ProviderSegmentT, typename _ProviderSegmentT::EntityT, _Derived>; + using Base = detail::MemoryContainerBase<std::map<std::string, _ProviderSegmentT>, _Derived>; public: @@ -35,6 +39,9 @@ namespace armarx::armem::base using EntitySnapshotT = typename EntityT::EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = ProviderSegmentT; + + struct UpdateResult { armarx::armem::UpdateType coreSegmentUpdateType; @@ -80,69 +87,90 @@ namespace armarx::armem::base // READ ACCESS - inline const std::string& name() const + // Get key + inline std::string& name() { return this->id().coreSegmentName; } - inline std::string& name() + inline const std::string& name() const { - return const_cast<std::string&>(const_cast<const CoreSegmentBase*>(this)->name()); + return this->id().coreSegmentName; } + // Has child by key bool hasProviderSegment(const std::string& name) const { - return this->_container.count(name) > 0; + return this->findProviderSegment(name) != nullptr; + } + // Has child by memory ID + bool hasProviderSegment(const MemoryID& providerSegmentID) const + { + return this->findProviderSegment(providerSegmentID) != nullptr; } - std::vector<std::string> getProviderSegmentNames() const + + // Find child by key + ProviderSegmentT* findProviderSegment(const std::string& name) { - return simox::alg::get_keys(this->_container); + return detail::findChildByKey(name, this->_container); + } + const ProviderSegmentT* findProviderSegment(const std::string& name) const + { + return detail::findChildByKey(name, this->_container); } + // Get child by key ProviderSegmentT& getProviderSegment(const std::string& name) { - return const_cast<ProviderSegmentT&>(const_cast<const CoreSegmentBase*>(this)->getProviderSegment(name)); + return detail::getChildByKey(name, this->_container, *this); } - const ProviderSegmentT& getProviderSegment(const std::string& name) const { - auto it = this->_container.find(name); - if (it != this->_container.end()) - { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<ProviderSegmentT>(name, *this); - } + return detail::getChildByKey(name, this->_container, *this); } - using Base::getEntity; - const EntityT& getEntity(const MemoryID& id) const + // Find child by MemoryID + ProviderSegmentT* findProviderSegment(const MemoryID& providerSegmentID) { - this->_checkContainerName(id.coreSegmentName, this->getKeyString()); - return getProviderSegment(id.providerSegmentName).getEntity(id); + detail::checkHasProviderSegmentName(providerSegmentID); + return this->findProviderSegment(providerSegmentID.providerSegmentName); + } + const ProviderSegmentT* findProviderSegment(const MemoryID& providerSegmentID) const + { + detail::checkHasProviderSegmentName(providerSegmentID); + return this->findProviderSegment(providerSegmentID.providerSegmentName); } - const EntityT* findEntity(const MemoryID& id) const + // Get child by MemoryID + ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) { - this->_checkContainerName(id.coreSegmentName, this->getKeyString()); - if (id.hasProviderSegmentName()) - { - return getProviderSegment(id.providerSegmentName).findEntity(id); - } - else - { - for (const auto& [_, providerSegment] : this->_container) - { - if (auto entity = providerSegment.findEntity(id)) - { - return entity; - } - } - return nullptr; - } + detail::checkHasProviderSegmentName(providerSegmentID); + return this->getProviderSegment(providerSegmentID.providerSegmentName); + } + const ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) const + { + detail::checkHasProviderSegmentName(providerSegmentID); + return this->getProviderSegment(providerSegmentID.providerSegmentName); + } + + // get/findInstance are provided by GetFindInstanceMixin + // get/findSnapshot are provided by GetFindSnapshotMixin + // get/findEntity are provided by GetFindEntityMixin + + // Search all provider segments for the first matching entity. + + bool hasEntity(const std::string& entityName) const + { + return this->findEntity(entityName) != nullptr; + } + EntityT* findEntity(const std::string& entityName) + { + return _findEntity(*this, entityName); + } + const EntityT* findEntity(const std::string& entityName) const + { + return _findEntity(*this, entityName); } @@ -170,6 +198,13 @@ namespace armarx::armem::base // forEachInstance() is provided by ForEachEntityInstanceMixin. + // Get child keys + std::vector<std::string> getProviderSegmentNames() const + { + return simox::alg::get_keys(this->_container); + } + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] inline const auto& providerSegments() const { @@ -193,7 +228,7 @@ namespace armarx::armem::base { this->_checkContainerName(update.entityID.coreSegmentName, this->name()); - auto [inserted, provSeg] = addProviderSegmentIfMissing(update.entityID.providerSegmentName); + auto [inserted, provSeg] = _addProviderSegmentIfMissing(update.entityID.providerSegmentName); // Update entry. @@ -209,30 +244,6 @@ namespace armarx::armem::base return ret; } - std::pair<bool, ProviderSegmentT*> addProviderSegmentIfMissing(const std::string& providerSegmentName) - { - ProviderSegmentT* provSeg; - - auto it = this->_container.find(providerSegmentName); - if (it == this->_container.end()) - { - if (_addMissingProviderSegmentDuringUpdate) - { - // Insert into map. - provSeg = &addProviderSegment(providerSegmentName); - return {true, provSeg}; - } - else - { - throw error::MissingEntry::create<ProviderSegmentT>(providerSegmentName, *this); - } - } - else - { - provSeg = &it->second; - return {false, provSeg}; - } - } void append(const _Derived& m) { @@ -317,6 +328,50 @@ namespace armarx::armem::base } + protected: + + template <class ParentT> + static + auto* + _findEntity(ParentT&& parent, const std::string& entityName) + { + decltype(parent.findEntity(entityName)) result = nullptr; + parent.forEachProviderSegment([&result, &entityName](auto & provSeg) + { + result = provSeg.findEntity(entityName); + return result == nullptr; // Keep going if null, break if not null. + }); + return result; + } + + + std::pair<bool, ProviderSegmentT*> + _addProviderSegmentIfMissing(const std::string& providerSegmentName) + { + ProviderSegmentT* provSeg; + + auto it = this->_container.find(providerSegmentName); + if (it == this->_container.end()) + { + if (_addMissingProviderSegmentDuringUpdate) + { + // Insert into map. + provSeg = &addProviderSegment(providerSegmentName); + return {true, provSeg}; + } + else + { + throw error::MissingEntry::create<ProviderSegmentT>(providerSegmentName, *this); + } + } + else + { + provSeg = &it->second; + return {false, provSeg}; + } + } + + private: bool _addMissingProviderSegmentDuringUpdate = true; diff --git a/source/RobotAPI/libraries/armem/core/base/EntityBase.h b/source/RobotAPI/libraries/armem/core/base/EntityBase.h index 05cbf780d..6f23cde75 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntityBase.h @@ -11,6 +11,7 @@ #include "EntitySnapshotBase.h" #include "detail/MemoryContainerBase.h" #include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" #include "detail/negative_index_semantics.h" @@ -38,8 +39,9 @@ namespace armarx::armem::base */ template <class _EntitySnapshotT, class _Derived> class EntityBase : - public detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived>, - public detail::ForEachEntityInstanceMixin<_Derived> + public detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived> + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> { using Base = detail::MemoryContainerBase<std::map<Time, _EntitySnapshotT>, _Derived>; @@ -51,6 +53,8 @@ namespace armarx::armem::base using EntitySnapshotT = _EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = EntitySnapshotT; + struct UpdateResult { armarx::armem::UpdateType entityUpdateType; @@ -83,50 +87,43 @@ namespace armarx::armem::base // READING - inline const std::string& name() const + // Get key + inline std::string& name() { return this->id().entityName; } - inline std::string& name() + inline const std::string& name() const { - return const_cast<std::string&>(const_cast<const EntityBase*>(this)->name()); + return this->id().entityName; } - /** - * @brief Indicates whether a history entry for the given time exists. - */ - bool hasSnapshot(const Time& time) - { - return const_cast<const EntityBase*>(this)->hasSnapshot(time); - } + + // Has child by key + /// Indicates whether a history entry for the given time exists. bool hasSnapshot(const Time& time) const { - return this->_container.count(time) > 0; + return this->findSnapshot(time) != nullptr; } - - /** - * @brief Get the latest timestamp. - * @throw `armem::error::EntityHistoryEmpty` If the history is empty. - */ - Time getLatestTimestamp() + // Has child by MemoryID + bool hasSnapshot(const MemoryID& snapshotID) const { - return const_cast<const EntityBase*>(this)->getLatestTimestamp(); + return this->findSnapshot(snapshotID) != nullptr; } - Time getLatestTimestamp() const + + // Find child via key + EntitySnapshotT* + findSnapshot(const Time& timestamp) { - return getLatestItem().first; + return detail::findChildByKey(timestamp, this->_container); } - - /** - * @brief Get all timestamps in the history. - */ - std::vector<Time> getTimestamps() const + const EntitySnapshotT* + findSnapshot(const Time& timestamp) const { - return simox::alg::get_keys(this->_container); + return detail::findChildByKey(timestamp, this->_container); } - + // Get child via key /** * @brief Get a snapshot. * @param time The time. @@ -134,33 +131,64 @@ namespace armarx::armem::base * * @throws `armem::error::MissingEntry` If there is no such entry. */ - EntitySnapshotT& getSnapshot(const Time& time) + EntitySnapshotT& + getSnapshot(const Time& time) { - return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getSnapshot(time)); + return detail::getChildByKey(time, this->_container, *this, [](const Time & time) + { + return toDateTimeMilliSeconds(time); + }); } - - const EntitySnapshotT& getSnapshot(const Time& time) const + const EntitySnapshotT& + getSnapshot(const Time& time) const { - auto it = this->_container.find(time); - if (it != this->_container.end()) + return detail::getChildByKey(time, this->_container, *this, [](const Time & time) { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<EntitySnapshotT>(toDateTimeMilliSeconds(time), *this); - } + return toDateTimeMilliSeconds(time); + }); + } + + // Find child via MemoryID + EntitySnapshotT* + findSnapshot(const MemoryID& snapshotID) + { + detail::checkHasTimestamp(snapshotID); + return this->findSnapshot(snapshotID.timestamp); + } + const EntitySnapshotT* + findSnapshot(const MemoryID& snapshotID) const + { + detail::checkHasTimestamp(snapshotID); + return this->findSnapshot(snapshotID.timestamp); } - EntitySnapshotT& getSnapshot(const MemoryID& id) + // Get child via MemoryID + EntitySnapshotT& + getSnapshot(const MemoryID& snapshotID) { - return const_cast<EntitySnapshotT&>(const_cast<const EntityBase*>(this)->getSnapshot(id)); + detail::checkHasTimestamp(snapshotID); + return this->getSnapshot(snapshotID.timestamp); } + const EntitySnapshotT& + getSnapshot(const MemoryID& snapshotID) const + { + detail::checkHasTimestamp(snapshotID); + return this->getSnapshot(snapshotID.timestamp); + + } + + // get/findInstance are provided by GetFindInstanceMixin + - const EntitySnapshotT& getSnapshot(const MemoryID& id) const + // More getter/finder for snapshots + + /** + * @brief Get the latest timestamp. + * @throw `armem::error::EntityHistoryEmpty` If the history is empty. + */ + Time getLatestTimestamp() const { - this->_checkContainerName(id.entityName, this->name()); - return getSnapshot(id.timestamp); + return getLatestItem().first; } /** @@ -386,6 +414,14 @@ namespace armarx::armem::base } + // Get child keys + /// @brief Get all timestamps in the history. + std::vector<Time> getTimestamps() const + { + return simox::alg::get_keys(this->_container); + } + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] inline const ContainerT& history() const { @@ -541,7 +577,6 @@ namespace armarx::armem::base return *this->_container.begin(); } - }; } diff --git a/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h b/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h index fcb1869f7..8f7b8aafc 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntityInstanceBase.h @@ -72,6 +72,7 @@ namespace armarx::armem::base } + // Key inline int& index() { return id().instanceIndex; @@ -82,6 +83,8 @@ namespace armarx::armem::base } + // Data + EntityInstanceMetadata& metadata() { return _metadata; @@ -102,6 +105,8 @@ namespace armarx::armem::base } + // Misc + static std::string getLevelName() { return "entity instance"; @@ -113,7 +118,6 @@ namespace armarx::armem::base } - protected: /// The metadata. @@ -122,7 +126,6 @@ namespace armarx::armem::base /// The data. May be nullptr. DataT _data; - }; } diff --git a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h index 765313644..0d0f091f5 100644 --- a/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h +++ b/source/RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h @@ -8,6 +8,7 @@ #include "EntityInstanceBase.h" #include "detail/MemoryContainerBase.h" #include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base::detail @@ -55,37 +56,60 @@ namespace armarx::armem::base // READING + // Get key inline Time& time() { return this->id().timestamp; } - inline const Time& time() const { return this->id().timestamp; } + + // Has child by key bool hasInstance(int index) const { - size_t si = size_t(index); - return index >= 0 && si < this->_container.size(); + return this->findInstance(index) != nullptr; + } + // Has child by ID + bool hasInstance(const MemoryID& instanceID) const + { + return this->findInstance(instanceID) != nullptr; + } + + + // Find child by key + EntityInstanceT* findInstance(int index) + { + return const_cast<EntityInstanceT*>(const_cast<const EntitySnapshotBase*>(this)->findInstance(index)); + } + const EntityInstanceT* findInstance(int index) const + { + const size_t si = static_cast<size_t>(index); + return (index >= 0 && si < this->_container.size()) + ? &this->_container[si] + : nullptr; } + // Get child by key /** * @brief Get the given instance. * @param index The instance's index. * @return The instance. * @throw `armem::error::MissingEntry` If the given index is invalid. */ - EntityInstanceT& getInstance(int index) + EntityInstanceT& + getInstance(int index) { return const_cast<EntityInstanceT&>(const_cast<const EntitySnapshotBase*>(this)->getInstance(index)); } - const EntityInstanceT& getInstance(int index) const + const EntityInstanceT& + getInstance(int index) const { - if (hasInstance(index)) + if (const EntityInstanceT* instance = findInstance(index)) { - return this->_container[static_cast<size_t>(index)]; + return *instance; } else { @@ -93,23 +117,21 @@ namespace armarx::armem::base } } - EntityInstanceT* findInstance(int index) + // Find child by MemoryID + EntityInstanceT* + findInstance(const MemoryID& instanceID) { - return const_cast<EntityInstanceT*>(const_cast<const EntitySnapshotBase*>(this)->findInstance(index)); + detail::checkHasInstanceIndex(instanceID); + return this->findInstance(instanceID.instanceIndex); } - const EntityInstanceT* findInstance(int index) const + const EntityInstanceT* + findInstance(const MemoryID& instanceID) const { - if (hasInstance(index)) - { - return &this->_container[static_cast<size_t>(index)]; - } - else - { - return nullptr; - } + detail::checkHasInstanceIndex(instanceID); + return this->findInstance(instanceID.instanceIndex); } - + // Get child by MemoryID /** * @brief Get the given instance. * @param index The instance's index. @@ -117,18 +139,17 @@ namespace armarx::armem::base * @throw `armem::error::MissingEntry` If the given index is invalid. * @throw `armem::error::InvalidMemoryID` If memory ID does not have an instance index. */ - EntityInstanceT& getInstance(const MemoryID& id) + EntityInstanceT& + getInstance(const MemoryID& instanceID) { - return const_cast<EntityInstanceT&>(const_cast<const EntitySnapshotBase*>(this)->getInstance(id)); + detail::checkHasInstanceIndex(instanceID); + return this->getInstance(instanceID.instanceIndex); } - - const EntityInstanceT& getInstance(const MemoryID& id) const + const EntityInstanceT& + getInstance(const MemoryID& instanceID) const { - if (!id.hasInstanceIndex()) - { - throw armem::error::InvalidMemoryID(id, "ID has no instance index."); - } - return getInstance(id.instanceIndex); + detail::checkHasInstanceIndex(instanceID); + return this->getInstance(instanceID.instanceIndex); } @@ -151,6 +172,20 @@ namespace armarx::armem::base return this->forEachChild(func); } + + // Get child keys + std::vector<int> getInstanceIndices() const + { + std::vector<int> indices; + indices.reserve(this->size()); + for (size_t i = 0; i < this->size(); ++i) + { + indices.push_back(static_cast<int>(i)); + } + return indices; + } + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] inline const std::vector<EntityInstanceT>& instances() const { diff --git a/source/RobotAPI/libraries/armem/core/base/MemoryBase.h b/source/RobotAPI/libraries/armem/core/base/MemoryBase.h index 9865f6da1..f4699b6ac 100644 --- a/source/RobotAPI/libraries/armem/core/base/MemoryBase.h +++ b/source/RobotAPI/libraries/armem/core/base/MemoryBase.h @@ -4,8 +4,9 @@ #include <string> #include "CoreSegmentBase.h" -#include "detail/EntityContainerBase.h" +#include "detail/MemoryContainerBase.h" #include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base @@ -16,13 +17,17 @@ namespace armarx::armem::base */ template <class _CoreSegmentT, class _Derived> class MemoryBase : - public detail::EntityContainerBase<_CoreSegmentT, typename _CoreSegmentT::ProviderSegmentT::EntityT, _Derived>, - public detail::ForEachEntityInstanceMixin<_Derived>, - public detail::ForEachEntitySnapshotMixin<_Derived>, - public detail::ForEachEntityMixin<_Derived>, - public detail::ForEachProviderSegmentMixin<_Derived> + public detail::MemoryContainerBase<std::map<std::string, _CoreSegmentT>, _Derived> + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::ForEachEntitySnapshotMixin<_Derived> + , public detail::ForEachEntityMixin<_Derived> + , public detail::ForEachProviderSegmentMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> + , public detail::GetFindSnapshotMixin<_Derived> + , public detail::GetFindEntityMixin<_Derived> + , public detail::GetFindProviderSegmentMixin<_Derived> { - using Base = detail::EntityContainerBase<_CoreSegmentT, typename _CoreSegmentT::ProviderSegmentT::EntityT, _Derived>; + using Base = detail::MemoryContainerBase<std::map<std::string, _CoreSegmentT>, _Derived>; public: @@ -35,6 +40,8 @@ namespace armarx::armem::base using EntitySnapshotT = typename EntityT::EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = CoreSegmentT; + struct UpdateResult { @@ -77,95 +84,77 @@ namespace armarx::armem::base // READ ACCESS - inline const std::string& name() const + // Get key + inline std::string& name() { return this->id().memoryName; } - inline std::string& name() + inline const std::string& name() const { - return const_cast<std::string&>(const_cast<const MemoryBase*>(this)->name()); + return this->id().memoryName; } + // Has child by key bool hasCoreSegment(const std::string& name) const { - return this->_container.count(name) > 0; + return this->findCoreSegment(name) != nullptr; + } + // Has child by MemoryID + bool hasCoreSegment(const MemoryID& coreSegmentID) const + { + return this->findCoreSegment(coreSegmentID) != nullptr; } - std::vector<std::string> getCoreSegmentNames() const + // Find child by key + CoreSegmentT* findCoreSegment(const std::string& name) { - return simox::alg::get_keys(this->_container); + return detail::findChildByKey(name, this->_container); + } + const CoreSegmentT* findCoreSegment(const std::string& name) const + { + return detail::findChildByKey(name, this->_container); } + // Get child by key CoreSegmentT& getCoreSegment(const std::string& name) { - return const_cast<CoreSegmentT&>(const_cast<const MemoryBase*>(this)->getCoreSegment(name)); + return detail::getChildByKey(name, this->_container, *this); } - const CoreSegmentT& getCoreSegment(const std::string& name) const { - auto it = this->_container.find(name); - if (it != this->_container.end()) - { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<CoreSegmentT>(name, *this); - } + return detail::getChildByKey(name, this->_container, *this); } - - bool hasProviderSegment(const MemoryID& providerSegmentID) const + // Find child by MemoryID + CoreSegmentT* findCoreSegment(const MemoryID& coreSegmentID) { - auto it = this->_container.find(providerSegmentID.coreSegmentName); - if (it != this->_container.end()) - { - return it->second.hasProviderSegment(providerSegmentID.providerSegmentName); - } - else - { - return false; - } + detail::checkHasCoreSegmentName(coreSegmentID); + return this->findCoreSegment(coreSegmentID.providerSegmentName); } - - ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) + const CoreSegmentT* findCoreSegment(const MemoryID& coreSegmentID) const { - return getCoreSegment(providerSegmentID.coreSegmentName).getProviderSegment(providerSegmentID.providerSegmentName); + detail::checkHasCoreSegmentName(coreSegmentID); + return this->findCoreSegment(coreSegmentID.providerSegmentName); } - const ProviderSegmentT& getProviderSegment(const MemoryID& providerSegmentID) const + // Get child by MemoryID + CoreSegmentT& getCoreSegment(const MemoryID& coreSegmentID) { - return getCoreSegment(providerSegmentID.coreSegmentName).getProviderSegment(providerSegmentID.providerSegmentName); + detail::checkHasCoreSegmentName(coreSegmentID); + return this->getCoreSegment(coreSegmentID.providerSegmentName); } - - - using Base::getEntity; - const EntityT& getEntity(const MemoryID& id) const + const CoreSegmentT& getCoreSegment(const MemoryID& coreSegmentID) const { - this->_checkContainerName(id.memoryName, this->name()); - return getCoreSegment(id.coreSegmentName).getEntity(id); + detail::checkHasCoreSegmentName(coreSegmentID); + return this->getCoreSegment(coreSegmentID.providerSegmentName); } - const EntityT* findEntity(const MemoryID& id) const - { - this->_checkContainerName(id.memoryName, this->name()); - if (id.hasCoreSegmentName()) - { - return getCoreSegment(id.coreSegmentName).findEntity(id); - } - else - { - for (const auto& [_, coreSegment] : this->_container) - { - if (auto entity = coreSegment.findEntity(id)) - { - return entity; - } - } - return nullptr; - } - } + // get/findInstance are provided by GetFindInstanceMixin + // get/findSnapshot are provided by GetFindSnapshotMixin + // get/findEntity are provided by GetFindEntityMixin + // get/findProviderSegment are provided by GetFindProviderSegmentMixin + // ITERATION @@ -193,6 +182,12 @@ namespace armarx::armem::base // forEachInstance() is provided by ForEachEntityInstanceMixin. + std::vector<std::string> getCoreSegmentNames() const + { + return simox::alg::get_keys(this->_container); + } + + [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] inline auto& coreSegments() const { @@ -229,28 +224,6 @@ namespace armarx::armem::base return _addCoreSegment(coreSegment.name(), coreSegment); } - /** - * @brief Add multiple core segments. - * @param The core segment names. - * @return The core segments. The contained pointers are never null. - */ - std::vector<CoreSegmentT*> addCoreSegments(const std::vector<std::string>& names) - { - std::vector<CoreSegmentT*> segments; - for (const auto& name : names) - { - try - { - segments.push_back(&addCoreSegment(name)); - } - catch (const armem::error::ContainerEntryAlreadyExists& e) - { - // ARMARX_INFO << e.what() << "\nIgnoring multiple addition."; - } - } - return segments; - } - /** * @brief Store all updates in `commit`. @@ -394,7 +367,7 @@ namespace armarx::armem::base std::pair<bool, CoreSegmentT*> _addCoreSegmentIfMissing(const std::string& coreSegmentName) { - CoreSegmentT* coreSeg; + CoreSegmentT* coreSeg = nullptr; auto it = this->_container.find(coreSegmentName); if (it == this->_container.end()) diff --git a/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h b/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h index e51632417..2f76aa839 100644 --- a/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h +++ b/source/RobotAPI/libraries/armem/core/base/ProviderSegmentBase.h @@ -5,8 +5,9 @@ #include "EntityBase.h" #include "detail/AronTyped.h" -#include "detail/EntityContainerBase.h" +#include "detail/MemoryContainerBase.h" #include "detail/iteration_mixins.h" +#include "detail/lookup_mixins.h" namespace armarx::armem::base @@ -17,12 +18,14 @@ namespace armarx::armem::base */ template <class _EntityT, class _Derived> class ProviderSegmentBase : - public detail::EntityContainerBase<_EntityT, _EntityT, _Derived>, - public detail::AronTyped, - public detail::ForEachEntityInstanceMixin<_Derived>, - public detail::ForEachEntitySnapshotMixin<_Derived> + public detail::MemoryContainerBase<std::map<std::string, _EntityT>, _Derived> + , public detail::AronTyped + , public detail::ForEachEntityInstanceMixin<_Derived> + , public detail::ForEachEntitySnapshotMixin<_Derived> + , public detail::GetFindInstanceMixin<_Derived> + , public detail::GetFindSnapshotMixin<_Derived> { - using Base = detail::EntityContainerBase<_EntityT, _EntityT, _Derived>; + using Base = detail::MemoryContainerBase<std::map<std::string, _EntityT>, _Derived>; public: @@ -33,6 +36,8 @@ namespace armarx::armem::base using EntitySnapshotT = typename EntityT::EntitySnapshotT; using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; + using ChildT = EntityT; + struct UpdateResult { armarx::armem::UpdateType providerSegmentUpdateType; @@ -48,6 +53,7 @@ namespace armarx::armem::base {} }; + public: ProviderSegmentBase() @@ -76,65 +82,76 @@ namespace armarx::armem::base // READ ACCESS - inline const std::string& name() const + // Get key + inline std::string& name() { return this->id().providerSegmentName; } - inline std::string& name() + inline const std::string& name() const { - return const_cast<std::string&>(const_cast<const ProviderSegmentBase*>(this)->name()); + return this->id().providerSegmentName; } + // Has child by key bool hasEntity(const std::string& name) const { - return this->_container.count(name) > 0; + return this->findEntity(name) != nullptr; } - - std::vector<std::string> getEntityNames() const + // Has child by ID + bool hasEntity(const MemoryID& entityID) const { - return simox::alg::get_keys(this->_container); + return this->findEntity(entityID) != nullptr; } - using Base::getEntity; - const EntityT& getEntity(const MemoryID& id) const + + // Find child by key + EntityT* findEntity(const std::string& name) + { + return detail::findChildByKey(name, this->_container); + } + const EntityT* findEntity(const std::string& name) const { - this->_checkContainerName(id.providerSegmentName, this->getKeyString()); - return getEntity(id.entityName); + return detail::findChildByKey(name, this->_container); } + // Get child by key EntityT& getEntity(const std::string& name) { - return const_cast<EntityT&>(const_cast<const ProviderSegmentBase*>(this)->getEntity(name)); + return detail::getChildByKey(name, this->_container, *this); } - const EntityT& getEntity(const std::string& name) const { - auto it = this->_container.find(name); - if (it != this->_container.end()) - { - return it->second; - } - else - { - throw armem::error::MissingEntry::create<EntityT>(name, *this); - } + return detail::getChildByKey(name, this->_container, *this); } - const EntityT* findEntity(const MemoryID& id) const + // Find child by MemoryID + EntityT* findEntity(const MemoryID& entityID) { - this->_checkContainerName(id.providerSegmentName, this->getKeyString()); - auto it = this->_container.find(id.entityName); - if (it != this->_container.end()) - { - return &it->second; - } - else - { - return nullptr; - } + detail::checkHasTimestamp(entityID); + return this->findEntity(entityID.entityName); + } + const EntityT* findEntity(const MemoryID& entityID) const + { + detail::checkHasTimestamp(entityID); + return this->findEntity(entityID.entityName); } + // Get child by MemoryID + EntityT& getEntity(const MemoryID& entityID) + { + detail::checkHasTimestamp(entityID); + return this->getEntity(entityID.entityName); + } + const EntityT& getEntity(const MemoryID& entityID) const + { + detail::checkHasTimestamp(entityID); + return this->getEntity(entityID.entityName); + } + + // get/findInstance are provided by GetFindInstanceMixin + // get/findSnapshot are provided by GetFindSnapshotMixin + // ITERATION @@ -157,8 +174,15 @@ namespace armarx::armem::base return this->forEachChild(func); } - // forEachSnapshot() is provided by ForEachEntitySnapshotMixin. - // forEachInstance() is provided by ForEachEntityInstanceMixin. + // forEachSnapshot() is provided by ForEachEntitySnapshotMixin + // forEachInstance() is provided by ForEachEntityInstanceMixin + + + // Get child keys + std::vector<std::string> getEntityNames() const + { + return simox::alg::get_keys(this->_container); + } [[deprecated("Direct container access is deprecated. Use forEach*() instead.")]] diff --git a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp b/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp deleted file mode 100644 index 05a23bc12..000000000 --- a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "EntityContainerBase.h" diff --git a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h b/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h deleted file mode 100644 index 53cddfff6..000000000 --- a/source/RobotAPI/libraries/armem/core/base/detail/EntityContainerBase.h +++ /dev/null @@ -1,124 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/armem/core/Commit.h> -#include <RobotAPI/libraries/armem/core/error/ArMemError.h> - -#include "MemoryContainerBase.h" - -#include <RobotAPI/libraries/armem/core/base/EntityBase.h> -#include <RobotAPI/libraries/armem/core/base/EntitySnapshotBase.h> - - -namespace armarx::armem::base::detail -{ - - /** - * @brief A container of entities at some point in the hierarchy. - * - * Can be updated by multiple entity updates. - */ - template <class _ValueT, class _EntityT, class _Derived> - class EntityContainerBase : - public MemoryContainerBase<std::map<std::string, _ValueT>, _Derived> - { - using Base = MemoryContainerBase<std::map<std::string, _ValueT>, _Derived>; - - public: - - using DerivedT = _Derived; - using ValueT = _ValueT; - - using EntityT = _EntityT; - using EntitySnapshotT = typename EntityT::EntitySnapshotT; - using EntityInstanceT = typename EntitySnapshotT::EntityInstanceT; - - public: - - // `using Base::MemoryContainerBase` breaks code completion of QtCreator. - using MemoryContainerBase<std::map<std::string, _ValueT>, _Derived>::MemoryContainerBase; - using MemoryContainerBase<std::map<std::string, _ValueT>, _Derived>::operator=; - - /** - * @brief Retrieve an entity. - * @param id The entity ID. - * @return The entity. - * @throw An exception deriving from `armem::error::ArMemError` if the entity is missing. - */ - // const EntityT& getEntity(const MemoryID& id) const; - EntityT& getEntity(const MemoryID& id) - { - return const_cast<EntityT&>(const_cast<const EntityContainerBase*>(this)->_derived().getEntity(id)); - } - - /** - * @brief Find an entity. - * - * Search for the entity with the given ID and return a pointer to the - * first match. If `id` is underspecified (e.g. no provider segment name), - * search all children until the first match is found. - * - * If no matching entity is found, return `nullptr`. - * - * @param id The entities ID. - * @return A pointer to the first matching entity or `nullptr` if none was found. - */ - // const EntityT* findEntity(const MemoryID& id) const; - EntityT* findEntity(const MemoryID& id) - { - return const_cast<EntityT*>(const_cast<const EntityContainerBase*>(this)->_derived().findEntity(id)); - } - - - /** - * @brief Retrieve an entity snapshot. - * - * Uses `getEntity()` to retrieve the respective entity. - * - * @param id The snapshot ID. - * @return The entity snapshot. - * @throw An exception deriving from `armem::error::ArMemError` if the snapshot is missing. - */ - EntitySnapshotT& getEntitySnapshot(const MemoryID& id) - { - return const_cast<EntitySnapshotT&>(const_cast<const EntityContainerBase*>(this)->_derived().getEntitySnapshot(id)); - } - - const EntitySnapshotT& getEntitySnapshot(const MemoryID& id) const - { - const EntityT& entity = _derived().getEntity(id); - - if (id.hasTimestamp()) - { - return entity.getSnapshot(id); - } - else - { - return entity.getLatestSnapshot(); - } - } - - EntityInstanceT& getEntityInstance(const MemoryID& id) - { - return getEntitySnapshot(id).getInstance(id); - } - - const EntityInstanceT& getEntityInstance(const MemoryID& id) const - { - return getEntitySnapshot(id).getInstance(id); - } - - - private: - - DerivedT& _derived() - { - return static_cast<DerivedT&>(*this); - } - const DerivedT& _derived() const - { - return static_cast<const DerivedT&>(*this); - } - - }; - -} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp new file mode 100644 index 000000000..ceaf41d3c --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.cpp @@ -0,0 +1,63 @@ +#include "lookup_mixins.h" + +#include <RobotAPI/libraries/armem/core/error/ArMemError.h> + + +namespace armarx::armem::base +{ + + void detail::checkHasInstanceIndex(const MemoryID& instanceID) + { + if (not instanceID.hasInstanceIndex()) + { + throw armem::error::InvalidMemoryID(instanceID, "Entity instance ID has no instance index."); + } + } + + + void detail::checkHasTimestamp(const MemoryID& snapshotID) + { + if (not snapshotID.hasTimestamp()) + { + throw armem::error::InvalidMemoryID(snapshotID, "Entity snapshot ID has no timestamp."); + } + } + + + void detail::checkHasEntityName(const MemoryID& entityID) + { + if (not entityID.hasEntityName()) + { + throw armem::error::InvalidMemoryID(entityID, "Entity ID has no entity name."); + } + } + + + void detail::checkHasProviderSegmentName(const MemoryID& providerSegmentID) + { + if (not providerSegmentID.hasEntityName()) + { + throw armem::error::InvalidMemoryID(providerSegmentID, "Provider Segment ID has no provider segment name."); + } + } + + + void detail::checkHasCoreSegmentName(const MemoryID& coreSegmentID) + { + if (not coreSegmentID.hasEntityName()) + { + throw armem::error::InvalidMemoryID(coreSegmentID, "Core Segment ID has no core segment name."); + } + } + + + void detail::checkHasMemoryName(const MemoryID& memoryID) + { + if (not memoryID.hasEntityName()) + { + throw armem::error::InvalidMemoryID(memoryID, "Memory ID has no memory name."); + } + } + + +} diff --git a/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h new file mode 100644 index 000000000..8afbd7108 --- /dev/null +++ b/source/RobotAPI/libraries/armem/core/base/detail/lookup_mixins.h @@ -0,0 +1,260 @@ +#pragma once + +#include <RobotAPI/libraries/armem/core/MemoryID.h> +#include <RobotAPI/libraries/armem/core/error/ArMemError.h> + + +namespace armarx::armem::base::detail +{ + + void checkHasInstanceIndex(const MemoryID& instanceID); + void checkHasTimestamp(const MemoryID& snapshotID); + void checkHasEntityName(const MemoryID& entityID); + void checkHasProviderSegmentName(const MemoryID& providerSegmentID); + void checkHasCoreSegmentName(const MemoryID& coreSegmentID); + void checkHasMemoryName(const MemoryID& memory); + + + template <class KeyT, class ContainerT> + auto* findChildByKey(const KeyT& key, ContainerT&& container) + { + auto it = container.find(key); + return it != container.end() ? &it->second : nullptr; + } + + template <class KeyT, class ContainerT, class ParentT, class KeyStringFn> + auto& + getChildByKey( + const KeyT& key, + ContainerT&& container, + const ParentT& parent, + KeyStringFn&& keyStringFn) + { + if (auto* child = findChildByKey(key, container)) + { + return *child; + } + else + { + throw armem::error::MissingEntry::create<typename ParentT::ChildT>(keyStringFn(key), parent); + } + } + template <class KeyT, class ContainerT, class ParentT> + auto& + getChildByKey( + const KeyT& key, + ContainerT&& container, + const ParentT& parent) + { + return getChildByKey(key, container, parent, [](const KeyT & key) + { + return key; + }); + } + + + template <class DerivedT, class ThisT> + DerivedT& + derived(ThisT* t) + { + return static_cast<DerivedT&>(*t); + } + template <class DerivedT, class ThisT> + const DerivedT& + derived(const ThisT* t) + { + return static_cast<const DerivedT&>(*t); + } + + + template <class DerivedT> + struct GetFindInstanceMixin + { + // Relies on this->find/getSnapshot() + + bool hasInstance(const MemoryID& instanceID) const + { + return derived<DerivedT>(this).findInstance(instanceID) != nullptr; + } + + /** + * @brief Find an entity instance. + * @param id The instance ID. + * @return The instance or nullptr if it is missing. + */ + auto* + findInstance(const MemoryID& instanceID) + { + auto* snapshot = derived<DerivedT>(this).findSnapshot(instanceID); + return snapshot ? snapshot->findInstance(instanceID) : nullptr; + } + const auto* + findInstance(const MemoryID& instanceID) const + { + const auto* snapshot = derived<DerivedT>(this).findSnapshot(instanceID); + return snapshot ? snapshot->findInstance(instanceID) : nullptr; + } + + /** + * @brief Retrieve an entity instance. + * @param id The instance ID. + * @return The instance if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getInstance(const MemoryID& instanceID) + { + return derived<DerivedT>(this).getSnapshot(instanceID).getInstance(instanceID); + } + const auto& + getInstance(const MemoryID& instanceID) const + { + return derived<DerivedT>(this).getSnapshot(instanceID).getInstance(instanceID); + } + }; + + + template <class DerivedT> + struct GetFindSnapshotMixin + { + // Relies on this->find/getEntity() + + bool hasSnapshot(const MemoryID& snapshotID) const + { + return derived<DerivedT>(this).findSnapshot(snapshotID) != nullptr; + } + + /** + * @brief Find an entity snapshot. + * @param id The snapshot ID. + * @return The snapshot or nullptr if it is missing. + */ + auto* + findSnapshot(const MemoryID& snapshotID) + { + auto* entity = derived<DerivedT>(this).findEntity(snapshotID); + return entity ? entity->findSnapshot(snapshotID) : nullptr; + } + const auto* + findSnapshot(const MemoryID& snapshotID) const + { + auto* entity = derived<DerivedT>(this).findEntity(snapshotID); + return entity ? entity->findSnapshot(snapshotID) : nullptr; + } + + /** + * @brief Retrieve an entity snapshot. + * @param id The snapshot ID. + * @return The snapshot if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getSnapshot(const MemoryID& snapshotID) + { + return derived<DerivedT>(this).getEntity(snapshotID).getSnapshot(snapshotID); + } + const auto& + getSnapshot(const MemoryID& snapshotID) const + { + return derived<DerivedT>(this).getEntity(snapshotID).getSnapshot(snapshotID); + } + }; + + + + template <class DerivedT> + struct GetFindEntityMixin + { + // Relies on this->find/getProviderSegment() + + bool hasEntity(const MemoryID& entityID) const + { + return derived<DerivedT>(this).findEntity(entityID) != nullptr; + } + + /** + * @brief Find an entity. + * @param id The entity ID. + * @return The entity or nullptr if it is missing. + */ + auto* + findEntity(const MemoryID& entityID) + { + auto* provSeg = derived<DerivedT>(this).findProviderSegment(entityID); + return provSeg ? provSeg->findEntity(entityID) : nullptr; + } + const auto* + findEntity(const MemoryID& entityID) const + { + auto* provSeg = derived<DerivedT>(this).findProviderSegment(entityID); + return provSeg ? provSeg->findEntity(entityID) : nullptr; + } + + /** + * @brief Retrieve an entity. + * @param id The entity ID. + * @return The entity if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getEntity(const MemoryID& entityID) + { + return derived<DerivedT>(this).getProviderSegment(entityID).getEntity(entityID); + } + const auto& + getEntity(const MemoryID& entityID) const + { + return derived<DerivedT>(this).getProviderSegment(entityID).getEntity(entityID); + } + }; + + + + template <class DerivedT> + struct GetFindProviderSegmentMixin + { + // Relies on this->find/getCoreSegment() + + bool hasProviderSegment(const MemoryID& providerSegmentID) const + { + return derived<DerivedT>(this).findProviderSegment(providerSegmentID) != nullptr; + } + + /** + * @brief Retrieve a provider segment. + * @param id The provider segment ID. + * @return The provider segment if it is found or nullptr if it missing. + */ + auto* + findProviderSegment(const MemoryID& providerSegmentID) + { + auto* provSeg = derived<DerivedT>(this).findCoreSegment(providerSegmentID); + return provSeg ? provSeg->findProviderSegment(providerSegmentID) : nullptr; + } + const auto* + findProviderSegment(const MemoryID& providerSegmentID) const + { + auto* provSeg = derived<DerivedT>(this).findCoreSegment(providerSegmentID); + return provSeg ? provSeg->findProviderSegment(providerSegmentID) : nullptr; + } + + /** + * @brief Retrieve a provider segment. + * @param id The provider segment ID. + * @return The provider segment if it is found. + * @throw `armem::error::ArMemError` if it is missing. + */ + auto& + getProviderSegment(const MemoryID& providerSegmentID) + { + return derived<DerivedT>(this).getCoreSegment(providerSegmentID).getProviderSegment(providerSegmentID); + } + const auto& + getProviderSegment(const MemoryID& providerSegmentID) const + { + return derived<DerivedT>(this).getCoreSegment(providerSegmentID).getProviderSegment(providerSegmentID); + } + + }; + +} -- GitLab