Code Review Stack Exchange is a question and answer site for peer programmer code reviews. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I'm working on an Entity Component System library. Right know I'm trying to clean up the Entity/Component part (only the code which manage the data).

But I have a class which I think is not really readable. Maybe the implementation can be improved too, but I reduced the implementation to be small and fast, so it's not really why I'm here (but every review is always welcome).

DefaultManager.hpp:

#ifndef  DEFAULT_MANAGER_HPP
#define  DEFAULT_MANAGER_HPP

#include  "Defines.hpp"
#include  "HandleEntry.hpp"

#include  <cstddef>
#include  <vector>
#include  <type_traits>

//!
//! @namespace ECS
//!
namespace  ECS {

  //!
  //! @class DefaultManager
  //!
  template < typename Object,
             bool Shared >

  class  DefaultManager {

    //-----------------------------------------
    //! Allow iterator to access the Pool
    template < ECS_INT Filter >
    friend class iterator;

    //-----------------------------------------
    //! GoogleTest Define
#ifdef  UTESTS
  #include "DefaultManager.utests"
#endif
    //-----------------------------------------


    private:
      static const size_t  BASE_SIZE = 1024;

    //-----------------------------------------
    //! Internal Structs
    private:
      struct ObjectPair {

        std::conditional_t<Shared,
                           HandleEntryShared,
                           HandleEntry>  entry;
        Object  data;
      };

    //-----------------------------------------
    //! Attributs
    protected:
      std::vector<ObjectPair>  m_pool;
      ECS_INT                  m_activeEntryCount;
      ECS_INT                  m_firstFreeEntry;


    //-----------------------------------------
    //! Iterator
    public:
      //! TODO: Need specialization for Filter = 0 (just remove the check on the filter)
      template < ECS_INT Filter = 0 >

      class iterator {

        private:
          size_t                           m_index;
          DefaultManager<Object, Shared>&  m_manager;

        public:
          using difference_type   = ptrdiff_t;
          using size_type         = size_t;
          using value_type        = Object;
          using pointer           = Object*;
          using const_pointer     = const Object*;
          using reference         = Object&;
          using const_reference   = const Object&;

          using iterator_category = std::bidirectional_iterator_tag;

          iterator(DefaultManager<Object, Shared>& manager,
                   size_t index = 0)
            : m_index(index),
              m_manager(manager)
          {}

          iterator& operator++() { while(!isValid()) ++m_index; return *this;}
          iterator& operator++(int) { iterator it = *this; ++(*this); return it;}
          iterator& operator--() { while(!isValid()) --m_index; return *this;}
          iterator& operator--(int) { iterator it = *this; --(*this); return it;}
          bool operator==(iterator other) const {return &m_pool[m_index] == &other.m_pool[m_index];}
          bool operator!=(iterator other) const {return !(*this == other);}
          reference operator*() { return m_manager.m_pool[m_index].data; }

          bool isValid() const {
            Object& obj = m_manager.m_pool[m_index].data;
            return (obj.type() & Filter) == obj.type();
          }
      };


      template < ECS_INT Filter = 0 >
      iterator<Filter>       begin();
      template < ECS_INT Filter = 0 >
      const iterator<Filter> cbegin() { return begin<Filter>(); }

      template < ECS_INT Filter = 0 >
      iterator<Filter>       end();
      template < ECS_INT Filter = 0 >
      const iterator<Filter> cend() { return end<Filter>(); }

    //-----------------------------------------

    //-----------------------------------------
    //! Constructors
    public:
      DefaultManager(size_t allocSize = BASE_SIZE)
        : m_pool(allocSize),
          m_activeEntryCount(0),
          m_firstFreeEntry(0) {
      }

    //-----------------------------------------
    //! Getters and Setters
    public:
      Object&  getObject(const ECS_INT index) {
        return m_pool[index].data;
      }

    //-----------------------------------------
    //! Functions
    protected:
      //!
      //! @brief Used by the manager to extend the pool when it's needed
      //!
      //! Add + BASIC_SIZE to the current pool capacity
      //! Copy current data into the new pool and init the new HandleEntry
      //!
      void  extendPool();

      //! @brief Primitive function used by the manager to release the data from one pool entry
      //!
      //! Put the activate bit to false for the pool entry whose index is specified as a parameter
      //! Update the manager firstFreeEntry, depend on the data released position
      //!
      //! @param index the index where the data to release is positionned.
      void  releaseObject(const ECS_INT index);

    public:
      //! @brief Specifie to the manager the need to reserve some memory space
      //!
      //! Use std::vector.resize() to reserve memory for the pool
      //! Initialize HandleEntry for every ObjectPair reserved
      //!
      //! @warning if you attempt do to so for every DefaultManager, think about change BASIC_SIZE
      //! @param allocSize the size to reserve in bytes
      void  reserveObjectMemory(size_t allocSize);

      //!
      //! @warning Don't use, not sure to be keep
      //!
      void  reset(size_t allocSize = BASE_SIZE);

      //!
      //! @brief Ask the manager to give a data segment from the pool
      //!
      //! Check if the pool has enough memory left (extend if not)
      //! Take the first free entry of the pool and activate the entry
      //! Initialize the data with args
      //!
      //! @param args pack of arguments you need to initialize your Object
      //! @return the index of the activated object
      template < typename ... Args >
      ECS_INT  addNewObject(Args && ... args);

      //!
      //! @brief Ask the manager to assign the object given to another entity
      //!
      //! Just increment the assigned counter of the HandleEntry which correspond
      //!
      //! @warning this function need the Shared from DefaultManager<typename Object, bool Shared> to be true
      //! @param index the index of the already added Object
      void  assignExistingObject(const ECS_INT index);

      //!
      //! @biref Remove need to be called when an entity revoke her access to an Object
      //!
      //! Depend on Shared value
      //! if Shared == true --> Will decrement the assigned counter and if the counter reach zero, release the memory
      //! if Shared == false --> Will just release the memory of the Object selected
      //!
      //! @param index the index of the data that you want remove
      void  removeObject(const ECS_INT index);
  };
}
#include  "DefaultManager.tpp"
#endif

DefaultManager.tpp:

namespace  ECS {

#define TEMPLATE_ARGS  typename Object, \
                       bool Shared

#define CLASS    DefaultManager<Object, Shared>
#define ITERATOR CLASS::iterator<Filter>

  //! Iterator
  //!------------------------------------------

  //!
  //! Begin
  //!
  template < TEMPLATE_ARGS >
    template < ECS_INT Filter >

  ITERATOR  CLASS::begin() {

    ITERATOR it(*this);
    while (! it.is_Valid())
      ++it;
    return std::move(it);
  }

  //!
  //! End
  //!
  template < TEMPLATE_ARGS >
    template < ECS_INT Filter >

  ITERATOR  CLASS::end() {

    ITERATOR it(*this);
    while (! it.is_Valid())
      ++it;
    return std::move(it);
  }

  //!------------------------------------------
  //!------------------------------------------

  //!
  //! ExtendPool
  //!
  template < typename Object,
             bool Shared >

  void   DefaultManager<Object, Shared>::extendPool() {

    size_t i = m_pool.capacity();
    m_pool.reserve(m_pool.capacity() + BASE_SIZE);

    for (;i < m_pool.capacity(); ++i)
      m_pool[i].entry.nextFreeIndex = i;
  }


  //!
  //!  ReleaseObject
  //!
  template < typename Object,
             bool Shared >

  void   DefaultManager<Object, Shared>::releaseObject(const ECS_INT index) {

    if (index < m_firstFreeEntry) {

      m_pool[index].entry.nextFreeIndex = m_firstFreeEntry;
      m_pool[index].entry.active = false;
      m_firstFreeEntry = index;
    }
    else {

      m_pool[index].entry.nextFreeIndex = m_pool[m_firstFreeEntry].entry.nextFreeIndex;
      m_pool[m_firstFreeEntry].entry.nextFreeIndex = index;
      m_pool[index].entry.active = false;
    }
    --m_activeEntryCount;
  }


  //!
  //! ReserveObjectMemory
  //!
  template < typename Object,
             bool Shared >

  void   DefaultManager<Object, Shared>::reserveObjectMemory(size_t allocSize) {

    m_pool.reserve(allocSize);
    for (unsigned int i = 0; i < m_pool.capacity(); ++i)
      m_pool[i].entry.nextFreeIndex = i + 1;
  }


  //!
  //! Reset
  //!
  template < typename Object,
             bool Shared >

  void   DefaultManager<Object, Shared>::reset(size_t allocSize) {

    m_pool.resize(allocSize);
    m_pool.shrink_to_fit();
    for (unsigned int i = 0; i < m_pool.capacity(); ++i)
      m_pool[i].entry.nextFreeIndex = i + 1;

    m_activeEntryCount = 0;
    m_firstFreeEntry = 0;
  }


  //!
  //! AddObject
  //!
  template < typename Object,
             bool Shared >
  template < typename ... Args >

  ECS_INT  DefaultManager<Object, Shared>::addNewObject(Args && ... args) {

    if (m_activeEntryCount >= m_pool.capacity())
      extendPool();

    const ECS_INT index = m_firstFreeEntry;
    HandleEntry& nHandle = m_pool[index].entry;

    m_firstFreeEntry = nHandle.nextFreeIndex;
    nHandle.nextFreeIndex = 0;
    nHandle.active = true;
    m_pool[index].data = Object(std::forward<Args>(args) ...);
    ++m_activeEntryCount;

    return index;
  }


  //!
  //!  AssignExistingObject
  //!
  template < typename Object,
             bool Shared >

  void   DefaultManager<Object, Shared>::assignExistingObject(const ECS_INT index) {

    // Waiting for if constexpr
  }


  //!
  //! RemoveObject
  //!
  template < typename Object,
             bool Shared >

  void  DefaultManager<Object, Shared>::removeObject(const ECS_INT index) {

    //Waiting for if constexpr
    releaseObject(index);
  }
}

The most weird code seems to be the iterator and begin/end function. The whole code is here if you want to see more.

share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.