Jump to content
  • Advertisement

matt77hias

Member
  • Content Count

    868
  • Joined

  • Last visited

  • Days Won

    1

matt77hias last won the day on March 15

matt77hias had the most liked content!

Community Reputation

557 Good

About matt77hias

  • Rank
    Advanced Member

Personal Information

Social

  • Twitter
    matt77hias
  • Github
    matt77hias

Recent Profile Visitors

14090 profile views
  1. An example (i.e., removed a private access modifier, added explicit cast) illustrating the contents of the three containers: https://wandbox.org/permlink/OvHtdU0llbXn5y6Y
  2. #include <algorithm> #include <cassert> #include <cstdint> #include <functional> #include <iostream> #include <unordered_map> #include <utility> #include <vector> using U32 = std::uint32_t; template< typename T > using AlignedVector = std::vector< T >; namespace mage { class Entity { public: constexpr explicit Entity(U32 id = 0u) noexcept : m_id(id) {} constexpr Entity(const Entity& entity) noexcept = default; constexpr Entity(Entity&& entity) noexcept = default; ~Entity() = default; Entity& operator=(const Entity& entity) noexcept = default; Entity& operator=(Entity&& entity) noexcept = default; [[nodiscard]] constexpr explicit operator bool() const noexcept { return IsValid(); } [[nodiscard]] constexpr bool IsValid() const noexcept { return 0u != m_id; } [[nodiscard]] constexpr bool operator==(const Entity& rhs) const noexcept { return m_id == rhs.m_id; } [[nodiscard]] constexpr bool operator!=(const Entity& rhs) const noexcept { return m_id != rhs.m_id; } [[nodiscard]] constexpr bool operator<=(const Entity& rhs) const noexcept { return m_id <= rhs.m_id; } [[nodiscard]] constexpr bool operator>=(const Entity& rhs) const noexcept { return m_id >= rhs.m_id; } [[nodiscard]] constexpr bool operator<(const Entity& rhs) const noexcept { return m_id < rhs.m_id; } [[nodiscard]] constexpr bool operator>(const Entity& rhs) const noexcept { return m_id > rhs.m_id; } [[nodiscard]] std::size_t Hash() const noexcept { return std::hash< U32 >()(m_id); } private: U32 m_id; }; static_assert(sizeof(U32) == sizeof(Entity)); } namespace std { template<> struct hash< mage::Entity > { size_t operator()(const mage::Entity& entity) const { return entity.Hash(); } }; } namespace mage { //------------------------------------------------------------------------- // ComponentManager //------------------------------------------------------------------------- template< typename T > class ComponentManager { public: //--------------------------------------------------------------------- // Class Member Types //--------------------------------------------------------------------- using ComponentContainer = AlignedVector< T >; using EntityContainer = AlignedVector< Entity >; using MappingContainer = std::unordered_map< Entity, std::size_t >; using value_type = typename ComponentContainer::value_type; using size_type = typename ComponentContainer::size_type; using difference_type = typename ComponentContainer::difference_type; using reference = typename ComponentContainer::reference; using const_reference = typename ComponentContainer::const_reference; using pointer = typename ComponentContainer::pointer; using const_pointer = typename ComponentContainer::const_pointer; using iterator = typename ComponentContainer::iterator; using const_iterator = typename ComponentContainer::const_iterator; using reverse_iterator = typename ComponentContainer::reverse_iterator; using const_reverse_iterator = typename ComponentContainer::const_reverse_iterator; //--------------------------------------------------------------------- // Friends //--------------------------------------------------------------------- template< typename U > friend class Record; template< typename U > friend class RecordIterator; //--------------------------------------------------------------------- // Constructors and Destructors //--------------------------------------------------------------------- ComponentManager() = default; ComponentManager(const ComponentManager& manager) = default; ComponentManager(ComponentManager&& manager) noexcept = default; ~ComponentManager() = default; //--------------------------------------------------------------------- // Assignment Operators //--------------------------------------------------------------------- ComponentManager& operator=(const ComponentManager& manager) = default; ComponentManager& operator=(ComponentManager&& manager) noexcept = default; //--------------------------------------------------------------------- // Member Methods: Element access //--------------------------------------------------------------------- [[nodiscard]] bool Contains(Entity entity) const noexcept { return m_mapping.find(entity) != m_mapping.cend(); } [[nodiscard]] pointer Get(Entity entity) noexcept { if (const auto it = m_mapping.find(entity); it != m_mapping.cend()) { return &m_components[it->second]; } return nullptr; } [[nodiscard]] const_pointer Get(Entity entity) const noexcept { if (const auto it = m_mapping.find(entity); it != m_mapping.cend()) { return &m_components[it->second]; } return nullptr; } [[nodiscard]] reference at(size_type index) { return m_components.at(index); } [[nodiscard]] const_reference at(size_type index) const { return m_components.at(index); } [[nodiscard]] reference operator[](size_type index) noexcept { return m_components[index]; } [[nodiscard]] const_reference operator[](size_type index) const noexcept { return m_components[index]; } [[nodiscard]] reference front() noexcept { return m_components.front(); } [[nodiscard]] const_reference front() const noexcept { return m_components.front(); } [[nodiscard]] reference back() noexcept { return m_components.back(); } [[nodiscard]] const_reference back() const noexcept { return m_components.back(); } [[nodiscard]] value_type* data() noexcept { return m_components.data(); } [[nodiscard]] const value_type* data() const noexcept { return m_components.data(); } //--------------------------------------------------------------------- // Member Methods: Iterators //--------------------------------------------------------------------- [[nodiscard]] iterator begin() noexcept { return m_components.begin(); } [[nodiscard]] const_iterator begin() const noexcept { return m_components.begin(); } [[nodiscard]] const_iterator cbegin() const noexcept { return m_components.cbegin(); } [[nodiscard]] iterator end() noexcept { return m_components.end(); } [[nodiscard]] const_iterator end() const noexcept { return m_components.end(); } [[nodiscard]] const_iterator cend() const noexcept { return m_components.end(); } [[nodiscard]] reverse_iterator rbegin() noexcept { return m_components.rbegin(); } [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return m_components.rbegin(); } [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return m_components.crbegin(); } [[nodiscard]] reverse_iterator rend() noexcept { return m_components.rend(); } [[nodiscard]] const_reverse_iterator rend() const noexcept { return m_components.rend(); } [[nodiscard]] const_reverse_iterator crend() const noexcept { return m_components.crend(); } //--------------------------------------------------------------------- // Member Methods: Capacity //--------------------------------------------------------------------- [[nodiscard]] bool empty() const noexcept { return m_components.empty(); } [[nodiscard]] size_type size() const noexcept { return m_components.size(); } [[nodiscard]] size_type max_size() const noexcept { return m_components.max_size(); } void reserve(size_type new_capacity) { m_components.reserve(new_capacity); m_entities.reserve(new_capacity); m_mapping.reserve(new_capacity); } [[nodiscard]] size_type capacity() const noexcept { return m_components.capacity(); } void shrink_to_fit() { m_components.shrink_to_fit(); m_entities.shrink_to_fit(); } //--------------------------------------------------------------------- // Member Methods: Modifiers //--------------------------------------------------------------------- void clear() noexcept { m_components.clear(); m_entities.clear(); m_mapping.clear(); } void push_back(Entity entity, const value_type& value) { emplace_back(entity, value); } void push_back(Entity entity, value_type&& value) { emplace_back(entity, std::move(value)); } template< typename... ConstructorArgsT > reference emplace_back(Entity entity, ConstructorArgsT&&... args) { if (const auto it = m_mapping.find(entity); it != m_mapping.cend()) { return m_components[it->second]; } m_mapping.emplace(entity, size()); m_entities.push_back(entity); return m_components.emplace_back( std::forward< ConstructorArgsT >(args)...); } void pop_back() { m_mapping.erase(m_entities.back()); m_components.pop_back(); m_entities.pop_back(); } void swap(ComponentManager& other) noexcept { std::swap(m_components, other.m_components); std::swap(m_entities, other.m_entities); std::swap(m_mapping, other.m_mapping); } private: //--------------------------------------------------------------------- // Member Variables //--------------------------------------------------------------------- AlignedVector< value_type > m_components; AlignedVector< Entity > m_entities; std::unordered_map< Entity, std::size_t > m_mapping; }; //------------------------------------------------------------------------- // Record //------------------------------------------------------------------------- template< typename T > class Record { public: //--------------------------------------------------------------------- // Class Member Types //--------------------------------------------------------------------- using ComponentIterator = typename ComponentManager< T >::iterator; //--------------------------------------------------------------------- // Constructors and Destructors //--------------------------------------------------------------------- Record() noexcept : m_component_it{}, m_component_manager(nullptr) {} explicit Record(ComponentIterator component_it, ComponentManager< T >* component_manager) noexcept : m_component_it(std::move(component_it)), m_component_manager(component_manager) {} Record(const Record& record) noexcept = default; Record(Record&& record) noexcept = default; ~Record() = default; //--------------------------------------------------------------------- // Assignment Operators //--------------------------------------------------------------------- Record& operator=(const Record& record) = delete; Record& operator=(Record&& record) noexcept { swap(record); return *this; } //--------------------------------------------------------------------- // Member Methods //--------------------------------------------------------------------- [[nodiscard]] constexpr bool operator==(const T& rhs) const noexcept { return *m_component_it == rhs; } [[nodiscard]] constexpr bool operator!=(const T& rhs) const noexcept { return *m_component_it != rhs; } [[nodiscard]] constexpr bool operator<=(const T& rhs) const noexcept { return *m_component_it <= rhs; } [[nodiscard]] constexpr bool operator>=(const T& rhs) const noexcept { return *m_component_it >= rhs; } [[nodiscard]] constexpr bool operator<(const T& rhs) const noexcept { return *m_component_it < rhs; } [[nodiscard]] constexpr bool operator>(const T& rhs) const noexcept { return *m_component_it > rhs; } [[nodiscard]] friend constexpr bool operator==(const T& lhs, const Record& rhs) noexcept { return lhs == *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator!=(const T& lhs, const Record& rhs) noexcept { return lhs != *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator<=(const T& lhs, const Record& rhs) noexcept { return lhs <= *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator>=(const T& lhs, const Record& rhs) noexcept { return lhs >= *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator<(const T& lhs, const Record& rhs) noexcept { return lhs < *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator>(const T& lhs, const Record& rhs) noexcept { return lhs > *rhs.m_component_it; } [[nodiscard]] constexpr bool operator==(const Record& rhs) const noexcept { return *m_component_it == *rhs.m_component_it; } [[nodiscard]] constexpr bool operator!=(const Record& rhs) const noexcept { return *m_component_it != *rhs.m_component_it; } [[nodiscard]] constexpr bool operator<=(const Record& rhs) const noexcept { return *m_component_it <= *rhs.m_component_it; } [[nodiscard]] constexpr bool operator>=(const Record& rhs) const noexcept { return *m_component_it >= *rhs.m_component_it; } [[nodiscard]] constexpr bool operator<(const Record& rhs) const noexcept { return *m_component_it < *rhs.m_component_it; } [[nodiscard]] constexpr bool operator>(const Record& rhs) const noexcept { return *m_component_it > *rhs.m_component_it; } void swap(Record& other) noexcept { assert(nullptr != m_component_manager); assert(m_component_manager == other.m_component_manager); assert(m_component_manager->end() != m_component_it); assert(m_component_manager->end() != other.m_component_it); const std::size_t n1 = m_component_it - m_component_manager->m_components.begin(); auto entity_it1 = m_component_manager->m_entities.begin() + n1; auto& mapping1 = m_component_manager->m_mapping[*entity_it1]; const std::size_t n2 = other.m_component_it - m_component_manager->m_components.begin(); auto entity_it2 = m_component_manager->m_entities.begin() + n2; auto& mapping2 = m_component_manager->m_mapping[*entity_it2]; std::swap(*m_component_it, *other.m_component_it); std::swap(*entity_it1, *entity_it2); std::swap(mapping1, mapping2); } private: //--------------------------------------------------------------------- // Member Variables //--------------------------------------------------------------------- ComponentIterator m_component_it; ComponentManager< T >* m_component_manager; }; //------------------------------------------------------------------------- // RecordIterator //------------------------------------------------------------------------- template< typename T > class RecordIterator { public: //--------------------------------------------------------------------- // Class Member Types //--------------------------------------------------------------------- using ComponentIterator = typename ComponentManager< T >::iterator; //--------------------------------------------------------------------- // Constructors and Destructors //--------------------------------------------------------------------- RecordIterator() noexcept : m_component_it{}, m_component_manager(nullptr) {} explicit RecordIterator(ComponentIterator component_it, ComponentManager< T >* component_manager) noexcept : m_component_it(std::move(component_it)), m_component_manager(component_manager) {} RecordIterator(const RecordIterator& it) noexcept = default; RecordIterator(RecordIterator&& it) noexcept = default; ~RecordIterator() = default; //--------------------------------------------------------------------- // Assignment Operators //--------------------------------------------------------------------- RecordIterator& operator=(const RecordIterator& it) noexcept = default; RecordIterator& operator=(RecordIterator&& it) noexcept = default; //--------------------------------------------------------------------- // Member Methods //--------------------------------------------------------------------- Record< T > operator*() noexcept { return Record< T >(m_component_it, m_component_manager); } Record< T > operator*() const noexcept { return Record< T >(m_component_it, m_component_manager); } [[nodiscard]] Record< T > operator[](std::size_t n) noexcept { return Record< T >(m_component_it + n, m_component_manager); } [[nodiscard]] Record< T > operator[](std::size_t n) const noexcept { return Record< T >(m_component_it + n, m_component_manager); } [[nodiscard]] std::size_t operator-(const RecordIterator& it) const noexcept { return m_component_it - it.m_component_it; } [[nodiscard]] const RecordIterator operator+(std::size_t n) const noexcept { return RecordIterator(m_component_it + n, m_component_manager); } [[nodiscard]] const RecordIterator operator-(std::size_t n) const noexcept { return RecordIterator(m_component_it - n, m_component_manager); } [[nodiscard]] friend const RecordIterator operator+(std::size_t n, const RecordIterator& it) noexcept { return it + n; } RecordIterator& operator++() noexcept { ++m_component_it; return *this; } RecordIterator& operator--() noexcept { --m_component_it; return *this; } [[nodiscard]] friend const RecordIterator operator++(const RecordIterator& it) noexcept { return RecordIterator(it.m_component_it + 1u, it.m_component_manager); } [[nodiscard]] friend const RecordIterator operator--(const RecordIterator& it) noexcept { return RecordIterator(it.m_component_it - 1u, it.m_component_manager); } RecordIterator& operator+=(std::size_t n) noexcept { m_component_it += n; return *this; } RecordIterator& operator-=(std::size_t n) noexcept { m_component_it -= n; return *this; } [[nodiscard]] constexpr bool operator==(const RecordIterator& rhs) const noexcept { return m_component_it == rhs.m_component_it; } [[nodiscard]] constexpr bool operator!=(const RecordIterator& rhs) const noexcept { return m_component_it != rhs.m_component_it; } [[nodiscard]] constexpr bool operator<=(const RecordIterator& rhs) const noexcept { return m_component_it <= rhs.m_component_it; } [[nodiscard]] constexpr bool operator>=(const RecordIterator& rhs) const noexcept { return m_component_it >= rhs.m_component_it; } [[nodiscard]] constexpr bool operator<(const RecordIterator& rhs) const noexcept { return m_component_it < rhs.m_component_it; } [[nodiscard]] constexpr bool operator>(const RecordIterator& rhs) const noexcept { return m_component_it > rhs.m_component_it; } private: ComponentIterator m_component_it; ComponentManager< T >* m_component_manager; }; //------------------------------------------------------------------------- // ComponentManager //------------------------------------------------------------------------- template< typename T > [[nodiscard]] inline RecordIterator< T > RecordBegin(ComponentManager< T >& manager) noexcept { return RecordIterator< T >(manager.begin(), &manager); } template< typename T > [[nodiscard]] inline RecordIterator< T > RecordEnd(ComponentManager< T >& manager) noexcept { return RecordIterator< T >(manager.end(), &manager); } template< typename T > void swap(mage::Record< T > lhs, mage::Record< T > rhs) noexcept { lhs.swap(rhs); } } namespace std { template< typename T > struct iterator_traits< mage::RecordIterator< T > > { public: using value_type = mage::Record< T >; using reference_type = mage::Record< T >&; using pointer_type = mage::Record< T >*; using difference_type = ptrdiff_t; using iterator_category = random_access_iterator_tag; }; } int main() { mage::ComponentManager< float > manager; manager.emplace_back(mage::Entity(5u), 5.0f); manager.emplace_back(mage::Entity(4u), 4.0f); manager.emplace_back(mage::Entity(3u), 3.0f); manager.emplace_back(mage::Entity(2u), 2.0f); manager.emplace_back(mage::Entity(1u), 1.0f); for (auto c : manager) { std::cout << c; } std::cout << std::endl; std::sort(RecordBegin(manager), RecordEnd(manager)); for (auto c : manager) { std::cout << c; } return 0; } Note that I need to put swap(mage::Record< T > lhs, mage::Record< T > rhs) in the mage instead of std namespace, since both arguments are not passed as references, because RecordIterator< T >::operator* returns by value instead of reference. Currently, I do not see a possibility of returning by reference, since Record< T >'s do not pre-exist. This, unfortunately, makes the overload of operator-> impossible (not sure, however, which STL algorithms actually use this operator anyway). Note that unlike my earlier attempt, it is not necessary to update all iterators at once. Only one iterator is updated via the iterator operator overloads. The other iterators are only computed upon swapping. This is much cheaper than retrieving the unordered_map iterator every time (e.g., a completely sorted vector of components does not require any swaps and will thus not perform any std::unordered_map::operator[] invocations). The erase-remove idiom is currently not possible, since associative containers do not support std::remove/std::remove_if.
  3. I refactored and converted the above code to the container I want (should compile by itself): #include <algorithm> #include <cassert> #include <cstdint> #include <functional> #include <iostream> #include <unordered_map> #include <utility> #include <vector> using U32 = std::uint32_t; template< typename T > using AlignedVector = std::vector< T >; namespace mage { class Entity { public: constexpr explicit Entity(U32 id = 0u) noexcept : m_id(id) {} constexpr Entity(const Entity& entity) noexcept = default; constexpr Entity(Entity&& entity) noexcept = default; ~Entity() = default; Entity& operator=(const Entity& entity) noexcept = default; Entity& operator=(Entity&& entity) noexcept = default; [[nodiscard]] constexpr explicit operator bool() const noexcept { return IsValid(); } [[nodiscard]] constexpr bool IsValid() const noexcept { return 0u != m_id; } [[nodiscard]] constexpr bool operator==(const Entity& rhs) const noexcept { return m_id == rhs.m_id; } [[nodiscard]] constexpr bool operator!=(const Entity& rhs) const noexcept { return m_id != rhs.m_id; } [[nodiscard]] constexpr bool operator<=(const Entity& rhs) const noexcept { return m_id <= rhs.m_id; } [[nodiscard]] constexpr bool operator>=(const Entity& rhs) const noexcept { return m_id >= rhs.m_id; } [[nodiscard]] constexpr bool operator<(const Entity& rhs) const noexcept { return m_id < rhs.m_id; } [[nodiscard]] constexpr bool operator>(const Entity& rhs) const noexcept { return m_id > rhs.m_id; } [[nodiscard]] std::size_t Hash() const noexcept { return std::hash< U32 >()(m_id); } private: U32 m_id; }; static_assert(sizeof(U32) == sizeof(Entity)); } namespace std { template<> struct hash< mage::Entity > { size_t operator()(const mage::Entity& entity) const { return entity.Hash(); } }; } namespace mage { template< typename T > class ComponentManager { public: //--------------------------------------------------------------------- // Class Member Types //--------------------------------------------------------------------- using ComponentContainer = AlignedVector< T >; using EntityContainer = AlignedVector< Entity >; using MappingContainer = std::unordered_map< Entity, std::size_t >; using value_type = typename ComponentContainer::value_type; using size_type = typename ComponentContainer::size_type; using difference_type = typename ComponentContainer::difference_type; using reference = typename ComponentContainer::reference; using const_reference = typename ComponentContainer::const_reference; using pointer = typename ComponentContainer::pointer; using const_pointer = typename ComponentContainer::const_pointer; using iterator = typename ComponentContainer::iterator; using const_iterator = typename ComponentContainer::const_iterator; using reverse_iterator = typename ComponentContainer::reverse_iterator; using const_reverse_iterator = typename ComponentContainer::const_reverse_iterator; //--------------------------------------------------------------------- // Friends //--------------------------------------------------------------------- friend class Record; friend class RecordIterator; //--------------------------------------------------------------------- // Record //--------------------------------------------------------------------- class Record { public: //----------------------------------------------------------------- // Class Member Types //----------------------------------------------------------------- using ComponentIterator = typename ComponentManager::iterator; //----------------------------------------------------------------- // Constructors and Destructors //----------------------------------------------------------------- Record() noexcept : m_component_it{}, m_component_manager(nullptr) {} explicit Record(ComponentIterator component_it, ComponentManager* component_manager) noexcept : m_component_it(std::move(component_it)), m_component_manager(component_manager) {} Record(const Record& record) noexcept = default; Record(Record&& record) noexcept = default; ~Record() = default; //----------------------------------------------------------------- // Assignment Operators //----------------------------------------------------------------- Record& operator=(const Record& record) = delete; Record& operator=(Record&& record) noexcept { swap(record); return *this; } //----------------------------------------------------------------- // Member Methods //----------------------------------------------------------------- [[nodiscard]] constexpr bool operator==(const T& rhs) const noexcept { return *m_component_it == rhs; } [[nodiscard]] constexpr bool operator!=(const T& rhs) const noexcept { return *m_component_it != rhs; } [[nodiscard]] constexpr bool operator<=(const T& rhs) const noexcept { return *m_component_it <= rhs; } [[nodiscard]] constexpr bool operator>=(const T& rhs) const noexcept { return *m_component_it >= rhs; } [[nodiscard]] constexpr bool operator<(const T& rhs) const noexcept { return *m_component_it < rhs; } [[nodiscard]] constexpr bool operator>(const T& rhs) const noexcept { return *m_component_it > rhs; } [[nodiscard]] friend constexpr bool operator==(const T& lhs, const Record& rhs) noexcept { return lhs == *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator!=(const T& lhs, const Record& rhs) noexcept { return lhs != *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator<=(const T& lhs, const Record& rhs) noexcept { return lhs <= *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator>=(const T& lhs, const Record& rhs) noexcept { return lhs >= *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator<(const T& lhs, const Record& rhs) noexcept { return lhs < *rhs.m_component_it; } [[nodiscard]] friend constexpr bool operator>(const T& lhs, const Record& rhs) noexcept { return lhs > *rhs.m_component_it; } [[nodiscard]] constexpr bool operator==(const Record& rhs) const noexcept { return *m_component_it == *rhs.m_component_it; } [[nodiscard]] constexpr bool operator!=(const Record& rhs) const noexcept { return *m_component_it != *rhs.m_component_it; } [[nodiscard]] constexpr bool operator<=(const Record& rhs) const noexcept { return *m_component_it <= *rhs.m_component_it; } [[nodiscard]] constexpr bool operator>=(const Record& rhs) const noexcept { return *m_component_it >= *rhs.m_component_it; } [[nodiscard]] constexpr bool operator<(const Record& rhs) const noexcept { return *m_component_it < *rhs.m_component_it; } [[nodiscard]] constexpr bool operator>(const Record& rhs) const noexcept { return *m_component_it > *rhs.m_component_it; } void swap(Record& other) noexcept { assert(nullptr != m_component_manager); assert(m_component_manager == other.m_component_manager); assert(m_component_manager->end() != m_component_it); assert(m_component_manager->end() != other.m_component_it); const std::size_t n1 = m_component_it - m_component_manager->m_components.begin(); auto entity_it1 = m_component_manager->m_entities.begin() + n1; auto mapping_it1 = m_component_manager->m_mapping.find(*entity_it1); const std::size_t n2 = other.m_component_it - m_component_manager->m_components.begin(); auto entity_it2 = m_component_manager->m_entities.begin() + n2; auto mapping_it2 = m_component_manager->m_mapping.find(*entity_it2); std::swap(*m_component_it, *other.m_component_it); std::swap(*entity_it1, *entity_it2); std::swap(*mapping_it1, *mapping_it2); } private: //----------------------------------------------------------------- // Member Variables //----------------------------------------------------------------- ComponentIterator m_component_it; ComponentManager* m_component_manager; }; //--------------------------------------------------------------------- // RecordIterator //--------------------------------------------------------------------- class RecordIterator { public: //----------------------------------------------------------------- // Class Member Types //----------------------------------------------------------------- using ComponentIterator = typename ComponentManager::iterator; //----------------------------------------------------------------- // Constructors and Destructors //----------------------------------------------------------------- RecordIterator() noexcept : m_component_it{}, m_component_manager(nullptr) {} explicit RecordIterator(ComponentIterator component_it, ComponentManager* component_manager) noexcept : m_component_it(std::move(component_it)), m_component_manager(component_manager) {} RecordIterator(const RecordIterator& it) noexcept = default; RecordIterator(RecordIterator&& it) noexcept = default; ~RecordIterator() = default; //----------------------------------------------------------------- // Assignment Operators //----------------------------------------------------------------- RecordIterator& operator=(const RecordIterator& it) noexcept = default; RecordIterator& operator=(RecordIterator&& it) noexcept = default; //----------------------------------------------------------------- // Member Methods //----------------------------------------------------------------- Record operator*() noexcept { return Record(m_component_it, m_component_manager); } Record operator*() const noexcept { return Record(m_component_it, m_component_manager); } [[nodiscard]] Record operator[](std::size_t n) noexcept { return Record(m_component_it + n, m_component_manager); } [[nodiscard]] Record operator[](std::size_t n) const noexcept { return Record(m_component_it + n, m_component_manager); } [[nodiscard]] std::size_t operator-(const RecordIterator& it) const noexcept { return m_component_it - it.m_component_it; } [[nodiscard]] const RecordIterator operator+(std::size_t n) const noexcept { return RecordIterator(m_component_it + n, m_component_manager); } [[nodiscard]] const RecordIterator operator-(std::size_t n) const noexcept { return RecordIterator(m_component_it - n, m_component_manager); } [[nodiscard]] friend const RecordIterator operator+(std::size_t n, const RecordIterator& it) noexcept { return it + n; } RecordIterator& operator++() noexcept { ++m_component_it; return *this; } RecordIterator& operator--() noexcept { --m_component_it; return *this; } [[nodiscard]] friend const RecordIterator operator++(const RecordIterator& it) noexcept { return RecordIterator(it.m_component_it + 1u, it.m_component_manager); } [[nodiscard]] friend const RecordIterator operator--(const RecordIterator& it) noexcept { return RecordIterator(it.m_component_it - 1u, it.m_component_manager); } RecordIterator& operator+=(std::size_t n) noexcept { m_component_it += n; return *this; } RecordIterator& operator-=(std::size_t n) noexcept { m_component_it -= n; return *this; } [[nodiscard]] constexpr bool operator==(const RecordIterator& rhs) const noexcept { return m_component_it == rhs.m_component_it; } [[nodiscard]] constexpr bool operator!=(const RecordIterator& rhs) const noexcept { return m_component_it != rhs.m_component_it; } [[nodiscard]] constexpr bool operator<=(const RecordIterator& rhs) const noexcept { return m_component_it <= rhs.m_component_it; } [[nodiscard]] constexpr bool operator>=(const RecordIterator& rhs) const noexcept { return m_component_it >= rhs.m_component_it; } [[nodiscard]] constexpr bool operator<(const RecordIterator& rhs) const noexcept { return m_component_it < rhs.m_component_it; } [[nodiscard]] constexpr bool operator>(const RecordIterator& rhs) const noexcept { return m_component_it > rhs.m_component_it; } private: ComponentIterator m_component_it; ComponentManager* m_component_manager; }; //--------------------------------------------------------------------- // Constructors and Destructors //--------------------------------------------------------------------- ComponentManager() = default; ComponentManager(const ComponentManager& manager) = default; ComponentManager(ComponentManager&& manager) noexcept = default; ~ComponentManager() = default; //--------------------------------------------------------------------- // Assignment Operators //--------------------------------------------------------------------- ComponentManager& operator=(const ComponentManager& manager) = default; ComponentManager& operator=(ComponentManager&& manager) noexcept = default; //--------------------------------------------------------------------- // Member Methods: Element access //--------------------------------------------------------------------- [[nodiscard]] bool Contains(Entity entity) const noexcept { return m_mapping.find(entity) != m_mapping.cend(); } [[nodiscard]] pointer Get(Entity entity) noexcept { if (const auto it = m_mapping.find(entity); it != m_mapping.cend()) { return &m_components[it->second]; } return nullptr; } [[nodiscard]] const_pointer Get(Entity entity) const noexcept { if (const auto it = m_mapping.find(entity); it != m_mapping.cend()) { return &m_components[it->second]; } return nullptr; } [[nodiscard]] reference at(size_type index) { return m_components.at(index); } [[nodiscard]] const_reference at(size_type index) const { return m_components.at(index); } [[nodiscard]] reference operator[](size_type index) noexcept { return m_components[index]; } [[nodiscard]] const_reference operator[](size_type index) const noexcept { return m_components[index]; } [[nodiscard]] reference front() noexcept { return m_components.front(); } [[nodiscard]] const_reference front() const noexcept { return m_components.front(); } [[nodiscard]] reference back() noexcept { return m_components.back(); } [[nodiscard]] const_reference back() const noexcept { return m_components.back(); } [[nodiscard]] value_type* data() noexcept { return m_components.data(); } [[nodiscard]] const value_type* data() const noexcept { return m_components.data(); } //--------------------------------------------------------------------- // Member Methods: Iterators //--------------------------------------------------------------------- [[nodiscard]] RecordIterator RecordBegin() noexcept { return RecordIterator(begin(), this); } [[nodiscard]] RecordIterator RecordEnd() noexcept { return RecordIterator(end(), this); } [[nodiscard]] iterator begin() noexcept { return m_components.begin(); } [[nodiscard]] const_iterator begin() const noexcept { return m_components.begin(); } [[nodiscard]] const_iterator cbegin() const noexcept { return m_components.cbegin(); } [[nodiscard]] iterator end() noexcept { return m_components.end(); } [[nodiscard]] const_iterator end() const noexcept { return m_components.end(); } [[nodiscard]] const_iterator cend() const noexcept { return m_components.end(); } [[nodiscard]] reverse_iterator rbegin() noexcept { return m_components.rbegin(); } [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return m_components.rbegin(); } [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return m_components.crbegin(); } [[nodiscard]] reverse_iterator rend() noexcept { return m_components.rend(); } [[nodiscard]] const_reverse_iterator rend() const noexcept { return m_components.rend(); } [[nodiscard]] const_reverse_iterator crend() const noexcept { return m_components.crend(); } //--------------------------------------------------------------------- // Member Methods: Capacity //--------------------------------------------------------------------- [[nodiscard]] bool empty() const noexcept { return m_components.empty(); } [[nodiscard]] size_type size() const noexcept { return m_components.size(); } [[nodiscard]] size_type max_size() const noexcept { return m_components.max_size(); } void reserve(size_type new_capacity) { m_components.reserve(new_capacity); m_entities.reserve(new_capacity); m_mapping.reserve(new_capacity); } [[nodiscard]] size_type capacity() const noexcept { return m_components.capacity(); } void shrink_to_fit() { m_components.shrink_to_fit(); m_entities.shrink_to_fit(); } //--------------------------------------------------------------------- // Member Methods: Modifiers //--------------------------------------------------------------------- void clear() noexcept { m_components.clear(); m_entities.clear(); m_mapping.clear(); } void push_back(Entity entity, const value_type& value) { emplace_back(entity, value); } void push_back(Entity entity, value_type&& value) { emplace_back(entity, std::move(value)); } template< typename... ConstructorArgsT > reference emplace_back(Entity entity, ConstructorArgsT&&... args) { if (const auto it = m_mapping.find(entity); it != m_mapping.cend()) { return m_components[it->second]; } m_mapping.emplace(entity, size()); m_entities.push_back(entity); return m_components.emplace_back( std::forward< ConstructorArgsT >(args)...); } void pop_back() { m_mapping.erase(m_entities.back()); m_components.pop_back(); m_entities.pop_back(); } void swap(ComponentManager& other) noexcept { std::swap(m_components, other.m_components); std::swap(m_entities, other.m_entities); std::swap(m_mapping, other.m_mapping); } private: //--------------------------------------------------------------------- // Member Variables //--------------------------------------------------------------------- AlignedVector< value_type > m_components; AlignedVector< Entity > m_entities; std::unordered_map< Entity, std::size_t > m_mapping; }; } namespace std { template< typename T > void swap(typename mage::ComponentManager< T >::Record& lhs, typename mage::ComponentManager< T >::Record& rhs) noexcept { lhs.swap(rhs); } } int main() { mage::ComponentManager< float > manager; manager.emplace_back(mage::Entity(5u), 5.0f); manager.emplace_back(mage::Entity(4u), 4.0f); manager.emplace_back(mage::Entity(3u), 3.0f); manager.emplace_back(mage::Entity(2u), 2.0f); manager.emplace_back(mage::Entity(1u), 1.0f); for (auto c : manager) { std::cout << c; } std::cout << std::endl; //std::sort(manager.RecordBegin(), manager.RecordEnd()); for (auto c : manager) { std::cout << c; } return 0; } Unfortunately, this does not compile if the sorting is uncommented: error: no type named 'value_type' in 'std::__1::iterator_traits<mage::ComponentManager<float>::RecordIterator>' So I suppose I need to specialize iterator_traits, but how can this be achieved for RecordIterator? Edit: I think the best way to fix the latter, is to move RecordIterator and Record out ComponentManager.
  4. How is the stripe value connected to the original collections (and the actual allocated data) to perform the swap (on the actual data)? Quick and dirty prototype (the comparison type should not be hard coded, etc.) : #include <algorithm> #include <functional> #include <iostream> #include <utility> #include <vector> template< typename FirstT, typename SecondT > class BiValue { public: BiValue() noexcept : m_it1{}, m_it2{} {} BiValue(FirstT it1, SecondT it2) noexcept : m_it1(std::move(it1)), m_it2(std::move(it2)) {} BiValue(const BiValue& value) : m_it1(*value.m_it1), m_it2(*value.m_it2) {} BiValue(BiValue&& value) noexcept : m_it1(std::move(*value.m_it1)), m_it2(std::move(*value.m_it2)) {} ~BiValue() = default; BiValue& operator=(const BiValue& value) { *m_it1 = *value.m_it1; *m_it2 = *value.m_it2; return *this; } BiValue& operator=(BiValue&& value) noexcept { *m_it1 = std::move(*value.m_it1); *m_it2 = std::move(*value.m_it2); return *this; } [[nodiscard]] friend constexpr bool operator==(const BiValue& lhs, const int& rhs) noexcept { return *lhs.m_it1 == rhs; } [[nodiscard]] friend constexpr bool operator!=(const BiValue& lhs, const int& rhs) noexcept { return *lhs.m_it1 != rhs; } [[nodiscard]] friend constexpr bool operator<=(const BiValue& lhs, const int& rhs) noexcept { return *lhs.m_it1 <= rhs; } [[nodiscard]] friend constexpr bool operator>=(const BiValue& lhs, const int& rhs) noexcept { return *lhs.m_it1 >= rhs; } [[nodiscard]] friend constexpr bool operator<(const BiValue& lhs, const int& rhs) noexcept { return *lhs.m_it1 < rhs; } [[nodiscard]] friend constexpr bool operator>(const BiValue& lhs, const int& rhs) noexcept { return *lhs.m_it1 > rhs; } [[nodiscard]] friend constexpr bool operator==(const int& lhs, const BiValue& rhs) noexcept { return lhs == *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator!=(const int& lhs, const BiValue& rhs) noexcept { return lhs != *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator<=(const int& lhs, const BiValue& rhs) noexcept { return lhs <= *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator>=(const int& lhs, const BiValue& rhs) noexcept { return lhs >= *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator<(const int& lhs, const BiValue& rhs) noexcept { return lhs < *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator>(const int& lhs, const BiValue& rhs) noexcept { return lhs > *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator==(const BiValue& lhs, const BiValue& rhs) noexcept { return *lhs.m_it1 == *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator!=(const BiValue& lhs, const BiValue& rhs) noexcept { return *lhs.m_it1 != *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator<=(const BiValue& lhs, const BiValue& rhs) noexcept { return *lhs.m_it1 <= *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator>=(const BiValue& lhs, const BiValue& rhs) noexcept { return *lhs.m_it1 >= *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator<(const BiValue& lhs, const BiValue& rhs) noexcept { return *lhs.m_it1 < *rhs.m_it1; } [[nodiscard]] friend constexpr bool operator>(const BiValue& lhs, const BiValue& rhs) noexcept { return *lhs.m_it1 > *rhs.m_it1; } void swap(BiValue& other) noexcept { std::swap(*m_it1, *other.m_it1); std::swap(*m_it2, *other.m_it2); } private: FirstT m_it1; SecondT m_it2; }; template< typename FirstT, typename SecondT > auto make_bi_value(FirstT first, SecondT second) { return BiValue< FirstT, SecondT >(std::move(first), std::move(second)); } namespace std { template< typename FirstT, typename SecondT > void swap(BiValue< FirstT, SecondT >& lhs, BiValue< FirstT, SecondT >& rhs) { lhs.swap(rhs); } } template< typename FirstT, typename SecondT > class BiRandomAccessIterator { public: BiRandomAccessIterator() noexcept : m_it1{}, m_it2{} {} BiRandomAccessIterator(FirstT it1, SecondT it2) noexcept : m_it1(std::move(it1)), m_it2(std::move(it2)) {} BiRandomAccessIterator(const BiRandomAccessIterator& bit) = default; BiRandomAccessIterator(BiRandomAccessIterator&& bit) noexcept = default; ~BiRandomAccessIterator() = default; BiRandomAccessIterator& operator=(const BiRandomAccessIterator& bit) = default; BiRandomAccessIterator& operator=(BiRandomAccessIterator&& bit) noexcept = default; auto operator*() noexcept { return make_bi_value(m_it1, m_it2); } auto operator*() const noexcept { return make_bi_value(m_it1, m_it2); } [[nodiscard]] auto operator[](std::size_t n) noexcept { return make_bi_value(m_it1 + n, m_it2 + n); } [[nodiscard]] auto operator[](std::size_t n) const noexcept { return make_bi_value(m_it1 + n, m_it2 + n); } [[nodiscard]] const BiRandomAccessIterator operator-(const BiRandomAccessIterator& bit) const { return { m_it1 - bit.m_it1, m_it2 - bit.m_it2 }; } [[nodiscard]] const BiRandomAccessIterator operator+(std::size_t n) const { return { m_it1 + n, m_it2 + n }; } [[nodiscard]] const BiRandomAccessIterator operator-(std::size_t n) const { return { m_it1 - n, m_it2 - n }; } [[nodiscard]] friend const BiRandomAccessIterator operator+(std::size_t n, const BiRandomAccessIterator& bit) { return bit + n; } BiRandomAccessIterator& operator++() { ++m_it1; ++m_it2; return *this; } BiRandomAccessIterator& operator--() { --m_it1; --m_it2; return *this; } [[nodiscard]] friend const BiRandomAccessIterator operator++(const BiRandomAccessIterator& bit) { return { bit.m_it1 + 1u, bit.m_it2 + 1u }; } [[nodiscard]] friend const BiRandomAccessIterator operator--(const BiRandomAccessIterator& bit) { return { bit.m_it1 - 1u, bit.m_it2 - 1u }; } BiRandomAccessIterator& operator+=(std::size_t n) { m_it1 += n; m_it2 += n; return *this; } BiRandomAccessIterator& operator-=(std::size_t n) { m_it1 -= n; m_it2 -= n; return *this; } [[nodiscard]] constexpr bool operator==(const BiRandomAccessIterator& rhs) const noexcept { return m_it1 == rhs.m_it1; } [[nodiscard]] constexpr bool operator!=(const BiRandomAccessIterator& rhs) const noexcept { return m_it1 != rhs.m_it1; } [[nodiscard]] constexpr bool operator<=(const BiRandomAccessIterator& rhs) const noexcept { return m_it1 <= rhs.m_it1; } [[nodiscard]] constexpr bool operator>=(const BiRandomAccessIterator& rhs) const noexcept { return m_it1 >= rhs.m_it1; } [[nodiscard]] constexpr bool operator<(const BiRandomAccessIterator& rhs) const noexcept { return m_it1 < rhs.m_it1; } [[nodiscard]] constexpr bool operator>(const BiRandomAccessIterator& rhs) const noexcept { return m_it1 > rhs.m_it1; } private: FirstT m_it1; SecondT m_it2; }; int main() { std::vector< int > a = { 5, 4, 3, 2, 1 }; std::vector< int > b = { 5, 4, 3, 2, 1 }; BiRandomAccessIterator begin = { a.begin(), b.begin() }; BiRandomAccessIterator end = { a.end(), b.end() }; std::remove(begin, end, 5); for (auto e : a) { std::cout << e; } std::cout << std::endl; for (auto e : b) { std::cout << e; } return 0; }
  5. Indeed. The benefit if possible, however, is reusing the already existing code (instead of reinventing the hot water) which saves some headaches as well ūüėČ
  6. It is of course pretty easy to write such a container that is not STL compliant; that is not the challenge. What I mean and want is a container and more importantly some associated iterators that are compatible with the functions provided in algorithm (e.g., works with std::remove, std::partition, std::sort, etc.).
  7. Cache coherency and hot paths. The values are packed and aligned in a minimal and optimal way. Furthermore, in most cases the values will be iterated without requiring the corresponding key. The additional boilerplate is for the few cases where the key is required as well or given the key, the corresponding value needs to be obtained. To make this more concrete: let the values be components and the keys be entities as in an ECS design.
  8. Is it possible to implement a STL compliant (e.g., support for std::remove, std::partition, std::sort, etc.) container containing multiple containers? So reordering one container based on some criteria results in the reorganization of other containers as well. Example 1: unidirectional map where the keys are stored in contiguous memory and the values are stored in contiguous memory with a 1-1 correspondence based on the index (e.g., the key at index n and the value at index n constitute a key-value pair of the mapping)? std::vector< KeyT > keys; std::vector< ValueT > values; Example 2: bidirectional map where the keys are stored in contiguous memory and the values are stored in contiguous memory with a 1-1 correspondence based on the index, and an additional mapping from keys to value indices. std::vector< KeyT > keys; std::vector< ValueT > values; std::unordered_map< KeyT, std::size_t > key_value_mapping; The above code fragments are just for illustrating a possible data layout satisfying the constraints. The major problem, however, is that reordering (e.g., removing, erasing, partitioning, sorting) values requires reordering keys and key_value_mapping as well. This basically bottles down to the iterators of the containers. I am not sure if one can implement some custom iterators handling the job for operator*, operator-> and operator[] transparently, since the corresponding elements of different containers are stored at different memory locations. Furthermore, it is not very transparent as well (example).
  9. matt77hias

    C++/WinRT

    But that is also the case for WRL. C++/CX uses language extensions. unmanaged/raw C++ with WRL does not use language extensions. With regard to the C++/CX monstrosity, I would definitely choose C++/WinRT. With regard to unmanaged/raw C++ with WRL, I don't know.
  10. matt77hias

    C++/WinRT

    At first glance, the WRL to WinRT mapping seems just like a bijection. But I have nothing against another COM facade.
  11. matt77hias

    C++/WinRT

    Basically, I like to switch without any performance decreases or functionality limitations. The statement: also indicates greater flexibility with regard to other C++ compilers. MSDN provides some simple guidelines to perform the switch, but these seem to be true small to suffice. Currently, I do not use C++/CX, but just unmanaged C++. But Wikipedia mentions: So this holds for both unmanaged C++ and the weird looking C++/CX (e.g., added ^ syntax elements).
  12. matt77hias

    C++/WinRT

    Do the same restrictions with regard to the swap chain configuration as for UWP hold? Given that I do not intend of creating UWP applications, does it make sense to use WinRT?
  13. matt77hias

    C++/WinRT

    What are the benefits of switching from unmanaged C++ to C++/WinRT? Is it just a "more modern" C++ API or are their functionality and/or performance changes as well?
  14. matt77hias

    Avoid branch instructions

    But this is only about the loop itself, not about the conditional operations regarding each bottle?
  15. matt77hias

    Avoid branch instructions

    But even if you don't bother the API user with such internal state, shouldn't you still have some internal mechanism to perform your own garbage collection at certain points to amortize the cost? Besides not active != dead, right?
  • Advertisement
√ó

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!