Boost logo

Boost :

Subject: Re: [boost] [mixin] New library reminder
From: Klaim - Joël Lamotte (mjklaim_at_[hidden])
Date: 2013-07-13 08:54:15


Just to clarify, when I'm talking about allocators having state, what I
expect is to be able to do something similar to the following.
Note that this is just an example, there are tons of variants of this:

template< class ComponentType >
class ComponentPool
{
public:

     template< typename... Args >
     ComponentType* create( Args... args )
    {
        if( auto free_slot = find_free_slot() )
        {
             *free_slot = make_optional<ComponentType>( args... ); // or
something similar
             return free_slot.get_ptr();
        }
        return nullptr;
    }

   void destroy( ComponentType& component )
   {
       auto slot = find_slot( component );
       assert( slot );
       *slot = none_t; // reset the slot
   }

    template< typename Func >
    void for_all_living_components( Func&& f )
    {
         for( ComponentSlot& slot : m_components )
         {
              if( !slot )
                 continue;
             f( slot.get() );
         }
    }

private:

    using ComponentSlot = boost::optional<ComponentType>; // to know if the
component memory is constructed or not.

    std::array< ComponentSlot, MAX_COMPONENTS_PER_WORLD > m_components;

    ComponentSlot* find_free_slot()
    {
        for( auto& slot : m_components )
           if( !slot )
              return &slot;

        return nullptr;
    }

    ComponentSlot* find_slot( ComponentType& component );
};

class ComponentSystem
{
public:

    class Allocator;

    Allocator allocator(); // proivde an allocator for component that will
use this;

     template< class ComponentType >
     void declare_component_type()
     {
          auto find_it = m_pool_index.find( typeid(ComponentType) );
          if( find_it == end(m_pool_index ) )
          {
               auto new_pool =
std::make_unique<PoolSlotSpecific<ComponentType>>();
               m_pool_index.insert( std::make_pair( typeid(ComponentType),
std::move(new_pool) ) );
          }
     }

    template< class ComponentType >
    ComponentPool<ComponentType>* find_pool()
    {
        auto find_it = m_pool_index.find( typeid(ComponentType) );
        if( find_it != end(m_pool_index ) )
        {
            return find_it->second.get();
        }

       return nullptr;
    }

private:

    struct PoolSlot{};
    template< class ComponentType >
    struct PoolSlotSpecific
    {
         CompomentPool<ComponentType> pool;
    };

    boost::flat_map< std::type_index, std::unique_ptr<PoolSlot> >
m_pool_index;

};

class World
{
public:
     using Entity = boost::mixin::object< ComponentSystem::Allocator >;

     World()
     {
          m_component_system.declare_component_type<PhysicsData>();
          m_component_system.declare_component_type<BrainData>();
          m_component_system.declare_component_type<GraphicData>();
          m_component_system.declare_component_type<AudioData>();
     }

    void update()
    {
         const now = m_clock.now();

         // here we assmue that the data don't communicate or do it through
thread-safe work queues/messages
         auto physics_update = async( /*some_executor,*/ [&] {
                 m_component_system.find_pool( typeid(PhysicsData)
)->for_all_living_components( [&]( PhysicsData& data ) { data.update( now
); } );
         };
         auto brain_update = async( /*some_executor,*/ [&] {
                 m_component_system.find_pool( typeid(BrainData)
)->for_all_living_components( [&]( BrainData& data ) { data.update( now );
} );
         };

         when_all( physics_update, brain_update );

         // for some reasons, cannot be updated in parallel
         m_component_system.find_pool( typeid(GraphicData)
)->for_all_living_components( [&]( GraphicData& data ) { data.update( now
); } );
         m_component_system.find_pool( typeid(AudioData)
)->for_all_living_components( [&]( AudioData& data ) { data.update( now );
} );

    }

    Entity& create_entity( EntityInfo info )
    {
         m_entities.emplace_back();
         auto& new_entity = m_entities.back( m_component_system.allocator()
); // here we pass an allocator which will use this specific instance of
component system.

         using boost::mixin;
         mutate( *new_entity )
             .add<PhysicsData>( info )
             .add<BrainData>( info )
             .add<GraphicData>( info )
             .add<AudioData>( info )
        ;
        return *new_entity;
    }

private:

     Clock m_clock; // some kind of clock implementation, which could be or
not real time (often it's a virtual time)

     ComponentSystem m_component_system;

     boost::stable_vector<Entity> m_entities; // or whatever stable way to
list them

};

class Universe
{
public:

    void update()
    {
         parallel_for( auto world_idx : m_awake_worlds_indice )
         {
              m_world[world_idx].update();
         }
    }

private:
     boost::stable_vector<World> m_worlds;
     std::vector<size_t> m_awake_worlds_indice;
};

-------------------------

This is a raw and a bit complex example but I hope it can clarify my
request.

Joel Lamotte


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk