* Removed namespace macros * Refactoring: split inline code in header and inline file * Continued implementation of system executionmaster
| @@ -1,2 +1,3 @@ | |||
| .vscode/ | |||
| build/ | |||
| build/ | |||
| docu/ | |||
| @@ -0,0 +1,3 @@ | |||
| [submodule "cmake/modules"] | |||
| path = cmake/modules | |||
| url = b3rgmann@git.bergmann89.de:cpp/CmakeModules.git | |||
| @@ -1,15 +1,22 @@ | |||
| # Initialize CMake ################################################################################ | |||
| CMake_Minimum_Required ( VERSION 3.5.1 FATAL_ERROR ) | |||
| If ( NOT CMAKE_BUILD_TYPE ) | |||
| Set ( CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build!" FORCE ) | |||
| EndIf ( ) | |||
| CMake_Minimum_Required ( VERSION 3.5.1 FATAL_ERROR ) | |||
| Set ( CMAKE_CXX_STANDARD 17 ) | |||
| Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ) | |||
| Include ( GlobalCompilerFlags OPTIONAL ) | |||
| Set ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} | |||
| "${CMAKE_CURRENT_SOURCE_DIR}/cmake" | |||
| "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) | |||
| Include ( pedantic OPTIONAL ) | |||
| Include ( cotire OPTIONAL ) | |||
| Set ( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${PEDANTIC_C_FLAGS}" ) | |||
| Set ( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${PEDANTIC_CXX_FLAGS}" ) | |||
| # Tests ########################################################################################### | |||
| Include ( CTest ) | |||
| @@ -22,4 +29,11 @@ Find_Package ( Hana REQUIRED ) | |||
| # Projects ######################################################################################## | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/src ) | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) | |||
| Add_SubDirectory ( ${CMAKE_CURRENT_SOURCE_DIR}/test ) | |||
| # Documentation ################################################################################### | |||
| Configure_File ( ${CMAKE_CURRENT_SOURCE_DIR}/doxygen | |||
| ${CMAKE_CURRENT_BINARY_DIR}/doxygen ) | |||
| Add_Custom_Target ( docu | |||
| doxygen ${CMAKE_CURRENT_BINARY_DIR}/doxygen ) | |||
| @@ -1,52 +0,0 @@ | |||
| Set( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} \ | |||
| -g \ | |||
| -Wall \ | |||
| -Wextra \ | |||
| -Wconversion \ | |||
| -Werror \ | |||
| -Wno-unused-parameter \ | |||
| -Wbad-function-cast \ | |||
| -Wcast-align \ | |||
| -Wcast-qual \ | |||
| -Wconversion \ | |||
| -Wdouble-promotion \ | |||
| -Wfloat-equal \ | |||
| -Wnested-externs \ | |||
| -Wno-attributes \ | |||
| -Wno-builtin-macro-redefined \ | |||
| -Wno-vla \ | |||
| -Wno-pragmas \ | |||
| -Wold-style-definition \ | |||
| -Woverlength-strings \ | |||
| -Wshadow \ | |||
| -Wwrite-strings") | |||
| # -Wlogical-op | |||
| # -Wno-aggressive-loop-optimizations | |||
| # -Wtrampolines" | |||
| Set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ | |||
| -ftemplate-depth=2000 \ | |||
| -g \ | |||
| -Wall \ | |||
| -Wextra \ | |||
| -Wconversion \ | |||
| -Werror \ | |||
| -Wno-reorder \ | |||
| -Wno-unused-parameter \ | |||
| -Wcast-align \ | |||
| -Wcast-qual \ | |||
| -Wconversion \ | |||
| -Wdouble-promotion \ | |||
| -Wfloat-equal \ | |||
| -Wno-attributes \ | |||
| -Wno-builtin-macro-redefined \ | |||
| -Wno-vla \ | |||
| -Wno-pragmas \ | |||
| -Woverlength-strings \ | |||
| -Wshadow \ | |||
| -Wwrite-strings") | |||
| # -Wlogical-op | |||
| # -Wtrampolines | |||
| # -Wno-aggressive-loop-optimizations | |||
| @@ -0,0 +1 @@ | |||
| Subproject commit 9a495487c981774d0cb57478b855d58898c1505b | |||
| @@ -1,6 +1,7 @@ | |||
| #pragma once | |||
| // TODO debug!!! | |||
| #include <string> | |||
| #include <cxxabi.h> | |||
| template<typename T> | |||
| @@ -18,116 +19,6 @@ struct type_helper | |||
| #include <boost/hana.hpp> | |||
| #include <boost/hana/ext/std/tuple.hpp> | |||
| #define ecs_define_namespace_beg(parent, name) \ | |||
| parent { \ | |||
| namespace name | |||
| #define ecs_define_namespace_end(parent) \ | |||
| } \ | |||
| parent | |||
| #define beg_namespace_ecs namespace ecs | |||
| #define end_namespace_ecs | |||
| #define beg_namespace_ecs_context ecs_define_namespace_beg(beg_namespace_ecs, context) | |||
| #define end_namespace_ecs_context ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_core ecs_define_namespace_beg(beg_namespace_ecs, core) | |||
| #define end_namespace_ecs_core ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_core_component ecs_define_namespace_beg(beg_namespace_ecs_core, component) | |||
| #define end_namespace_ecs_core_component ecs_define_namespace_end(end_namespace_ecs_core) | |||
| #define beg_namespace_ecs_core_component_storage ecs_define_namespace_beg(beg_namespace_ecs_core_component, storage) | |||
| #define end_namespace_ecs_core_component_storage ecs_define_namespace_end(end_namespace_ecs_core_component) | |||
| #define beg_namespace_ecs_core_entity ecs_define_namespace_beg(beg_namespace_ecs_core, entity) | |||
| #define end_namespace_ecs_core_entity ecs_define_namespace_end(end_namespace_ecs_core) | |||
| #define beg_namespace_ecs_core_entity_storage ecs_define_namespace_beg(beg_namespace_ecs_core_entity, storage) | |||
| #define end_namespace_ecs_core_entity_storage ecs_define_namespace_end(end_namespace_ecs_core_entity) | |||
| #define beg_namespace_ecs_core_mp ecs_define_namespace_beg(beg_namespace_ecs_core, mp) | |||
| #define end_namespace_ecs_core_mp ecs_define_namespace_end(end_namespace_ecs_core) | |||
| #define beg_namespace_ecs_core_mp_list ecs_define_namespace_beg(beg_namespace_ecs_core_mp, list) | |||
| #define end_namespace_ecs_core_mp_list ecs_define_namespace_end(end_namespace_ecs_core_mp) | |||
| #define beg_namespace_ecs_core_mp_option_map ecs_define_namespace_beg(beg_namespace_ecs_core_mp, option_map) | |||
| #define end_namespace_ecs_core_mp_option_map ecs_define_namespace_end(end_namespace_ecs_core_mp) | |||
| #define beg_namespace_ecs_core_mp_tag ecs_define_namespace_beg(beg_namespace_ecs_core_mp, tag) | |||
| #define end_namespace_ecs_core_mp_tag ecs_define_namespace_end(end_namespace_ecs_core_mp) | |||
| #define beg_namespace_ecs_core_system ecs_define_namespace_beg(beg_namespace_ecs_core, system) | |||
| #define end_namespace_ecs_core_system ecs_define_namespace_end(end_namespace_ecs_core) | |||
| #define beg_namespace_ecs_core_parallelism ecs_define_namespace_beg(beg_namespace_ecs_core_system, parallelism) | |||
| #define end_namespace_ecs_core_parallelism ecs_define_namespace_end(end_namespace_ecs_core_system) | |||
| #define beg_namespace_ecs_core_system_scheduler ecs_define_namespace_beg(beg_namespace_ecs_core_system, scheduler) | |||
| #define end_namespace_ecs_core_system_scheduler ecs_define_namespace_end(end_namespace_ecs_core_system) | |||
| #define beg_namespace_ecs_core_system_storage ecs_define_namespace_beg(beg_namespace_ecs_core_system, storage) | |||
| #define end_namespace_ecs_core_system_storage ecs_define_namespace_end(end_namespace_ecs_core_system) | |||
| #define beg_namespace_ecs_core_utils ecs_define_namespace_beg(beg_namespace_ecs_core, utils) | |||
| #define end_namespace_ecs_core_utils ecs_define_namespace_end(end_namespace_ecs_core) | |||
| #define beg_namespace_ecs_settings ecs_define_namespace_beg(beg_namespace_ecs, settings) | |||
| #define end_namespace_ecs_settings ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_settings_entity_storage ecs_define_namespace_beg(beg_namespace_ecs_settings, entity_storage) | |||
| #define end_namespace_ecs_settings_entity_storage ecs_define_namespace_end(end_namespace_ecs_settings) | |||
| #define beg_namespace_ecs_settings_refresh_parallelism ecs_define_namespace_beg(beg_namespace_ecs_settings, refresh_parallelism) | |||
| #define end_namespace_ecs_settings_refresh_parallelism ecs_define_namespace_end(end_namespace_ecs_settings) | |||
| #define beg_namespace_ecs_settings_scheduler ecs_define_namespace_beg(beg_namespace_ecs_settings, scheduler) | |||
| #define end_namespace_ecs_settings_scheduler ecs_define_namespace_end(end_namespace_ecs_settings) | |||
| #define beg_namespace_ecs_settings_system_storage ecs_define_namespace_beg(beg_namespace_ecs_settings, system_storage) | |||
| #define end_namespace_ecs_settings_system_storage ecs_define_namespace_end(end_namespace_ecs_settings) | |||
| #define beg_namespace_ecs_signature ecs_define_namespace_beg(beg_namespace_ecs, signature) | |||
| #define end_namespace_ecs_signature ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_signature_component ecs_define_namespace_beg(beg_namespace_ecs_signature, component) | |||
| #define end_namespace_ecs_signature_component ecs_define_namespace_end(end_namespace_ecs_signature) | |||
| #define beg_namespace_ecs_signature_component_storage ecs_define_namespace_beg(beg_namespace_ecs_signature_component, storage) | |||
| #define end_namespace_ecs_signature_component_storage ecs_define_namespace_end(end_namespace_ecs_signature_component) | |||
| #define beg_namespace_ecs_signature_system ecs_define_namespace_beg(beg_namespace_ecs_signature, system) | |||
| #define end_namespace_ecs_signature_system ecs_define_namespace_end(end_namespace_ecs_signature) | |||
| #define beg_namespace_ecs_signature_system_output ecs_define_namespace_beg(beg_namespace_ecs_signature_system, output) | |||
| #define end_namespace_ecs_signature_system_output ecs_define_namespace_end(end_namespace_ecs_signature_system) | |||
| #define beg_namespace_ecs_signature_system_parallelism ecs_define_namespace_beg(beg_namespace_ecs_signature_system, parallelism) | |||
| #define end_namespace_ecs_signature_system_parallelism ecs_define_namespace_end(end_namespace_ecs_signature_system) | |||
| #define beg_namespace_ecs_signature_list ecs_define_namespace_beg(beg_namespace_ecs, signature_list) | |||
| #define end_namespace_ecs_signature_list ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_signature_list_component ecs_define_namespace_beg(beg_namespace_ecs_signature_list, component) | |||
| #define end_namespace_ecs_signature_list_component ecs_define_namespace_end(end_namespace_ecs_signature_list) | |||
| #define beg_namespace_ecs_signature_list_system ecs_define_namespace_beg(beg_namespace_ecs_signature_list, system) | |||
| #define end_namespace_ecs_signature_list_system ecs_define_namespace_end(end_namespace_ecs_signature_list) | |||
| #define beg_namespace_ecs_system_execution_adapter ecs_define_namespace_beg(beg_namespace_ecs, system_execution_adapter) | |||
| #define end_namespace_ecs_system_execution_adapter ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_tag ecs_define_namespace_beg(beg_namespace_ecs, tag) | |||
| #define end_namespace_ecs_tag ecs_define_namespace_end(end_namespace_ecs) | |||
| #define beg_namespace_ecs_tag_component ecs_define_namespace_beg(beg_namespace_ecs_tag, component) | |||
| #define end_namespace_ecs_tag_component ecs_define_namespace_end(end_namespace_ecs_tag) | |||
| #define beg_namespace_ecs_tag_system ecs_define_namespace_beg(beg_namespace_ecs_tag, system) | |||
| #define end_namespace_ecs_tag_system ecs_define_namespace_end(end_namespace_ecs_tag) | |||
| namespace ecs | |||
| { | |||
| @@ -3,4 +3,6 @@ | |||
| #include "./context/base.h" | |||
| #include "./context/context.h" | |||
| #include "./context/defer_proxy.h" | |||
| #include "./context/step_proxy.h" | |||
| #include "./context/step_proxy.h" | |||
| #include "./context/base.inl" | |||
| @@ -14,11 +14,17 @@ | |||
| ._##name(std::forward<T_args>(args)...))) \ | |||
| { return this->_##name(std::forward<T_args>(args)...); } | |||
| beg_namespace_ecs_context | |||
| { | |||
| namespace ecs { | |||
| namespace context { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * the base ecs context implementation | |||
| * | |||
| * @tparam T_settings settings type the environment is configured with | |||
| */ | |||
| template<typename T_settings> | |||
| struct base_t | |||
| { | |||
| @@ -28,8 +34,8 @@ beg_namespace_ecs_context | |||
| protected: | |||
| using context_type = ::ecs::context::type<settings_type>; | |||
| using component_manager_type = core::component::manager<settings_type>; | |||
| using chunk_meta_data_type = typename component_manager_type::chunk_meta_data_type; | |||
| using entity_storage_type = decltype((settings_type { }).entity_storage()(settings_type { }, hana::type_c<chunk_meta_data_type>)); | |||
| using storage_meta_data_type = typename component_manager_type::storage_meta_data_type; | |||
| using entity_storage_type = decltype((settings_type { }).entity_storage()(settings_type { }, hana::type_c<storage_meta_data_type>)); | |||
| using entity_handle_type = typename entity_storage_type::entity_handle_type; | |||
| using system_storage_type = decltype((settings_type { }).system_storage()(settings_type { }, hana::type_c<entity_handle_type>)); | |||
| using scheduler_type = decltype((settings_type { }).scheduler()(settings_type { }, std::declval<context_type&>())); | |||
| @@ -41,179 +47,233 @@ beg_namespace_ecs_context | |||
| using handle_vector_type = std::vector<handle_type>; | |||
| protected: | |||
| context_type& _context; | |||
| context_type& _context; //!< reference to its own child class | |||
| component_manager_type _components; | |||
| entity_storage_type _entities; | |||
| system_storage_type _systems; | |||
| scheduler_type _scheduler; | |||
| component_manager_type _components; //!< object to store and manage all components | |||
| entity_storage_type _entities; //!< object to store and manage all entities | |||
| system_storage_type _systems; //!< object to store and manage all systems | |||
| scheduler_type _scheduler; //!< scheduler to execute the systems | |||
| core::utils::thread_pool _thread_pool; | |||
| core::utils::thread_pool _thread_pool; //!< thread pool to execute tasks parallel | |||
| handle_vector_type _to_match; | |||
| handle_vector_type _to_kill; | |||
| handle_vector_type _to_match; //!< handles of entities that were created in the last tick | |||
| handle_vector_type _to_kill; //!< handles of entities that were destroyed in the last tick | |||
| public: | |||
| inline base_t(context_type& p_context) | |||
| : _context (p_context) | |||
| , _components () | |||
| , _entities ((settings_type { }).entity_storage()(settings_type { }, hana::type_c<chunk_meta_data_type>)) | |||
| , _systems ((settings_type { }).system_storage()(settings_type { }, hana::type_c<entity_handle_type>)) | |||
| , _scheduler ((settings_type { }).scheduler()(settings_type { }, p_context)) | |||
| , _thread_pool (std::thread::hardware_concurrency() != 0 | |||
| ? std::thread::hardware_concurrency() | |||
| : 8 ) | |||
| { } | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param p_context reference to its own child class | |||
| */ | |||
| inline base_t(context_type& p_context); | |||
| protected: /* misc */ | |||
| /** | |||
| * execute the given function in the thread pool | |||
| * | |||
| * @tparam T_func type of the function to execute | |||
| * | |||
| * @param func function to execute | |||
| */ | |||
| template<typename T_func> | |||
| inline decltype(auto) _post_in_thread_pool(T_func&& func) | |||
| { | |||
| return _thread_pool.post(std::forward<T_func>(func)); | |||
| } | |||
| inline void _post_in_thread_pool(T_func&& func); | |||
| protected: /* entity */ | |||
| /** | |||
| * create a new entity | |||
| * | |||
| * @tparam T_args argument types to pass to the entity manager | |||
| * | |||
| * @param args arguments to pass to the entity manager | |||
| * | |||
| * @return handle of the created entity | |||
| */ | |||
| template<typename... T_args> | |||
| inline handle_type _create_entity(T_args&&... args) | |||
| { | |||
| _to_match.emplace_back(_entities.claim(std::forward<T_args>(args)...)); | |||
| return _to_match.back(); | |||
| } | |||
| inline void _kill_entity(const handle_type& handle) | |||
| { | |||
| _to_kill.emplace_back(handle); | |||
| } | |||
| inline bool _is_alive(const handle_type& handle) const | |||
| { | |||
| return _entities.is_valid(handle); | |||
| } | |||
| inline decltype(auto) _create_entity(T_args&&... args); | |||
| /** | |||
| * kill an entity | |||
| * | |||
| * @param handle handle of the entity to kill | |||
| */ | |||
| inline void _kill_entity(const handle_type& handle); | |||
| /** | |||
| * check if an entity is alive | |||
| * | |||
| * @param handle handle of the entity to check | |||
| * | |||
| * @retval TRUE if the entity is alive | |||
| * @retval FALSE if the entity is not alive | |||
| */ | |||
| inline bool _is_alive(const handle_type& handle) const; | |||
| protected: /* component */ | |||
| /** | |||
| * add a new component to an entity | |||
| * | |||
| * @tparam T_component_tag component tag type to add to the entity | |||
| * | |||
| * @param handle handle of the entity | |||
| * @param ct tag of component to add | |||
| * | |||
| * @return reference to the created component | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) _add_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| _to_match.emplace_back(handle); | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& cmd = meta.chunk_meta_data(); | |||
| auto& c = _components.add(ct, handle, cmd); | |||
| auto& bitset = meta.bitset(); | |||
| bitset.set_component(ct, true); | |||
| return c; | |||
| } | |||
| inline decltype(auto) _add_component(const handle_type& handle, T_component_tag ct); | |||
| /** | |||
| * check if an entity has an specific component | |||
| * | |||
| * @tparam T_component_tag component tag type to check for | |||
| * | |||
| * @param handle handle of entity to check | |||
| * @param ct component tag to check | |||
| * | |||
| * @retval TRUE if given entity has the requested component | |||
| * @retval FALSE if the given entity does not have the requested component | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) _has_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& bitset = meta.bitset(); | |||
| return bitset.has_component(ct); | |||
| } | |||
| inline decltype(auto) _has_component(const handle_type& handle, T_component_tag ct); | |||
| /** | |||
| * get a reference to the requested component | |||
| * | |||
| * @tparam T_component_tag component tag type to get | |||
| * | |||
| * @param handle handle of the entity to get the component for | |||
| * @param ct component tag to get the component for | |||
| * | |||
| * @return reference to the requested component | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) _get_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& bitset = meta.bitset(); | |||
| auto& cmd = meta.chunk_meta_data(); | |||
| if (!bitset.has_component(ct)) | |||
| throw std::invalid_argument("entity does not contain the requested compnent"); | |||
| return _components.get(ct, handle, cmd); | |||
| } | |||
| inline decltype(auto) _get_component(const handle_type& handle, T_component_tag ct); | |||
| /** | |||
| * remove a component from an entity | |||
| * | |||
| * @tparam T_component_tag component tag type to remove | |||
| * | |||
| * @param handle handle of the entity to remove component at | |||
| * @param ct component tag to remove component for | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) _remove_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| _to_match.emplace_back(handle); | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& bitset = meta.bitset(); | |||
| bitset.set_component(ct, false); | |||
| } | |||
| inline void _remove_component(const handle_type& handle, T_component_tag ct); | |||
| protected: /* systems */ | |||
| /** | |||
| * get the reference to a system instance by the system tag | |||
| * | |||
| * @tparam T_system_tag system tag type to get system instance for | |||
| * | |||
| * @param st system tag to get system instance for | |||
| * | |||
| * @return reference to the system instance | |||
| */ | |||
| template<typename T_system_tag> | |||
| inline auto& _instance_by_tag(T_system_tag st) | |||
| { | |||
| return _systems.instance_by_tag(st); | |||
| } | |||
| inline auto& _instance_by_tag(T_system_tag st); | |||
| /** | |||
| * get the reference to a system instance by it's index | |||
| * | |||
| * @tparam T_index index type to get system instance for | |||
| * | |||
| * @param index index to get system instance for | |||
| * | |||
| * @return reference to the system instance | |||
| */ | |||
| template<typename T_index> | |||
| inline auto& _instance_by_index(T_index) | |||
| { | |||
| return _systems.instance_by_index(T_index { }); | |||
| } | |||
| inline auto& _instance_by_index(T_index index); | |||
| /** | |||
| * get the reference of a system by the system tag | |||
| * | |||
| * @tparam T_system_tag system tag type to get system for | |||
| * | |||
| * @param st system tag to get system for | |||
| * | |||
| * @return reference to the system | |||
| */ | |||
| template<typename T_system_tag> | |||
| inline auto& _system_by_tag(T_system_tag st) | |||
| { | |||
| return _systems.instance_by_tag(st).system(); | |||
| } | |||
| inline auto& _system_by_tag(T_system_tag st); | |||
| /** | |||
| * get the reference to a system by it's index | |||
| * | |||
| * @tparam T_index index type to get system for | |||
| * | |||
| * @param index index to get system for | |||
| * | |||
| * @return reference to the system | |||
| */ | |||
| template<typename T_index> | |||
| inline auto& _system_by_index(T_index) | |||
| { | |||
| return _systems.instance_by_index(T_index { }).system(); | |||
| } | |||
| inline auto& _system_by_index(T_index index); | |||
| /** | |||
| * execute the given function for each system sequential | |||
| * | |||
| * @tparam T_func function type to execute for each system | |||
| * | |||
| * @param func function to execute for each system | |||
| */ | |||
| template<typename T_func> | |||
| inline void _for_systems_sequential(T_func&& func) | |||
| { | |||
| _systems.for_each(std::forward<T_func>(func)); | |||
| } | |||
| inline void _for_systems_sequential(T_func&& func); | |||
| /** | |||
| * execute the given function for each system in parallel | |||
| * | |||
| * @tparam T_func function type to execute for each system | |||
| * | |||
| * @param func function to execute for each system | |||
| */ | |||
| template<typename T_func> | |||
| inline void _for_systems_parallel(T_func&& func) | |||
| { | |||
| core::utils::counter_blocker counter(_systems.size()); | |||
| counter.execute_and_wait_until_zero([this, &counter, func = std::forward<T_func>(func)]{ | |||
| _systems.for_each([this, &counter, &func](auto& instance){ | |||
| this->_post_in_thread_pool([&counter, &func, &instance]{ | |||
| ecs_make_scope_guard([&counter](){ | |||
| counter.decrement_and_notify_one(); | |||
| }); | |||
| func(instance); | |||
| }); | |||
| }); | |||
| }); | |||
| } | |||
| inline void _for_systems_parallel(T_func&& func); | |||
| /** | |||
| * Execute function for each system, depending on the refresh_parallelism option of the settings object | |||
| * it will be executed sequentially or in parallel. | |||
| * | |||
| * @tparam T_func function type to execute for each system | |||
| * | |||
| * @param func function to execute for each system | |||
| */ | |||
| template<typename T_func> | |||
| inline void _for_systems_dispatch(T_func&& func) | |||
| { | |||
| hana::eval_if( | |||
| (settings_type { }).refresh_parallelism(), | |||
| [this, func=std::forward<T_func>(func)](auto _) { | |||
| _(this)->_for_systems_parallel(func); | |||
| }, | |||
| [this, func=std::forward<T_func>(func)](auto _) { | |||
| _(this)->_for_systems_sequential(func); | |||
| }); | |||
| } | |||
| inline void _for_systems_dispatch(T_func&& func); | |||
| protected: /* execute */ | |||
| /** | |||
| * run the scheduler for the passed system tags (if no tags are passed, all systems are executed) | |||
| * | |||
| * @tparam T_system_tags system tag types to execute scheduler for | |||
| * | |||
| * @param sts system tags to execute scheduler for | |||
| * | |||
| * @return functor to run system execution adapter with | |||
| */ | |||
| template<typename... T_system_tags> | |||
| inline decltype(auto) _execute_systems(T_system_tags&&... sts) noexcept | |||
| { | |||
| auto tags = make_system_tag_list(std::forward<T_system_tags>(sts)...); | |||
| return [tags, this](auto&&... fns) { | |||
| auto os = hana::overload_linearly(std::forward<decltype(fns)>(fns)...); | |||
| this->_scheduler.execute(tags, os); | |||
| }; | |||
| } | |||
| inline decltype(auto) _execute_systems(T_system_tags&&... sts) noexcept; | |||
| private: | |||
| private: /* static */ | |||
| /** | |||
| * create a list of system tags from the passed tags (if no tag is passed all system tags are returned) | |||
| * | |||
| * @tparam T_system_tags system tag types to create list for | |||
| * | |||
| * @param tags system tags to create list for | |||
| * | |||
| * @return list of system tags | |||
| */ | |||
| template<typename... T_system_tags> | |||
| static constexpr decltype(auto) make_system_tag_list(T_system_tags&&... tags) noexcept | |||
| { | |||
| auto list = core::mp::list::make(std::forward<T_system_tags>(tags)...); | |||
| return hana::if_( | |||
| hana::equal(hana::size(list), hana::size_c<0>), | |||
| (settings_type { }).system_signatures().tags(), | |||
| list); | |||
| } | |||
| static constexpr decltype(auto) make_system_tag_list(T_system_tags&&... tags) noexcept; | |||
| }; | |||
| } | |||
| } | |||
| end_namespace_ecs_context | |||
| } } | |||
| @@ -0,0 +1,210 @@ | |||
| #pragma once | |||
| #include <ecs/context/base.h> | |||
| namespace ecs { | |||
| namespace context { | |||
| namespace detail { | |||
| /* constructor */ | |||
| template<typename T_settings> | |||
| inline base_t<T_settings> | |||
| ::base_t(context_type& p_context) | |||
| : _context (p_context) | |||
| , _components () | |||
| , _entities ((settings_type { }).entity_storage()(settings_type { }, hana::type_c<storage_meta_data_type>)) | |||
| , _systems ((settings_type { }).system_storage()(settings_type { }, hana::type_c<entity_handle_type>)) | |||
| , _scheduler ((settings_type { }).scheduler()(settings_type { }, p_context)) | |||
| , _thread_pool (std::thread::hardware_concurrency() != 0 | |||
| ? std::thread::hardware_concurrency() | |||
| : 8 ) | |||
| { } | |||
| /* misc */ | |||
| template<typename T_settings> | |||
| template<typename T_func> | |||
| inline void base_t<T_settings> | |||
| ::_post_in_thread_pool(T_func&& func) | |||
| { | |||
| _thread_pool.post(std::forward<T_func>(func)); | |||
| } | |||
| /* entity */ | |||
| template<typename T_settings> | |||
| template<typename... T_args> | |||
| inline decltype(auto) base_t<T_settings> | |||
| ::_create_entity(T_args&&... args) | |||
| { | |||
| _to_match.emplace_back(_entities.claim(std::forward<T_args>(args)...)); | |||
| return _to_match.back(); | |||
| } | |||
| template<typename T_settings> | |||
| inline void base_t<T_settings> | |||
| ::_kill_entity(const handle_type& handle) | |||
| { | |||
| _to_kill.emplace_back(handle); | |||
| } | |||
| template<typename T_settings> | |||
| inline bool base_t<T_settings> | |||
| ::_is_alive(const handle_type& handle) const | |||
| { | |||
| return _entities.is_valid(handle); | |||
| } | |||
| /* components */ | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) base_t<T_settings> | |||
| ::_add_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| _to_match.emplace_back(handle); | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& cmd = meta.storage_meta_data(); | |||
| auto& c = _components.add(ct, handle, cmd); | |||
| auto& bitset = meta.bitset(); | |||
| bitset.set_component(ct, true); | |||
| return c; | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) base_t<T_settings> | |||
| ::_has_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& bitset = meta.bitset(); | |||
| return bitset.has_component(ct); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) base_t<T_settings> | |||
| ::_get_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& bitset = meta.bitset(); | |||
| auto& cmd = meta.storage_meta_data(); | |||
| if (!bitset.has_component(ct)) | |||
| throw std::invalid_argument("entity does not contain the requested compnent"); | |||
| return _components.get(ct, handle, cmd); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| inline void base_t<T_settings> | |||
| ::_remove_component(const handle_type& handle, T_component_tag ct) | |||
| { | |||
| _to_match.emplace_back(handle); | |||
| auto& meta = _entities.meta_data(handle); | |||
| auto& bitset = meta.bitset(); | |||
| bitset.set_component(ct, false); | |||
| } | |||
| /* systems */ | |||
| template<typename T_settings> | |||
| template<typename T_system_tag> | |||
| inline auto& base_t<T_settings> | |||
| ::_instance_by_tag(T_system_tag st) | |||
| { | |||
| return _systems.instance_by_tag(st); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_index> | |||
| inline auto& base_t<T_settings> | |||
| ::_instance_by_index(T_index index) | |||
| { | |||
| return _systems.instance_by_index(T_index { }); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_system_tag> | |||
| inline auto& base_t<T_settings> | |||
| ::_system_by_tag(T_system_tag st) | |||
| { | |||
| return _systems.instance_by_tag(st).system(); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_index> | |||
| inline auto& base_t<T_settings> | |||
| ::_system_by_index(T_index) | |||
| { | |||
| return _systems.instance_by_index(T_index { }).system(); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_func> | |||
| inline void base_t<T_settings> | |||
| ::_for_systems_sequential(T_func&& func) | |||
| { | |||
| _systems.for_each(std::forward<T_func>(func)); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_func> | |||
| inline void base_t<T_settings> | |||
| ::_for_systems_parallel(T_func&& func) | |||
| { | |||
| core::utils::counter_blocker counter(_systems.size()); | |||
| counter.execute_and_wait_until_zero([this, &counter, func = std::forward<T_func>(func)]{ | |||
| _systems.for_each([this, &counter, &func](auto& instance){ | |||
| this->_post_in_thread_pool([&counter, &func, &instance]{ | |||
| ecs_make_scope_guard([&counter](){ | |||
| counter.decrement(); | |||
| }); | |||
| func(instance); | |||
| }); | |||
| }); | |||
| }); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_func> | |||
| inline void base_t<T_settings> | |||
| ::_for_systems_dispatch(T_func&& func) | |||
| { | |||
| hana::eval_if( | |||
| (settings_type { }).refresh_parallelism(), | |||
| [this, func=std::forward<T_func>(func)](auto _) { | |||
| _(this)->_for_systems_parallel(func); | |||
| }, | |||
| [this, func=std::forward<T_func>(func)](auto _) { | |||
| _(this)->_for_systems_sequential(func); | |||
| }); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename... T_system_tags> | |||
| inline decltype(auto) base_t<T_settings> | |||
| ::_execute_systems(T_system_tags&&... sts) noexcept | |||
| { | |||
| auto tags = make_system_tag_list(std::forward<T_system_tags>(sts)...); | |||
| return [tags, this](auto&&... fns) { | |||
| auto os = hana::overload_linearly(std::forward<decltype(fns)>(fns)...); | |||
| this->_scheduler.execute(tags, os); | |||
| }; | |||
| } | |||
| /* static */ | |||
| template<typename T_settings> | |||
| template<typename... T_system_tags> | |||
| constexpr decltype(auto) base_t<T_settings> | |||
| ::make_system_tag_list(T_system_tags&&... tags) noexcept | |||
| { | |||
| auto list = core::mp::list::make(std::forward<T_system_tags>(tags)...); | |||
| return hana::if_( | |||
| hana::equal(hana::size(list), hana::size_c<0>), | |||
| (settings_type { }).system_signatures().tags(), | |||
| list); | |||
| } | |||
| } } } | |||
| @@ -2,17 +2,16 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_context | |||
| { | |||
| namespace ecs { | |||
| namespace context { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<typename T_settings> | |||
| struct context_t; | |||
| } | |||
| template<typename T_settings> | |||
| using type = __impl::context_t<T_settings>; | |||
| using type = detail::context_t<T_settings>; | |||
| } | |||
| end_namespace_ecs_context | |||
| } } | |||
| @@ -6,10 +6,10 @@ | |||
| #include "./context.fwd.h" | |||
| #include "./step_proxy.h" | |||
| beg_namespace_ecs_context | |||
| { | |||
| namespace ecs { | |||
| namespace context { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<typename T_settings> | |||
| struct context_t | |||
| @@ -60,9 +60,8 @@ beg_namespace_ecs_context | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make = __impl::make_t { }; | |||
| constexpr decltype(auto) make = detail::make_t { }; | |||
| constexpr decltype(auto) make_uptr = __impl::make_uptr_t { }; | |||
| constexpr decltype(auto) make_uptr = detail::make_uptr_t { }; | |||
| } | |||
| end_namespace_ecs_context | |||
| } } | |||
| @@ -4,11 +4,14 @@ | |||
| #include "./base.h" | |||
| beg_namespace_ecs_context | |||
| { | |||
| namespace ecs { | |||
| namespace context { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * proxy class to make entity, component and system management visible to users of the context | |||
| */ | |||
| template<typename T_settings> | |||
| struct defer_proxy_t | |||
| : public base_t<T_settings> | |||
| @@ -45,5 +48,4 @@ beg_namespace_ecs_context | |||
| }; | |||
| } | |||
| } | |||
| end_namespace_ecs_context | |||
| } } | |||
| @@ -4,11 +4,14 @@ | |||
| #include "./defer_proxy.h" | |||
| beg_namespace_ecs_context | |||
| { | |||
| namespace ecs { | |||
| namespace context { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * proxy class to make system execution visible to users of the context | |||
| */ | |||
| template<typename T_settings> | |||
| struct step_proxy_t | |||
| : public defer_proxy_t<T_settings> | |||
| @@ -31,5 +34,4 @@ beg_namespace_ecs_context | |||
| }; | |||
| } | |||
| } | |||
| end_namespace_ecs_context | |||
| } } | |||
| @@ -1,4 +1,6 @@ | |||
| #pragma once | |||
| #include "./component/manager.h" | |||
| #include "./component/storage.h" | |||
| #include "./component/storage.h" | |||
| #include "./component/manager.inl" | |||
| @@ -5,128 +5,189 @@ | |||
| #include <ecs/core/mp/core/decay.h> | |||
| #include <ecs/core/mp/core/wrap.h> | |||
| beg_namespace_ecs_core_component | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| /* make_chunk_tuple */ | |||
| /* make_storage_tuple */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct make_chunk_tuple_t | |||
| /** | |||
| * predicate type to make a tuple of the different component storages from the settings | |||
| */ | |||
| struct make_storage_tuple_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_settings settings type | |||
| * | |||
| * @param settings settings to build tuple from | |||
| * | |||
| * @return tuple of component storages (@ref storage_iterface_t) | |||
| */ | |||
| template<typename T_settings> | |||
| constexpr decltype(auto) operator()(T_settings s) const noexcept | |||
| { | |||
| auto cls = s.component_signatures(); | |||
| return hana::unpack( | |||
| hana::transform(cls.items, [s](auto x){ | |||
| auto csig = mp::unwrap(x); | |||
| return csig.storage()(s, csig.tags());; | |||
| }), | |||
| hana::make_tuple); | |||
| } | |||
| constexpr decltype(auto) operator()(T_settings settings) const noexcept; | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make_chunk_tuple = __impl::make_chunk_tuple_t { }; | |||
| constexpr decltype(auto) make_storage_tuple = detail::make_storage_tuple_t { }; | |||
| /* make_chunk_meta_data */ | |||
| /* make_storage_meta_data */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct make_chunk_meta_data_t | |||
| /** | |||
| * predicate type to make a meta data object of a given storage | |||
| */ | |||
| struct make_storage_meta_data_t | |||
| { | |||
| template<typename T_chunk> | |||
| constexpr decltype(auto) operator()(T_chunk&& chunk) const noexcept | |||
| { | |||
| using chunk_type = mp::decay_t<decltype(chunk)>; | |||
| using meta_data_type = typename chunk_type::meta_data_type; | |||
| return meta_data_type { }; | |||
| } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_storage storage type to make the meta data object for | |||
| * | |||
| * @param storage storage to make the meta data object for | |||
| * | |||
| * @return meta data object for the given storage | |||
| */ | |||
| template<typename T_storage> | |||
| constexpr decltype(auto) operator()(T_storage&& storage) const noexcept; | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make_chunk_meta_data = __impl::make_chunk_meta_data_t { }; | |||
| constexpr decltype(auto) make_storage_meta_data = detail::make_storage_meta_data_t { }; | |||
| /* make_chunk_meta_data_tuple */ | |||
| /* make_storage_meta_data_tuple */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct make_chunk_meta_data_tuple_t | |||
| /** | |||
| * predicate to create a tuple of meta data from the settings | |||
| */ | |||
| struct make_storage_meta_data_tuple_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_settings settings type to create the meta data tuple for | |||
| * | |||
| * @param settings settings to create the meta data tuple for | |||
| * | |||
| * @return meta data tuple for the given settings | |||
| */ | |||
| template<typename T_settings> | |||
| constexpr decltype(auto) operator()(T_settings s) const noexcept | |||
| { | |||
| using chunk_tuple_type = decltype(make_chunk_tuple(s)); | |||
| using chunk_meta_data_tuple_type = decltype(hana::transform( | |||
| std::declval<chunk_tuple_type>(), | |||
| make_chunk_meta_data)); | |||
| return chunk_meta_data_tuple_type { }; | |||
| } | |||
| constexpr decltype(auto) operator()(T_settings settings) const noexcept; | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make_chunk_meta_data_tuple = __impl::make_chunk_meta_data_tuple_t { }; | |||
| constexpr decltype(auto) make_storage_meta_data_tuple = detail::make_storage_meta_data_tuple_t { }; | |||
| /* chunk_contains_tag */ | |||
| /* storage_contains_tag */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to check if the given storage contains the given component tag | |||
| * | |||
| * @tparam T_component_tag component tag type to check | |||
| */ | |||
| template<typename T_component_tag> | |||
| struct chunk_contains_tag_t | |||
| struct storage_contains_tag_t | |||
| { | |||
| template<typename T_chunk> | |||
| constexpr decltype(auto) operator()(T_chunk&&) const noexcept | |||
| { | |||
| using chunk_type = mp::decay_t<T_chunk>; | |||
| using component_tag_list_type = typename chunk_type::component_tag_list_type; | |||
| return hana::contains(component_tag_list_type { }, T_component_tag { }); | |||
| } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_storage storage type to check | |||
| * | |||
| * @param storage storage object to check | |||
| * | |||
| * @retval integral constant TRUE if the given component tag is managed by the given storage | |||
| * @retval integral constant FALSE if the given component tag is not managed by the given storage | |||
| */ | |||
| template<typename T_storage> | |||
| constexpr decltype(auto) operator()(T_storage&& storage) const noexcept; | |||
| }; | |||
| } | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) chunk_contains_tag = __impl::chunk_contains_tag_t<T_component_tag> { }; | |||
| constexpr decltype(auto) storage_contains_tag = detail::storage_contains_tag_t<T_component_tag> { }; | |||
| /* manager */ | |||
| /** | |||
| * component manager | |||
| * | |||
| * @tparam T_settings settings to build the component manager for | |||
| */ | |||
| template<typename T_settings> | |||
| struct manager | |||
| { | |||
| public: | |||
| using chunk_tuple_type = decltype(make_chunk_tuple(T_settings { })); | |||
| using chunk_meta_data_type = decltype(make_chunk_meta_data_tuple(T_settings { })); | |||
| /** tuple of the different storages */ | |||
| using storage_tuple_type = decltype(make_storage_tuple(T_settings { })); | |||
| /** tuple of the different storage meta data */ | |||
| using storage_meta_data_type = decltype(make_storage_meta_data_tuple(T_settings { })); | |||
| private: | |||
| chunk_tuple_type _chunks; | |||
| storage_tuple_type _storages; | |||
| public: | |||
| /** | |||
| * get the storage index of the given component tag | |||
| * | |||
| * @tparam T_component_tag component tag type to get storage index for | |||
| * | |||
| * @param ct component tag to get storage index for | |||
| * | |||
| * @return integral constant of the storage index | |||
| */ | |||
| template<typename T_component_tag> | |||
| static constexpr decltype(auto) get_chunk_index(T_component_tag) noexcept | |||
| { | |||
| using ret_type = decltype(mp::list::index_of_first_matching( | |||
| std::declval<chunk_tuple_type>(), | |||
| chunk_contains_tag<T_component_tag>)); | |||
| return ret_type { }; | |||
| } | |||
| static constexpr decltype(auto) get_storage_index(T_component_tag ct) noexcept; | |||
| /** | |||
| * execute the given function for the storage that manages the given component tag | |||
| * | |||
| * @tparam T_self the component manager type | |||
| * @tparam T_component_tag component tag type to execute function for | |||
| * @tparam T_meta_data_tuple meta data tuple type to get meta data from | |||
| * @tparam T_func function type to execute | |||
| * | |||
| * @param self the component manager | |||
| * @param ct component tag to execute function for | |||
| * @param mdt meta data tuple to get meta data from | |||
| * @param func function to execute | |||
| * | |||
| * @return result of func | |||
| */ | |||
| template< | |||
| typename T_self, | |||
| typename T_component_tag, | |||
| typename T_meta_data_tuple, | |||
| typename T_func> | |||
| static constexpr decltype(auto) for_chunk_do( | |||
| static constexpr decltype(auto) for_storage_do( | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| T_meta_data_tuple&& mdt, | |||
| T_func&& func) | |||
| { | |||
| decltype(auto) index = get_chunk_index(ct); | |||
| decltype(auto) chunk = std::forward<T_self>(self).get_chunk_by_index(index); | |||
| decltype(auto) meta = hana::at(std::forward<T_meta_data_tuple>(mdt), index); | |||
| return std::forward<T_func>(func)(chunk, meta); | |||
| } | |||
| T_func&& func) noexcept; | |||
| /** | |||
| * get the component for the given component tag | |||
| * | |||
| * @tparam T_self the component manager type | |||
| * @tparam T_component_tag component tag type to execute function for | |||
| * @tparam T_entity_handle entity handle type | |||
| * @tparam T_meta_data_tuple meta data tuple type to get meta data from | |||
| * | |||
| * @param self the component manager | |||
| * @param ct component tag to execute function for | |||
| * qparam handle handle of the entity | |||
| * @param mdt meta data tuple to get meta data from | |||
| * | |||
| * @return the requested component | |||
| */ | |||
| template< | |||
| typename T_self, | |||
| typename T_component_tag, | |||
| @@ -136,14 +197,23 @@ beg_namespace_ecs_core_component | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| const T_entity_handle& handle, | |||
| const T_meta_data_tuple& mdt) | |||
| { | |||
| return for_chunk_do(std::forward<T_self>(self), ct, mdt, | |||
| [ct, handle](auto& chunk, const auto& md) -> decltype(auto) { | |||
| return chunk.get(ct, handle, md); | |||
| }); | |||
| } | |||
| const T_meta_data_tuple& mdt) noexcept; | |||
| /** | |||
| * add a new component for the given component tag | |||
| * | |||
| * @tparam T_self the component manager type | |||
| * @tparam T_component_tag component tag type to execute function for | |||
| * @tparam T_entity_handle entity handle type | |||
| * @tparam T_meta_data_tuple meta data tuple type to get meta data from | |||
| * | |||
| * @param self the component manager | |||
| * @param ct component tag to execute function for | |||
| * qparam handle handle of the entity | |||
| * @param mdt meta data tuple to get meta data from | |||
| * | |||
| * @return the requested component | |||
| */ | |||
| template< | |||
| typename T_self, | |||
| typename T_component_tag, | |||
| @@ -153,44 +223,92 @@ beg_namespace_ecs_core_component | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| const T_entity_handle& handle, | |||
| const T_meta_data_tuple& mdt) | |||
| { | |||
| return for_chunk_do(std::forward<T_self>(self), ct, mdt, | |||
| [ct, handle](auto& chunk, const auto& md) -> decltype(auto) { | |||
| return chunk.add(ct, handle, md); | |||
| }); | |||
| } | |||
| const T_meta_data_tuple& mdt) noexcept; | |||
| private: | |||
| /** | |||
| * get the component storage by index | |||
| * | |||
| * @tparam T_index index type to get component storage for | |||
| * | |||
| * @param index index to get component storage for | |||
| * | |||
| * @return component storage at the given index | |||
| */ | |||
| template<typename T_index> | |||
| constexpr decltype(auto) get_chunk_by_index(T_index index) noexcept | |||
| { | |||
| return hana::at(_chunks, index); | |||
| } | |||
| constexpr decltype(auto) get_storage_by_index(T_index index) noexcept; | |||
| /** | |||
| * get the component storage by the component tag | |||
| * | |||
| * @tparam T_component_tag component tag type to get component storage for | |||
| * | |||
| * @param ct component tag to get component storage for | |||
| * | |||
| * @return component storage for the given tag | |||
| */ | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) get_chunk_by_tag(T_component_tag ct) noexcept | |||
| { | |||
| return hana::at(_chunks, get_chunk_index(ct)); | |||
| } | |||
| constexpr decltype(auto) get_storage_by_tag(T_component_tag ct) noexcept; | |||
| public: | |||
| manager() | |||
| : _chunks(make_chunk_tuple(T_settings { })) | |||
| { } | |||
| /** | |||
| * constructor | |||
| */ | |||
| inline manager(); | |||
| /** | |||
| * get a reference to the component for the given component tag | |||
| * | |||
| * @tparam T_args set of the following parameter types | |||
| * T_component_tag component tag type to execute function for | |||
| * T_entity_handle entity handle type | |||
| * T_meta_data_tuple meta data tuple type to get meta data from | |||
| * | |||
| * @param args set of the following parameters | |||
| * ct component tag to execute function for | |||
| * handle handle of the entity | |||
| * mdt meta data tuple to get meta data from | |||
| * | |||
| * @return the requested component | |||
| */ | |||
| template<typename... T_args> | |||
| inline auto& get(T_args&&... args) & noexcept | |||
| { return get_impl(*this, std::forward<T_args>(args)...); } | |||
| inline auto& get(T_args&&... args) &; | |||
| /** | |||
| * get a read-only reference to the component for the given component tag | |||
| * | |||
| * @tparam T_args set of the following parameter types | |||
| * T_component_tag component tag type to execute function for | |||
| * T_entity_handle entity handle type | |||
| * T_meta_data_tuple meta data tuple type to get meta data from | |||
| * | |||
| * @param args set of the following parameters | |||
| * ct component tag to execute function for | |||
| * handle handle of the entity | |||
| * mdt meta data tuple to get meta data from | |||
| * | |||
| * @return the requested component | |||
| */ | |||
| template<typename... T_args> | |||
| inline auto& get(T_args&&... args) const & noexcept | |||
| { return get_impl(*this, std::forward<T_args>(args)...); } | |||
| inline auto& get(T_args&&... args) const &; | |||
| /** | |||
| * add a new component for the given component tag | |||
| * | |||
| * @tparam T_args set of the following parameter types | |||
| * T_component_tag component tag type to execute function for | |||
| * T_entity_handle entity handle type | |||
| * T_meta_data_tuple meta data tuple type to get meta data from | |||
| * | |||
| * @param args set of the following parameters | |||
| * ct component tag to execute function for | |||
| * handle handle of the entity | |||
| * mdt meta data tuple to get meta data from | |||
| * | |||
| * @return the requested component | |||
| */ | |||
| template<typename... T_args> | |||
| inline auto& add(T_args&&... args) & noexcept | |||
| { return add_impl(*this, std::forward<T_args>(args)...); } | |||
| inline auto& add(T_args&&... args) &; | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_component | |||
| } } } | |||
| @@ -0,0 +1,196 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/mp/list.h> | |||
| #include <ecs/core/mp/core/decay.h> | |||
| #include <ecs/core/mp/core/wrap.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| namespace detail | |||
| { | |||
| /* make_tuple_t */ | |||
| struct make_tuple_t | |||
| { | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) operator()(T_args&&... args) const | |||
| { return std::make_tuple(std::forward<T_args>(args)...); } | |||
| }; | |||
| /* make_storage_tuple_t */ | |||
| template<typename T_settings> | |||
| constexpr decltype(auto) make_storage_tuple_t | |||
| ::operator()(T_settings settings) const noexcept | |||
| { | |||
| auto cls = settings.component_signatures(); | |||
| return hana::unpack( | |||
| hana::transform(cls.items, [settings](auto x){ | |||
| auto csig = mp::unwrap(x); | |||
| return csig.storage()(settings, csig.tags());; | |||
| }), | |||
| make_tuple_t { }); | |||
| } | |||
| /* make_storage_meta_data_t */ | |||
| template<typename T_storage> | |||
| constexpr decltype(auto) make_storage_meta_data_t | |||
| ::operator()(T_storage&& storage) const noexcept | |||
| { | |||
| using storage_type = mp::decay_t<decltype(storage)>; | |||
| using meta_data_type = typename storage_type::meta_data_type; | |||
| return meta_data_type { }; | |||
| } | |||
| /* make_storage_meta_data_tuple_t */ | |||
| template<typename T_settings> | |||
| constexpr decltype(auto) make_storage_meta_data_tuple_t | |||
| ::operator()(T_settings settings) const noexcept | |||
| { | |||
| using storage_tuple_type = decltype(make_storage_tuple(settings)); | |||
| using storage_meta_data_tuple_type = decltype(hana::transform( | |||
| std::declval<storage_tuple_type>(), | |||
| make_storage_meta_data)); | |||
| return storage_meta_data_tuple_type { }; | |||
| } | |||
| /* storage_contains_tag_t */ | |||
| template<typename T_component_tag> | |||
| template<typename T_storage> | |||
| constexpr decltype(auto) storage_contains_tag_t<T_component_tag> | |||
| ::operator()(T_storage&& storage) const noexcept | |||
| { | |||
| using storage_type = mp::decay_t<T_storage>; | |||
| using component_tag_list_type = typename storage_type::component_tag_list_type; | |||
| return hana::contains(component_tag_list_type { }, T_component_tag { }); | |||
| } | |||
| } | |||
| /* manager */ | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) manager<T_settings> | |||
| ::get_storage_index(T_component_tag ct) noexcept | |||
| { | |||
| using ret_type = decltype(mp::list::index_of_first_matching( | |||
| std::declval<storage_tuple_type>(), | |||
| storage_contains_tag<T_component_tag>)); | |||
| return ret_type { }; | |||
| } | |||
| template< | |||
| typename T_settings> | |||
| template< | |||
| typename T_self, | |||
| typename T_component_tag, | |||
| typename T_meta_data_tuple, | |||
| typename T_func> | |||
| constexpr decltype(auto) manager<T_settings> | |||
| ::for_storage_do( | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| T_meta_data_tuple&& mdt, | |||
| T_func&& func) noexcept | |||
| { | |||
| decltype(auto) index = get_storage_index(ct); | |||
| decltype(auto) storage = std::forward<T_self>(self).get_storage_by_index(index); | |||
| decltype(auto) meta = hana::at(std::forward<T_meta_data_tuple>(mdt), index); | |||
| return std::forward<T_func>(func)(storage, meta); | |||
| } | |||
| template< | |||
| typename T_settings> | |||
| template< | |||
| typename T_self, | |||
| typename T_component_tag, | |||
| typename T_entity_handle, | |||
| typename T_meta_data_tuple> | |||
| constexpr decltype(auto) manager<T_settings> | |||
| ::get_impl( | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| const T_entity_handle& handle, | |||
| const T_meta_data_tuple& mdt) noexcept | |||
| { | |||
| return for_storage_do(std::forward<T_self>(self), ct, mdt, | |||
| [ct, handle](auto& storage, const auto& md) -> decltype(auto) { | |||
| return storage.get(ct, handle, md); | |||
| }); | |||
| } | |||
| template< | |||
| typename T_settings> | |||
| template< | |||
| typename T_self, | |||
| typename T_component_tag, | |||
| typename T_entity_handle, | |||
| typename T_meta_data_tuple> | |||
| constexpr decltype(auto) manager<T_settings> | |||
| ::add_impl( | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| const T_entity_handle& handle, | |||
| const T_meta_data_tuple& mdt) noexcept | |||
| { | |||
| return for_storage_do(std::forward<T_self>(self), ct, mdt, | |||
| [ct, handle](auto& storage, const auto& md) -> decltype(auto) { | |||
| return storage.add(ct, handle, md); | |||
| }); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_index> | |||
| constexpr decltype(auto) manager<T_settings> | |||
| ::get_storage_by_index(T_index index) noexcept | |||
| { | |||
| return hana::at(_storages, index); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) manager<T_settings> | |||
| ::get_storage_by_tag(T_component_tag ct) noexcept | |||
| { | |||
| return hana::at(_storages, get_storage_index(ct)); | |||
| } | |||
| template<typename T_settings> | |||
| inline manager<T_settings> | |||
| ::manager() | |||
| : _storages(make_storage_tuple(T_settings { })) | |||
| { } | |||
| template<typename T_settings> | |||
| template<typename... T_args> | |||
| inline auto& manager<T_settings> | |||
| ::get(T_args&&... args) & | |||
| { | |||
| return get_impl(*this, std::forward<T_args>(args)...); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename... T_args> | |||
| inline auto& manager<T_settings> | |||
| ::get(T_args&&... args) const & | |||
| { | |||
| return get_impl(*this, std::forward<T_args>(args)...); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename... T_args> | |||
| inline auto& manager<T_settings> | |||
| ::add(T_args&&... args) & | |||
| { | |||
| return add_impl(*this, std::forward<T_args>(args)...); | |||
| } | |||
| } } } | |||
| @@ -3,4 +3,7 @@ | |||
| #include "./storage/base.h" | |||
| #include "./storage/dynamic.h" | |||
| #include "./storage/empty.h" | |||
| #include "./storage/fixed.h" | |||
| #include "./storage/fixed.h" | |||
| #include "./storage/interface.h" | |||
| #include "./storage/base.inl" | |||
| @@ -2,11 +2,21 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| namespace storage { | |||
| struct empty_meta_data { }; | |||
| /** | |||
| * Base implementation of the component storage. | |||
| * | |||
| * Specializes @ref storage_iterface_t. | |||
| * | |||
| * @tparam T_container_builder internal container type | |||
| * @tparam T_component_tag_list list of component tags that are managed by this storage | |||
| */ | |||
| template<typename T_container_builder, typename T_component_tag_list> | |||
| struct base | |||
| { | |||
| @@ -23,40 +33,72 @@ beg_namespace_ecs_core_component_storage | |||
| container_type _container; | |||
| private: | |||
| /** | |||
| * get the compoent of the given tag from the storage | |||
| * | |||
| * @tparam T_self component storage type | |||
| * @tparam T_component_tag component tag type | |||
| * @tparam T_entity_handle entity handle type | |||
| * | |||
| * @param self component storage to get component from | |||
| * @param ct component tag to get component for | |||
| * @param handle handle of the entity to get the component for | |||
| * @param meta meta data stored in the entity to get component for | |||
| * | |||
| * @return component for the given parameters | |||
| */ | |||
| template<typename T_self, typename T_component_tag, typename T_entity_handle> | |||
| static inline decltype(auto) get_impl( | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| const T_entity_handle& handle, | |||
| const meta_data_type& meta) noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| using component_type = unwrap_t<decay_t<decltype(ct)>>; | |||
| decltype(auto) index = handle.index(); | |||
| decltype(auto) data = std::forward<T_self>(self)._container.at(index); | |||
| return std::get<component_type>(data); | |||
| } | |||
| const meta_data_type& meta); | |||
| public: | |||
| /** | |||
| * get a writable reference to the requested component | |||
| * | |||
| * @tparam T_component_tag component tag type to get reference for | |||
| * @tparam T_entity_handle entity handle type to get reference for | |||
| * | |||
| * @param ct component tag to get reference for | |||
| * @param handle entity handle to get reference for | |||
| * @param meta meta data for this storage class that is stored in the entity | |||
| * | |||
| * @return writable reference to the requested component | |||
| */ | |||
| template<typename T_component_tag, typename... Xs> | |||
| inline auto& get(T_component_tag ct, Xs&&... xs) & noexcept | |||
| { return get_impl(*this, ct, std::forward<Xs>(xs)...); } | |||
| inline auto& get(T_component_tag ct, Xs&&... xs) &; | |||
| /** | |||
| * get a read-only reference to the requested component | |||
| * | |||
| * @tparam T_component_tag component tag type to get reference for | |||
| * @tparam T_entity_handle entity handle type to get reference for | |||
| * | |||
| * @param ct component tag to get reference for | |||
| * @param handle entity handle to get reference for | |||
| * @param meta meta data for this storage class that is stored in the entity | |||
| * | |||
| * @return read-only reference to the requested component | |||
| */ | |||
| template<typename T_component_tag, typename... Xs> | |||
| inline const auto& get(T_component_tag ct, Xs&&... xs) const & noexcept | |||
| { return get_impl(*this, ct, std::forward<Xs>(xs)...); } | |||
| inline const auto& get(T_component_tag ct, Xs&&... xs) const &; | |||
| /** | |||
| * add and initialize a new component to this storage | |||
| * | |||
| * @tparam T_component_tag component tag type to get reference for | |||
| * @tparam T_entity_handle entity handle type to get reference for | |||
| * | |||
| * @param ct component tag to get reference for | |||
| * @param handle entity handle to get reference for | |||
| * @param meta meta data for this storage class that is stored in the entity | |||
| * | |||
| * @return writable reference to the requested component | |||
| */ | |||
| template<typename T_component_tag, typename T_entity_handle> | |||
| inline auto& add(T_component_tag ct, const T_entity_handle& handle, const meta_data_type& meta) | |||
| { | |||
| auto index = handle.index(); | |||
| if (_container.size() <= index) | |||
| { | |||
| _container.resize(index + grow_size); | |||
| } | |||
| return get(ct, handle, meta); | |||
| } | |||
| inline auto& add(T_component_tag ct, const T_entity_handle& handle, const meta_data_type& meta); | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_component_storage | |||
| } } } } | |||
| @@ -0,0 +1,55 @@ | |||
| #pragma once | |||
| #include <ecs/core/component/storage/base.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| namespace storage { | |||
| template<typename T_container_builder, typename T_component_tag_list> | |||
| template<typename T_self, typename T_component_tag, typename T_entity_handle> | |||
| decltype(auto) base<T_container_builder, T_component_tag_list> | |||
| ::get_impl( | |||
| T_self&& self, | |||
| T_component_tag ct, | |||
| const T_entity_handle& handle, | |||
| const meta_data_type& meta) | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| using component_type = unwrap_t<decay_t<decltype(ct)>>; | |||
| decltype(auto) index = handle.index(); | |||
| decltype(auto) data = std::forward<T_self>(self)._container.at(index); | |||
| return std::get<component_type>(data); | |||
| } | |||
| template<typename T_container_builder, typename T_component_tag_list> | |||
| template<typename T_component_tag, typename... Xs> | |||
| auto& base<T_container_builder, T_component_tag_list> | |||
| ::get(T_component_tag ct, Xs&&... xs) & | |||
| { | |||
| return get_impl(*this, ct, std::forward<Xs>(xs)...); | |||
| } | |||
| template<typename T_container_builder, typename T_component_tag_list> | |||
| template<typename T_component_tag, typename... Xs> | |||
| const auto& base<T_container_builder, T_component_tag_list> | |||
| ::get(T_component_tag ct, Xs&&... xs) const & | |||
| { | |||
| return get_impl(*this, ct, std::forward<Xs>(xs)...); | |||
| } | |||
| template<typename T_container_builder, typename T_component_tag_list> | |||
| template<typename T_component_tag, typename T_entity_handle> | |||
| auto& base<T_container_builder, T_component_tag_list> | |||
| ::add(T_component_tag ct, const T_entity_handle& handle, const meta_data_type& meta) | |||
| { | |||
| auto index = handle.index(); | |||
| if (_container.size() <= index) | |||
| { | |||
| _container.resize(index + grow_size); | |||
| } | |||
| return get(ct, handle, meta); | |||
| } | |||
| } } } } | |||
| @@ -4,8 +4,10 @@ | |||
| #include "./base.h" | |||
| beg_namespace_ecs_core_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| namespace storage { | |||
| struct vector_type_builder | |||
| { | |||
| @@ -13,6 +15,13 @@ beg_namespace_ecs_core_component_storage | |||
| using type = std::vector<T>; | |||
| }; | |||
| /** | |||
| * Dynamic component storage. | |||
| * | |||
| * Specializes @ref storage_iterface_t. | |||
| * | |||
| * @tparam T_component_tag_list list of component tags that are managed by this storage | |||
| */ | |||
| template<typename T_component_tag_list> | |||
| struct dynamic | |||
| : base<vector_type_builder, T_component_tag_list> | |||
| @@ -26,5 +35,4 @@ beg_namespace_ecs_core_component_storage | |||
| { this->_container.resize(inital_size); } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_component_storage | |||
| } } } } | |||
| @@ -2,9 +2,18 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| namespace storage { | |||
| /** | |||
| * Empty component storage. | |||
| * | |||
| * Specializes @ref storage_iterface_t. | |||
| * | |||
| * @tparam T_component_tag_list list of component tags that are managed by this storage | |||
| */ | |||
| template<typename T_component_tag_list> | |||
| struct empty | |||
| { | |||
| @@ -20,5 +29,4 @@ beg_namespace_ecs_core_component_storage | |||
| { } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_component_storage | |||
| } } } } | |||
| @@ -4,8 +4,10 @@ | |||
| #include "./base.h" | |||
| beg_namespace_ecs_core_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| namespace storage { | |||
| template<size_t T_size> | |||
| struct array_type_builder | |||
| @@ -13,13 +15,21 @@ beg_namespace_ecs_core_component_storage | |||
| template<typename T> | |||
| struct impl : public std::array<T, T_size> | |||
| { | |||
| inline void resize(size_t) { } | |||
| inline void resize(size_t) | |||
| { throw std::overflow_error("fixed component storage can not grow!"); } | |||
| }; | |||
| template<typename T> | |||
| using type = impl<T>; | |||
| }; | |||
| /** | |||
| * Fixed component storage. | |||
| * | |||
| * Specializes @ref storage_iterface_t. | |||
| * | |||
| * @tparam T_component_tag_list list of component tags that are managed by this storage | |||
| */ | |||
| template<typename T_component_tag_list, size_t T_size> | |||
| struct fixed | |||
| : base<array_type_builder<T_size>, T_component_tag_list> | |||
| @@ -30,5 +40,4 @@ beg_namespace_ecs_core_component_storage | |||
| using typename base_type::meta_data_type; | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_component_storage | |||
| } } } } | |||
| @@ -0,0 +1,71 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace component { | |||
| /** | |||
| * type to store and manage components | |||
| * | |||
| * @tparam T_component_tag_list list of component tags to manage in this storage type | |||
| */ | |||
| template<typename T_component_tag_list> | |||
| struct storage_iterface_t | |||
| { | |||
| struct dummy_meta_data { }; | |||
| /** list of component tags managed by this storage class */ | |||
| using component_tag_list_type = T_component_tag_list; | |||
| /** meta data to store in the entity for this storage class */ | |||
| using meta_data_type = dummy_meta_data; | |||
| /** | |||
| * get a writable reference to the requested component | |||
| * | |||
| * @tparam T_component_tag component tag type to get reference for | |||
| * @tparam T_entity_handle entity handle type to get reference for | |||
| * | |||
| * @param ct component tag to get reference for | |||
| * @param handle entity handle to get reference for | |||
| * @param meta meta data for this storage class that is stored in the entity | |||
| * | |||
| * @return writable reference to the requested component | |||
| */ | |||
| template<typename T_component_tag, typename T_entity_handle> | |||
| inline auto& get(T_component_tag ct, const T_entity_handle& handle, const meta_data_type& meta) &; | |||
| /** | |||
| * get a read-only reference to the requested component | |||
| * | |||
| * @tparam T_component_tag component tag type to get reference for | |||
| * @tparam T_entity_handle entity handle type to get reference for | |||
| * | |||
| * @param ct component tag to get reference for | |||
| * @param handle entity handle to get reference for | |||
| * @param meta meta data for this storage class that is stored in the entity | |||
| * | |||
| * @return read-only reference to the requested component | |||
| */ | |||
| template<typename T_component_tag, typename T_entity_handle> | |||
| inline const auto& get(T_component_tag ct, const T_entity_handle& handle, const meta_data_type& meta) const &; | |||
| /** | |||
| * add and initialize a new component to this storage | |||
| * | |||
| * @tparam T_component_tag component tag type to get reference for | |||
| * @tparam T_entity_handle entity handle type to get reference for | |||
| * | |||
| * @param ct component tag to get reference for | |||
| * @param handle entity handle to get reference for | |||
| * @param meta meta data for this storage class that is stored in the entity | |||
| * | |||
| * @return writable reference to the requested component | |||
| */ | |||
| template<typename T_component_tag, typename T_entity_handle> | |||
| inline auto& add(T_component_tag ct, const T_entity_handle& handle, const meta_data_type& meta); | |||
| }; | |||
| } } } | |||
| @@ -2,4 +2,7 @@ | |||
| #include "./storage/base.h" | |||
| #include "./storage/dynamic.h" | |||
| #include "./storage/fixed.h" | |||
| #include "./storage/fixed.h" | |||
| #include "./storage/interface.h" | |||
| #include "./storage/base.inl" | |||
| @@ -4,9 +4,14 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/utils/bitset.h> | |||
| beg_namespace_ecs_core_entity_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace entity { | |||
| namespace storage { | |||
| /** | |||
| * struct to represent an entity handle | |||
| */ | |||
| struct entity_handle | |||
| { | |||
| private: | |||
| @@ -14,58 +19,125 @@ beg_namespace_ecs_core_entity_storage | |||
| uint32_t _counter; | |||
| public: | |||
| entity_handle(uint32_t i, uint32_t c) | |||
| : _index (i) | |||
| , _counter (c) | |||
| { } | |||
| inline auto index() const | |||
| { return _index; } | |||
| inline auto counter() const | |||
| { return _counter; } | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param i index the entity is stored at | |||
| * @param c counter to check the reusage of the entity | |||
| */ | |||
| inline entity_handle(uint32_t i, uint32_t c); | |||
| /** | |||
| * get the index of the entity | |||
| * | |||
| * @return index of the entity | |||
| */ | |||
| inline auto index() const; | |||
| /** | |||
| * get the counter of the entity | |||
| * | |||
| * @return counter of the entity | |||
| */ | |||
| inline auto counter() const; | |||
| /** | |||
| * compare with other handle | |||
| * | |||
| * @param other handle to compare with | |||
| * | |||
| * @retval -1 if this is smaller than other | |||
| * @retval 0 if this is equal to other | |||
| * @retval -1 if this is larger than other | |||
| */ | |||
| inline int compare(const entity_handle& other) const; | |||
| /* compare operators */ | |||
| inline bool operator <=(const entity_handle& other) const | |||
| { return compare(other) <= 0; } | |||
| inline bool operator <(const entity_handle& other) const | |||
| { return compare(other) < 0; } | |||
| inline bool operator ==(const entity_handle& other) const | |||
| { return compare(other) == 0; } | |||
| inline bool operator !=(const entity_handle& other) const | |||
| { return compare(other) != 0; } | |||
| inline bool operator >(const entity_handle& other) const | |||
| { return compare(other) > 0; } | |||
| inline bool operator >=(const entity_handle& other) const | |||
| { return compare(other) >= 0; } | |||
| }; | |||
| template <typename T_settings, typename T_chunk_meta_data> | |||
| /** | |||
| * struct to manage the meta data for each entity | |||
| * | |||
| * @tparam T_settings settings type the environment is configured with | |||
| * @tparam T_storage_meta_data meta data type of the component storagte container | |||
| */ | |||
| template <typename T_settings, typename T_storage_meta_data> | |||
| struct entity_meta_data | |||
| : private T_storage_meta_data | |||
| { | |||
| public: | |||
| using bitset_type = utils::bitset<T_settings>; | |||
| using chunk_meta_data_type = T_chunk_meta_data; | |||
| using counter_type = uint32_t; | |||
| using bitset_type = utils::bitset<T_settings>; | |||
| using storage_meta_data_type = T_storage_meta_data; | |||
| using counter_type = uint32_t; | |||
| private: | |||
| bitset_type _bitset; | |||
| counter_type _counter; | |||
| chunk_meta_data_type _chunk_meta_data; | |||
| bitset_type _bitset; | |||
| counter_type _counter; | |||
| public: | |||
| entity_meta_data() | |||
| : _counter(0) | |||
| { } | |||
| inline auto& bitset() | |||
| { return _bitset; } | |||
| inline auto counter() const | |||
| { return _counter; } | |||
| inline auto& chunk_meta_data() | |||
| { return _chunk_meta_data; } | |||
| inline void reset() | |||
| { | |||
| ++_counter; | |||
| _bitset.clear(); | |||
| } | |||
| /** | |||
| * constructor | |||
| */ | |||
| inline entity_meta_data(); | |||
| /** | |||
| * get the bitset of components stored for this entity | |||
| * | |||
| * @return component bitset | |||
| */ | |||
| inline auto& bitset(); | |||
| /** | |||
| * get the current reusage counter of the entity | |||
| * | |||
| * @return current reusage counter of the entity | |||
| */ | |||
| inline auto counter() const; | |||
| /** | |||
| * get the storage meta data for this entity | |||
| * | |||
| * @return storage meta data for this entity | |||
| */ | |||
| inline auto& storage_meta_data(); | |||
| /** | |||
| * reset all meta data to their defaults | |||
| */ | |||
| inline void reset(); | |||
| }; | |||
| template<typename T_container_builder, typename T_settings, typename T_chunk_meta_data> | |||
| /** | |||
| * basic entity storage container | |||
| * | |||
| * @tparam T_container_builder type to build the inner storage container | |||
| * @tparam T_settings settings type the environemtn is configured with | |||
| * @tparam T_storage_meta_data meta data type of the component storage | |||
| */ | |||
| template<typename T_container_builder, typename T_settings, typename T_storage_meta_data> | |||
| struct base | |||
| { | |||
| public: | |||
| using entity_handle_type = entity_handle; | |||
| using entity_meta_data_type = entity_meta_data<T_settings, T_chunk_meta_data>; | |||
| using entity_meta_data_type = entity_meta_data<T_settings, T_storage_meta_data>; | |||
| private: | |||
| using container_type = typename T_container_builder::template type<entity_meta_data_type>; | |||
| @@ -78,65 +150,51 @@ beg_namespace_ecs_core_entity_storage | |||
| queue_type _free_ids; | |||
| private: | |||
| inline void grow(size_t count) | |||
| { | |||
| auto old_size = _container.size(); | |||
| _container.resize(old_size + count); | |||
| for (size_t i = old_size; i < _container.size(); ++i) | |||
| { | |||
| _free_ids.emplace(i); | |||
| } | |||
| } | |||
| /** | |||
| * grow the container to the given size | |||
| * | |||
| * @param count new size of the container | |||
| */ | |||
| inline void grow(size_t count); | |||
| public: | |||
| inline bool is_valid(const entity_handle& handle) const | |||
| { | |||
| return handle.index() < _container.size() | |||
| && _container.at(handle.index()).counter() == handle.counter(); | |||
| } | |||
| inline decltype(auto) claim() | |||
| { | |||
| if (_free_ids.empty()) | |||
| { | |||
| grow(grow_size); | |||
| } | |||
| assert(!_free_ids.empty()); | |||
| auto index = _free_ids.back(); | |||
| auto& item = _container.at(index); | |||
| _free_ids.pop(); | |||
| return entity_handle(index, item.counter()); | |||
| } | |||
| inline void reclaim(const entity_handle& handle) | |||
| { | |||
| if (!is_valid(handle)) | |||
| throw std::invalid_argument("invalid entity handle"); | |||
| auto index = handle.index(); | |||
| auto& item = _container.at(index); | |||
| item.reset(); | |||
| _free_ids.emplace(index); | |||
| } | |||
| inline void clear() | |||
| { | |||
| _free_ids = queue_type { }; | |||
| for (size_t i = 0; i < _container.size(); ++i) | |||
| { | |||
| _container.at(i).reset(); | |||
| _free_ids.emplace(i); | |||
| } | |||
| } | |||
| inline auto& meta_data(const entity_handle& handle) | |||
| { | |||
| if (!is_valid(handle)) | |||
| throw std::invalid_argument("invalid entity handle"); | |||
| auto index = handle.index(); | |||
| return _container.at(index); | |||
| } | |||
| /** | |||
| * check if the given entity handle is valid | |||
| * | |||
| * @param handle handle to check | |||
| * | |||
| * @retval TRUE if handle is valid | |||
| * @retval FALSE if handle is invalid | |||
| */ | |||
| inline bool is_valid(const entity_handle& handle) const; | |||
| /** | |||
| * create a new entity inside the container | |||
| * | |||
| * @return handle of the new created entity | |||
| */ | |||
| inline decltype(auto) claim(); | |||
| /** | |||
| * release an entiy handle | |||
| * | |||
| * @param handle entity handle to release | |||
| */ | |||
| inline void reclaim(const entity_handle& handle); | |||
| /** | |||
| * remove all stored entities | |||
| */ | |||
| inline void clear(); | |||
| /** | |||
| * get the meta data stored for the given handle | |||
| * | |||
| * @param handle entity handle to get meta data for | |||
| * | |||
| * @return meta data for the given entity handle | |||
| */ | |||
| inline auto& meta_data(const entity_handle& handle); | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_entity_storage | |||
| } } } } | |||
| @@ -0,0 +1,172 @@ | |||
| #pragma once | |||
| #include <ecs/core/entity/storage/base.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace entity { | |||
| namespace storage { | |||
| /* entity_handle */ | |||
| inline entity_handle | |||
| ::entity_handle(uint32_t i, uint32_t c) | |||
| : _index (i) | |||
| , _counter (c) | |||
| { } | |||
| inline auto entity_handle | |||
| ::index() const | |||
| { | |||
| return _index; | |||
| } | |||
| inline auto entity_handle | |||
| ::counter() const | |||
| { | |||
| return _counter; | |||
| } | |||
| inline int entity_handle | |||
| ::compare(const entity_handle& other) const | |||
| { | |||
| if (_counter < other._counter) { | |||
| return -1; | |||
| } else if (_counter > other._counter) { | |||
| return 1; | |||
| } else if (_index < other._index) { | |||
| return -1; | |||
| } else if (_index > other._index) { | |||
| return 1; | |||
| } else { | |||
| return 0; | |||
| } | |||
| } | |||
| /* entity_meta_data */ | |||
| template <typename T_settings, typename T_storage_meta_data> | |||
| inline entity_meta_data<T_settings, T_storage_meta_data> | |||
| ::entity_meta_data() | |||
| : _counter(0) | |||
| { } | |||
| template <typename T_settings, typename T_storage_meta_data> | |||
| inline auto& entity_meta_data<T_settings, T_storage_meta_data> | |||
| ::bitset() | |||
| { | |||
| return _bitset; | |||
| } | |||
| template <typename T_settings, typename T_storage_meta_data> | |||
| inline auto entity_meta_data<T_settings, T_storage_meta_data> | |||
| ::counter() const | |||
| { | |||
| return _counter; | |||
| } | |||
| template <typename T_settings, typename T_storage_meta_data> | |||
| inline auto& entity_meta_data<T_settings, T_storage_meta_data> | |||
| ::storage_meta_data() | |||
| { | |||
| return *static_cast<storage_meta_data_type*>(this); | |||
| } | |||
| template <typename T_settings, typename T_storage_meta_data> | |||
| inline void entity_meta_data<T_settings, T_storage_meta_data> | |||
| ::reset() | |||
| { | |||
| ++_counter; | |||
| _bitset.clear(); | |||
| } | |||
| /* base */ | |||
| template< | |||
| typename T_container_builder, | |||
| typename T_settings, | |||
| typename T_storage_meta_data> | |||
| inline void base<T_container_builder, T_settings, T_storage_meta_data> | |||
| ::grow(size_t count) | |||
| { | |||
| auto old_size = _container.size(); | |||
| _container.resize(old_size + count); | |||
| for (size_t i = old_size; i < _container.size(); ++i) | |||
| { | |||
| _free_ids.emplace(i); | |||
| } | |||
| } | |||
| template< | |||
| typename T_container_builder, | |||
| typename T_settings, | |||
| typename T_storage_meta_data> | |||
| inline bool base<T_container_builder, T_settings, T_storage_meta_data> | |||
| ::is_valid(const entity_handle& handle) const | |||
| { | |||
| return handle.index() < _container.size() | |||
| && _container.at(handle.index()).counter() == handle.counter(); | |||
| } | |||
| template< | |||
| typename T_container_builder, | |||
| typename T_settings, | |||
| typename T_storage_meta_data> | |||
| inline decltype(auto) base<T_container_builder, T_settings, T_storage_meta_data> | |||
| ::claim() | |||
| { | |||
| if (_free_ids.empty()) | |||
| { | |||
| grow(grow_size); | |||
| } | |||
| assert(!_free_ids.empty()); | |||
| auto index = _free_ids.back(); | |||
| auto& item = _container.at(index); | |||
| _free_ids.pop(); | |||
| return entity_handle(index, item.counter()); | |||
| } | |||
| template< | |||
| typename T_container_builder, | |||
| typename T_settings, | |||
| typename T_storage_meta_data> | |||
| inline void base<T_container_builder, T_settings, T_storage_meta_data> | |||
| ::reclaim(const entity_handle& handle) | |||
| { | |||
| if (!is_valid(handle)) | |||
| throw std::invalid_argument("invalid entity handle"); | |||
| auto index = handle.index(); | |||
| auto& item = _container.at(index); | |||
| item.reset(); | |||
| _free_ids.emplace(index); | |||
| } | |||
| template< | |||
| typename T_container_builder, | |||
| typename T_settings, | |||
| typename T_storage_meta_data> | |||
| inline void base<T_container_builder, T_settings, T_storage_meta_data> | |||
| ::clear() | |||
| { | |||
| _free_ids = queue_type { }; | |||
| for (size_t i = 0; i < _container.size(); ++i) | |||
| { | |||
| _container.at(i).reset(); | |||
| _free_ids.emplace(i); | |||
| } | |||
| } | |||
| template< | |||
| typename T_container_builder, | |||
| typename T_settings, | |||
| typename T_storage_meta_data> | |||
| inline auto& base<T_container_builder, T_settings, T_storage_meta_data> | |||
| ::meta_data(const entity_handle& handle) | |||
| { | |||
| if (!is_valid(handle)) | |||
| throw std::invalid_argument("invalid entity handle"); | |||
| auto index = handle.index(); | |||
| return _container.at(index); | |||
| } | |||
| } } } } | |||
| @@ -4,8 +4,10 @@ | |||
| #include "./base.h" | |||
| beg_namespace_ecs_core_entity_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace entity { | |||
| namespace storage { | |||
| struct vector_type_builder | |||
| { | |||
| @@ -13,12 +15,20 @@ beg_namespace_ecs_core_entity_storage | |||
| using type = std::vector<T>; | |||
| }; | |||
| template<typename T_settings, typename T_chunk_meta_data> | |||
| /** | |||
| * Dynamic entity storage. | |||
| * | |||
| * Specializes @ref storage_iterface_t. | |||
| * | |||
| * @tparam T_settings settings type the environment is configured with | |||
| * @tparam T_storage_meta_data storage meta data type to store within the entity meta data | |||
| */ | |||
| template<typename T_settings, typename T_storage_meta_data> | |||
| struct dynamic | |||
| : base<vector_type_builder, T_settings, T_chunk_meta_data> | |||
| : base<vector_type_builder, T_settings, T_storage_meta_data> | |||
| { | |||
| public: | |||
| using base_type = base<vector_type_builder, T_settings, T_chunk_meta_data>; | |||
| using base_type = base<vector_type_builder, T_settings, T_storage_meta_data>; | |||
| using typename base_type::entity_handle_type; | |||
| using typename base_type::entity_meta_data_type; | |||
| @@ -34,5 +44,4 @@ beg_namespace_ecs_core_entity_storage | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_entity_storage | |||
| } } } } | |||
| @@ -4,8 +4,10 @@ | |||
| #include "./base.h" | |||
| beg_namespace_ecs_core_entity_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace entity { | |||
| namespace storage { | |||
| template<size_t T_size> | |||
| struct array_type_builder | |||
| @@ -13,19 +15,28 @@ beg_namespace_ecs_core_entity_storage | |||
| template<typename T> | |||
| struct impl : public std::array<T, T_size> | |||
| { | |||
| inline void resize(size_t) { } | |||
| inline void resize(size_t) | |||
| { throw std::overflow_error("fixed entity storage can not grow!"); } | |||
| }; | |||
| template<typename T> | |||
| using type = impl<T>; | |||
| }; | |||
| template<typename T_settings, typename T_chunk_meta_data, size_t T_size> | |||
| /** | |||
| * Fixed entity storage. | |||
| * | |||
| * Specializes @ref storage_iterface_t. | |||
| * | |||
| * @tparam T_settings settings type the environment is configured with | |||
| * @tparam T_storage_meta_data storage meta data type to store within the entity meta data | |||
| */ | |||
| template<typename T_settings, typename T_storage_meta_data, size_t T_size> | |||
| struct fixed | |||
| : base<array_type_builder<T_size>, T_settings, T_chunk_meta_data> | |||
| : base<array_type_builder<T_size>, T_settings, T_storage_meta_data> | |||
| { | |||
| public: | |||
| using base_type = base<array_type_builder<T_size>, T_settings, T_chunk_meta_data>; | |||
| using base_type = base<array_type_builder<T_size>, T_settings, T_storage_meta_data>; | |||
| using typename base_type::entity_handle_type; | |||
| using typename base_type::entity_meta_data_type; | |||
| @@ -40,5 +51,4 @@ beg_namespace_ecs_core_entity_storage | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_entity_storage | |||
| } } } } | |||
| @@ -0,0 +1,69 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace entity { | |||
| namespace storage { | |||
| /** | |||
| * type to store and manage entities | |||
| * | |||
| * @tparam T_storage_meta_data meta data type of the component storage | |||
| */ | |||
| template<typename T_container_builder, typename T_settings, typename T_storage_meta_data> | |||
| struct storage_iterface_t | |||
| { | |||
| public: | |||
| /** | |||
| * type of the entity handle to identify an entity | |||
| */ | |||
| using entity_handle_type = int; | |||
| /** | |||
| * meta data type of the entity handle containing all relevant meta information stored for an entity | |||
| */ | |||
| using entity_meta_data_type = T_storage_meta_data; | |||
| public: | |||
| /** | |||
| * check if the given entity handle is valid | |||
| * | |||
| * @param handle handle to check | |||
| * | |||
| * @retval TRUE if handle is valid | |||
| * @retval FALSE if handle is invalid | |||
| */ | |||
| inline bool is_valid(const entity_handle& handle) const; | |||
| /** | |||
| * create a new entity inside the container | |||
| * | |||
| * @return handle of the new created entity | |||
| */ | |||
| inline decltype(auto) claim(); | |||
| /** | |||
| * release an entiy handle | |||
| * | |||
| * @param handle entity handle to release | |||
| */ | |||
| inline void reclaim(const entity_handle& handle); | |||
| /** | |||
| * remove all stored entities | |||
| */ | |||
| inline void clear(); | |||
| /** | |||
| * get the meta data stored for the given handle | |||
| * | |||
| * @param handle entity handle to get meta data for | |||
| * | |||
| * @return meta data for the given entity handle | |||
| */ | |||
| inline auto& meta_data(const entity_handle& handle); | |||
| }; | |||
| } } } } | |||
| @@ -2,9 +2,16 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_mp | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| /** | |||
| * type that copies the const qualifier from one type to another | |||
| * | |||
| * @tparam T type to copy qualifier to | |||
| * @tparam T_source type to copy qualifier at | |||
| */ | |||
| template <typename T, typename T_source> | |||
| using copy_const_qualifier = | |||
| std::conditional_t< | |||
| @@ -12,6 +19,12 @@ beg_namespace_ecs_core_mp | |||
| std::add_const_t<T>, | |||
| T>; | |||
| /** | |||
| * type that copies the volatile qualifier from one type to another | |||
| * | |||
| * @tparam T type to copy qualifier to | |||
| * @tparam T_source type to copy qualifier at | |||
| */ | |||
| template <typename T, typename T_source> | |||
| using copy_volatile_qualifier = | |||
| std::conditional_t< | |||
| @@ -19,11 +32,16 @@ beg_namespace_ecs_core_mp | |||
| std::add_volatile_t<T>, | |||
| T>; | |||
| /** | |||
| * type that copies the const volatile qualifier from one type to another | |||
| * | |||
| * @tparam T type to copy qualifier to | |||
| * @tparam T_source type to copy qualifier at | |||
| */ | |||
| template <typename T, typename T_source> | |||
| using copy_cv_qualifiers = | |||
| copy_const_qualifier< | |||
| copy_volatile_qualifier<T, T_source>, | |||
| T_source>; | |||
| } | |||
| end_namespace_ecs_core_mp | |||
| } } } | |||
| @@ -2,11 +2,14 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_mp | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| /** | |||
| * remove all classifiers from a given type | |||
| */ | |||
| template<typename T> | |||
| using decay_t = typename hana::detail::decay<T>::type; | |||
| } | |||
| end_namespace_ecs_core_mp | |||
| } } } | |||
| @@ -3,22 +3,42 @@ | |||
| #include <ecs/config.h> | |||
| #include "./is_valid.h" | |||
| beg_namespace_ecs_core_mp | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<template<typename...> class T_tag> | |||
| /** | |||
| * predicate type to check if each element of a list is a specialization of the given type | |||
| * | |||
| * @tparam T_check template type to check each element with | |||
| */ | |||
| template<template<typename...> class T_check> | |||
| struct is_list_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T list type to check | |||
| * | |||
| * @param t list to check | |||
| * | |||
| * @retval integral constant TRUE if each element has passed the check | |||
| * @retval integral constant TRUE if at least one element has not passed the check | |||
| */ | |||
| template<typename T> | |||
| constexpr decltype(auto) operator()(T&& t) const noexcept | |||
| { return hana::all_of(std::forward<T>(t), is_valid<T_tag>); } | |||
| { return hana::all_of(std::forward<T>(t), is_valid<T_check>); } | |||
| }; | |||
| } | |||
| template<template<typename...> class T_tag> | |||
| constexpr decltype(auto) is_list = __impl::is_list_t<T_tag> { }; | |||
| /** | |||
| * predicate type to check if the given parameter is a list of the given tag | |||
| * | |||
| * @tparam T_check check to execute for each element of the given list | |||
| */ | |||
| template<template<typename...> class T_check> | |||
| constexpr decltype(auto) is_list = detail::is_list_t<T_check> { }; | |||
| } | |||
| end_namespace_ecs_core_mp | |||
| } } } | |||
| @@ -2,9 +2,16 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_mp | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| /** | |||
| * template to check if a given type T is a specialization of another template T_template | |||
| * | |||
| * @tparam T_template template definition to check for | |||
| * @tparam T type to check | |||
| */ | |||
| template<template<typename...> class T_template, typename T> | |||
| struct is_specialization_of | |||
| : public hana::false_ | |||
| @@ -15,5 +22,4 @@ beg_namespace_ecs_core_mp | |||
| : public hana::true_ | |||
| { }; | |||
| } | |||
| end_namespace_ecs_core_mp | |||
| } } } | |||
| @@ -4,25 +4,45 @@ | |||
| #include "./is_specialization_of.h" | |||
| #include "../list.h" | |||
| beg_namespace_ecs_core_mp | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<template<typename...> class T_tag> | |||
| /** | |||
| * predicate type to check if the passed arguments are specializations of the given template | |||
| * | |||
| * @tparam T_check template to check for | |||
| */ | |||
| template<template<typename...> class T_check> | |||
| struct is_valid_t | |||
| { | |||
| template<typename T> | |||
| static constexpr decltype(auto) is_tag = mp::is_specialization_of<T_tag, T> { }; | |||
| static constexpr decltype(auto) is_tag = mp::is_specialization_of<T_check, T> { }; | |||
| template<typename... Xs> | |||
| constexpr decltype(auto) operator()(Xs... xs) const noexcept | |||
| { return mp::list::all(is_tag<Xs>...); } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_args argument types to check | |||
| * | |||
| * @param args arguments to check | |||
| * | |||
| * @retval integral constant TRUE if each element has passed the check | |||
| * @retval integral constant TRUE if at least one element has not passed the check | |||
| */ | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) operator()(T_args... args) const noexcept | |||
| { return mp::list::all(is_tag<T_args>...); } | |||
| }; | |||
| } | |||
| /** | |||
| * predicate to check if the passed arguments are specializations of the given template | |||
| * | |||
| * @tparam T_check template to check for | |||
| */ | |||
| template<template<typename...> class T_tag> | |||
| constexpr decltype(auto) is_valid = __impl::is_valid_t<T_tag> { }; | |||
| constexpr decltype(auto) is_valid = detail::is_valid_t<T_tag> { }; | |||
| } | |||
| end_namespace_ecs_core_mp | |||
| } } } | |||
| @@ -2,35 +2,67 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_mp | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| /** | |||
| * pack the given type in a boost::hana::type | |||
| */ | |||
| template<typename T> | |||
| using wrap_t = hana::type<T>; | |||
| /** | |||
| * unpack a given boost::hana::type | |||
| **/ | |||
| template<typename T> | |||
| using unwrap_t = typename T::type; | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to wrap a given type into a boost::hana::type | |||
| */ | |||
| struct wrap_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T type to wrap | |||
| * | |||
| * @return object of the type wrapped in boost::hana::type | |||
| */ | |||
| template<typename T> | |||
| constexpr decltype(auto) operator()(T) const noexcept | |||
| { return mp::wrap_t<T> { }; } | |||
| }; | |||
| /** | |||
| * predicate type to unwrap a given boost::hana::type | |||
| */ | |||
| struct unwrap_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T type to unwrap | |||
| * | |||
| * @return object of the unwrapped type | |||
| */ | |||
| template<typename T> | |||
| constexpr decltype(auto) operator()(T) const noexcept | |||
| { return mp::unwrap_t<T> { }; } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) wrap = __impl::wrap_t { }; | |||
| /** | |||
| * predicate to wrap a given type into a boost::hana::type | |||
| */ | |||
| constexpr decltype(auto) wrap = detail::wrap_t { }; | |||
| constexpr decltype(auto) unwrap = __impl::unwrap_t { }; | |||
| /** | |||
| * predicate to unwrap a given boost::hana::type | |||
| */ | |||
| constexpr decltype(auto) unwrap = detail::unwrap_t { }; | |||
| } | |||
| end_namespace_ecs_core_mp | |||
| } } } | |||
| @@ -2,56 +2,99 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_mp_list | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| namespace list { | |||
| /* base */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * simple meta programming list implementation | |||
| */ | |||
| template<typename... Xs> | |||
| using list_t = hana::basic_tuple<Xs...>; | |||
| } | |||
| /** | |||
| * wrapper to the list type | |||
| */ | |||
| template<typename... Xs> | |||
| using type = __impl::list_t<Xs...>; | |||
| using type = detail::list_t<Xs...>; | |||
| /** | |||
| * create a list object | |||
| */ | |||
| template<typename... Xs> | |||
| constexpr decltype(auto) value = type<Xs...> { }; | |||
| /** | |||
| * empty list | |||
| */ | |||
| constexpr decltype(auto) empty = value<>; | |||
| /* make */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to create a list | |||
| */ | |||
| struct make_t | |||
| { | |||
| template<typename... Xs> | |||
| constexpr decltype(auto) operator()(Xs...) const noexcept | |||
| { return value<Xs...>; } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_args argument types to wrap into a list | |||
| * | |||
| * @return list that contains the given types | |||
| */ | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) operator()(T_args...) const noexcept | |||
| { return value<T_args...>; } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make = __impl::make_t { }; | |||
| /** | |||
| * predicate to create a list | |||
| */ | |||
| constexpr decltype(auto) make = detail::make_t { }; | |||
| /* all */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to check if all passed arguments are true-type or not | |||
| */ | |||
| struct all_t | |||
| { | |||
| template<typename... Xs> | |||
| constexpr decltype(auto) operator()(Xs&&... xs) const noexcept | |||
| { return hana::all(hana::make_basic_tuple(std::forward<Xs>(xs)...)); } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_args argument types to check | |||
| * | |||
| * @param args arguments to check | |||
| * | |||
| * @retval integral constant TRUE if each argument is the true-type | |||
| * @retval integral constant TRUE if at least one argument is not the true-type | |||
| */ | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) operator()(T_args&&... args) const noexcept | |||
| { return hana::all(hana::make_basic_tuple(std::forward<T_args>(args)...)); } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) all = __impl::all_t { }; | |||
| /** | |||
| * predicate to check if all passed arguments are true-type or not | |||
| */ | |||
| constexpr decltype(auto) all = detail::all_t { }; | |||
| /* unwrap */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<template<typename...> class T_outer, typename T_list> | |||
| struct unwrap_t; | |||
| @@ -61,15 +104,35 @@ beg_namespace_ecs_core_mp_list | |||
| { using type = T_outer<typename Ts::type...>; }; | |||
| } | |||
| /** | |||
| * type to unwrap the types stored in a list | |||
| * | |||
| * @tparam T_outer container to store unwrapped types in | |||
| * @tparam T_list list of wrapped types to unwrap | |||
| */ | |||
| template<template<typename...> class T_outer, typename T_list> | |||
| using unwrap_t = typename __impl::unwrap_t<T_outer, T_list>::type; | |||
| using unwrap_t = typename detail::unwrap_t<T_outer, T_list>::type; | |||
| /* index_of */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to get the index of a certain element inside a list | |||
| */ | |||
| struct index_of_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_list list type to find the element in | |||
| * @tparam T element type to get index for | |||
| * | |||
| * @param list list to find the element in | |||
| * @param item element to get index for | |||
| * | |||
| * @return integral constant with the index of the searched element | |||
| */ | |||
| template<typename T_list, typename T> | |||
| constexpr decltype(auto) operator()(T_list list, T&& item) const noexcept | |||
| { | |||
| @@ -82,14 +145,31 @@ beg_namespace_ecs_core_mp_list | |||
| }; | |||
| } | |||
| constexpr decltype(auto) index_of = __impl::index_of_t { }; | |||
| /** | |||
| * predicate to get the index of a certain element inside a list | |||
| */ | |||
| constexpr decltype(auto) index_of = detail::index_of_t { }; | |||
| /* index_of_first_matching */ | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to get the index of an element of a list that first matches the given predicate | |||
| */ | |||
| struct index_of_first_matching_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_list list type to search element in | |||
| * @tparam T_func predicate type to execute for each element | |||
| * | |||
| * @param list list to search element in | |||
| * @param func predicate to execute for each element | |||
| * | |||
| * @return integral constant of the index the element where found | |||
| */ | |||
| template<typename T_list, typename T_func> | |||
| constexpr decltype(auto) operator()(T_list list, T_func&& func) const noexcept | |||
| { | |||
| @@ -102,7 +182,9 @@ beg_namespace_ecs_core_mp_list | |||
| }; | |||
| } | |||
| constexpr decltype(auto) index_of_first_matching = __impl::index_of_first_matching_t { }; | |||
| /** | |||
| * predicate to get the index of an element of a list that first matches the given predicate | |||
| */ | |||
| constexpr decltype(auto) index_of_first_matching = detail::index_of_first_matching_t { }; | |||
| } | |||
| end_namespace_ecs_core_mp_list | |||
| } } } } | |||
| @@ -3,11 +3,20 @@ | |||
| #include <ecs/config.h> | |||
| #include "./core/decay.h" | |||
| beg_namespace_ecs_core_mp_option_map | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| namespace option_map { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * Helper template to replace a given element of the options map. | |||
| * Will forward the new pair if it was not set already. | |||
| * | |||
| * @tparam T_new_pair type of the new data pair | |||
| */ | |||
| template<typename T_new_pair> | |||
| struct replace_helper_t | |||
| { | |||
| @@ -15,6 +24,15 @@ beg_namespace_ecs_core_mp_option_map | |||
| const new_pair_type& new_pair; | |||
| /** | |||
| * execute the helper | |||
| * | |||
| * @tparam T_pair type of the existing pair | |||
| * | |||
| * @param pair existing pair | |||
| * | |||
| * @return The new pair if it is not already set, the old pair otherwise. | |||
| */ | |||
| template<typename T_pair> | |||
| constexpr decltype(auto) operator()(T_pair&& pair) const noexcept | |||
| { | |||
| @@ -25,8 +43,22 @@ beg_namespace_ecs_core_mp_option_map | |||
| } | |||
| }; | |||
| /** | |||
| * predicate type to replace a certain pair of a list | |||
| */ | |||
| struct replace_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_map type of the map to replace a pair in | |||
| * @tparam T_new_pair type of the new pair | |||
| * | |||
| * @param map map to replace a pair in | |||
| * @param new_pair pair to replace the old pair with | |||
| * | |||
| * @return updated map | |||
| */ | |||
| template<typename T_map, typename T_new_pair> | |||
| constexpr decltype(auto) operator()(T_map&& map, T_new_pair&& new_pair) const noexcept | |||
| { | |||
| @@ -40,8 +72,14 @@ beg_namespace_ecs_core_mp_option_map | |||
| } | |||
| }; | |||
| /** | |||
| * predicate to replace a certain pair of a list | |||
| */ | |||
| constexpr decltype(auto) replace = replace_t { }; | |||
| /** | |||
| * implements an option map to store key value pairs | |||
| */ | |||
| template<typename T_map> | |||
| struct option_map_t | |||
| { | |||
| @@ -51,14 +89,43 @@ beg_namespace_ecs_core_mp_option_map | |||
| map_type _map; | |||
| public: | |||
| /** | |||
| * get the value for a certain key | |||
| * | |||
| * @tparam T_key key type to get value for | |||
| * | |||
| * @param key key to get value for | |||
| * | |||
| * @return value stored for the given key | |||
| */ | |||
| template<typename T_key> | |||
| constexpr decltype(auto) at(const T_key& key) const noexcept | |||
| { return hana::first(hana::at_key(_map, key)); } | |||
| /** | |||
| * get the value type for a certain key | |||
| * | |||
| * @tparam T_key key type to get value type for | |||
| * | |||
| * @param key key to get value type for | |||
| * | |||
| * @return value type stored for the given key | |||
| */ | |||
| template<typename T_key> | |||
| constexpr decltype(auto) type_at(const T_key& key) const noexcept | |||
| { return hana::type_c<decay_t<decltype(at(key))>>; } | |||
| /** | |||
| * add a new key to the map | |||
| * | |||
| * @tparam T_key key type to add | |||
| * @tparam T_value value type of the initial value | |||
| * | |||
| * @param key key to add | |||
| * @param value initial value | |||
| * | |||
| * @return the updated option map | |||
| */ | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) add(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| @@ -72,6 +139,17 @@ beg_namespace_ecs_core_mp_option_map | |||
| return option_map_t<decltype(new_map)> { }; | |||
| } | |||
| /** | |||
| * set the value of a certain key (values can only be set once!) | |||
| * | |||
| * @tparam T_key type of the key to set value for | |||
| * @tparam T_value type of the value to set | |||
| * | |||
| * @param key key to set value for | |||
| * @param value value to set | |||
| * | |||
| * @return the updated option map | |||
| */ | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) set(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| @@ -89,14 +167,24 @@ beg_namespace_ecs_core_mp_option_map | |||
| } | |||
| }; | |||
| /** | |||
| * predicate type to create a empty option map | |||
| */ | |||
| struct make_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @return empty option map | |||
| */ | |||
| constexpr decltype(auto) operator()() const noexcept | |||
| { return option_map_t<decay_t<decltype(hana::make_map())>> { }; } | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make = __impl::make_t { }; | |||
| /** | |||
| * predicate to create a empty option map | |||
| */ | |||
| constexpr decltype(auto) make = detail::make_t { }; | |||
| } | |||
| end_namespace_ecs_core_mp_option_map | |||
| } } } } | |||
| @@ -3,22 +3,40 @@ | |||
| #include <ecs/config.h> | |||
| #include "./core/decay.h" | |||
| beg_namespace_ecs_core_mp_tag | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace mp { | |||
| namespace tag { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * predicate type to create a tag of a given type | |||
| * | |||
| * @tparam T_tag tag to create | |||
| */ | |||
| template<template<typename> class T_tag> | |||
| struct make_t | |||
| { | |||
| template<typename T_component> | |||
| constexpr decltype(auto) operator()(T_component&&) const noexcept | |||
| { return T_tag<core::mp::decay_t<T_component>> { }; } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T type to create tag for | |||
| * | |||
| * @return tag of the given type | |||
| */ | |||
| template<typename T> | |||
| constexpr decltype(auto) operator()(T&&) const noexcept | |||
| { return T_tag<core::mp::decay_t<T>> { }; } | |||
| }; | |||
| } | |||
| /** | |||
| * predicate to create a tag of a given type | |||
| * | |||
| * @tparam T_tag tag to create | |||
| */ | |||
| template<template<typename> class T_tag> | |||
| constexpr decltype(auto) make = __impl::make_t<T_tag> { }; | |||
| constexpr decltype(auto) make = detail::make_t<T_tag> { }; | |||
| } | |||
| end_namespace_ecs_core_mp_tag | |||
| } } } } | |||
| @@ -1,5 +1,6 @@ | |||
| #pragma once | |||
| #include "./system/data_proxy.h" | |||
| #include "./system/instance.h" | |||
| #include "./system/parallelism.h" | |||
| #include "./system/scheduler.h" | |||
| @@ -0,0 +1,7 @@ | |||
| #pragma once | |||
| #include "./data_proxy/base.h" | |||
| #include "./data_proxy/single.h" | |||
| #include "./data_proxy/base.inl" | |||
| #include "./data_proxy/single.inl" | |||
| @@ -0,0 +1,54 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace data_proxy { | |||
| /** | |||
| * base class for the data proxies | |||
| * | |||
| * @tparam T_context context type of the ECS environment | |||
| * @tparam T_instance system instance type | |||
| */ | |||
| template<typename T_context, typename T_instance> | |||
| struct base | |||
| { | |||
| protected: | |||
| using context_type = T_context; | |||
| using instance_type = T_instance; | |||
| using system_signature_type = typename instance_type::system_signature_type; | |||
| using handle_type = typename context_type::handle_type; | |||
| protected: | |||
| context_type& context; | |||
| instance_type& instance; | |||
| public: | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param context context of the ECS environment | |||
| * @param instance system instance | |||
| */ | |||
| inline base(context_type& p_context, instance_type& p_instance); | |||
| /** | |||
| * get a reference to the requested component | |||
| * | |||
| * @tparam T_component_tag component tag type to get | |||
| * | |||
| * @param ct component tag to get the component for | |||
| * @param handle handle of the entity to get the component for | |||
| * | |||
| * @return reference to the requested component | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) get_component(T_component_tag ct, const handle_type& handle); | |||
| }; | |||
| } } } } | |||
| @@ -0,0 +1,39 @@ | |||
| #pragma once | |||
| #include <ecs/core/system/data_proxy/base.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace data_proxy { | |||
| template<typename T_context, typename T_instance> | |||
| inline base<T_context, T_instance> | |||
| ::base(T_context& p_context, T_instance& p_instance) | |||
| : context (p_context) | |||
| , instance (p_instance) | |||
| { } | |||
| template<typename T_context, typename T_instance> | |||
| template<typename T_component_tag> | |||
| inline decltype(auto) base<T_context, T_instance> | |||
| ::get_component(T_component_tag ct, const handle_type& handle) | |||
| { | |||
| using component_tag_type = T_component_tag; | |||
| constexpr auto can_write = mp::unwrap(system_signature_type { }).can_write(component_tag_type { }); | |||
| constexpr auto can_read = mp::unwrap(system_signature_type { }).can_read(component_tag_type { }); | |||
| static_assert(can_write || can_read, "access to this component is denied for this system!"); | |||
| return hana::eval_if( | |||
| can_write, | |||
| [this, &handle](auto _) -> auto& { | |||
| return _(context).get_component(handle, component_tag_type { }); | |||
| }, | |||
| [this, &handle](auto _) -> const auto& { | |||
| return _(context).get_component(handle, component_tag_type { }); | |||
| }); | |||
| } | |||
| } } } } | |||
| @@ -0,0 +1,95 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/data_proxy/base.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace data_proxy { | |||
| /** | |||
| * data proxy to execute system non-parallel | |||
| * | |||
| * @tparam T_context context type of the ECS environment | |||
| * @tparam T_instance system instance type | |||
| */ | |||
| template<typename T_context, typename T_instance> | |||
| struct single | |||
| : public base<T_context, T_instance> | |||
| { | |||
| private: | |||
| using context_type = T_context; | |||
| using instance_type = T_instance; | |||
| using base_type = base<context_type, instance_type>; | |||
| public: | |||
| using base_type::base_type; | |||
| /** | |||
| * execute the given function for all entities handled by this data proxy | |||
| * | |||
| * @tparam T_func function type to execute | |||
| * | |||
| * @param func function to execute | |||
| */ | |||
| template<typename T_func> | |||
| void for_entities(T_func&& func) const; | |||
| /** | |||
| * execute the given function for all entities not handled by this data proxy | |||
| * | |||
| * @tparam T_func function type to execute | |||
| * | |||
| * @param func function to execute | |||
| */ | |||
| template<typename T_func> | |||
| void for_other_entities(T_func&& func) const; | |||
| /** | |||
| * execute the given function for all entities | |||
| * | |||
| * @tparam T_func function type to execute | |||
| * | |||
| * @param func function to execute | |||
| */ | |||
| template<typename T_func> | |||
| void for_all_entities(T_func&& func) const; | |||
| /** | |||
| * get the number of entities handled by this data proxy | |||
| * | |||
| * @return number of entities handle by this data proxy | |||
| */ | |||
| inline size_t entity_count() const; | |||
| /** | |||
| * get the number of entities not handled by this data proxy | |||
| * | |||
| * @return number of entities not handle by this data proxy | |||
| */ | |||
| inline size_t other_entity_count() const; | |||
| /** | |||
| * get the number of all entities | |||
| * | |||
| * @return number of all entities | |||
| */ | |||
| inline size_t all_entity_count() const; | |||
| }; | |||
| /** | |||
| * create a single data proxy (data proxy that does not execute in parallel) | |||
| * | |||
| * @tparam T_context context type | |||
| * @tparam T_instance instance type | |||
| * | |||
| * @param context context | |||
| * @param instance instance | |||
| * | |||
| * @return single data proxy | |||
| */ | |||
| template<typename T_context, typename T_instance> | |||
| constexpr decltype(auto) make_single(T_context&& context, T_instance&& instance); | |||
| } } } } | |||
| @@ -0,0 +1,70 @@ | |||
| #pragma once | |||
| #include <ecs/core/system/data_proxy/single.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace data_proxy { | |||
| /* single */ | |||
| template<typename T_context, typename T_instance> | |||
| template<typename T_func> | |||
| void single<T_context, T_instance> | |||
| ::for_entities(T_func&& func) const | |||
| { | |||
| for (auto& e : this->instance.subscribed()) | |||
| { | |||
| func(e); | |||
| } | |||
| } | |||
| template<typename T_context, typename T_instance> | |||
| template<typename T_func> | |||
| void single<T_context, T_instance> | |||
| ::for_other_entities(T_func&& func) const | |||
| { } | |||
| template<typename T_context, typename T_instance> | |||
| template<typename T_func> | |||
| void single<T_context, T_instance> | |||
| ::for_all_entities(T_func&& func) const | |||
| { | |||
| for_entities(std::forward<T_func>(func)); | |||
| } | |||
| template<typename T_context, typename T_instance> | |||
| inline size_t single<T_context, T_instance> | |||
| ::entity_count() const | |||
| { | |||
| return this->instance.subscribed_count(); | |||
| } | |||
| template<typename T_context, typename T_instance> | |||
| inline size_t single<T_context, T_instance> | |||
| ::other_entity_count() const | |||
| { | |||
| return 0; | |||
| } | |||
| template<typename T_context, typename T_instance> | |||
| inline size_t single<T_context, T_instance> | |||
| ::all_entity_count() const | |||
| { | |||
| return entity_count(); | |||
| } | |||
| /* make_single */ | |||
| template<typename T_context, typename T_instance> | |||
| constexpr decltype(auto) make_single(T_context&& context, T_instance&& instance) | |||
| { | |||
| using context_type = mp::decay_t<T_context>; | |||
| using instance_type = mp::decay_t<T_instance>; | |||
| return single<context_type, instance_type>( | |||
| std::forward<T_context>(context), | |||
| std::forward<T_instance>(instance)); | |||
| } | |||
| } } } } | |||
| @@ -4,9 +4,11 @@ | |||
| #include <ecs/signature/system.h> | |||
| #include <ecs/core/utils/bitset.h> | |||
| #include <ecs/core/utils/fixed_function.h> | |||
| #include <ecs/core/utils/ordered_vector.h> | |||
| beg_namespace_ecs_core_system | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| template<typename T_settings> | |||
| struct deferred_function_vector | |||
| @@ -55,7 +57,7 @@ beg_namespace_ecs_core_system | |||
| using bitset_type = utils::bitset<settings_type>; | |||
| using entity_handle_type = T_entity_handle; | |||
| using executor_type = decltype(mp::unwrap(system_signature_type { }).parallelism()()); | |||
| using entity_handle_set_type = std::set<entity_handle_type>; | |||
| using entity_handle_set_type = utils::ordered_vector<entity_handle_type>; | |||
| struct state_t | |||
| : public output_type | |||
| @@ -114,6 +116,12 @@ beg_namespace_ecs_core_system | |||
| } | |||
| public: | |||
| inline void prepare_states(size_t n) | |||
| { | |||
| assert(n > 0); | |||
| clear_and_prepare(n); | |||
| } | |||
| template<typename T_context, typename T_func> | |||
| inline void prepare_and_wait_subtasks(T_context& context, size_t n, T_func& func) | |||
| { | |||
| @@ -125,7 +133,7 @@ beg_namespace_ecs_core_system | |||
| return [this, &b, &context, &f](auto&&... xs) { | |||
| context.post_in_thread_pool([&f, &b, xs...]() { | |||
| ecs_make_scope_guard([&b](){ | |||
| b.decrement_and_notify_one(); | |||
| b.decrement(); | |||
| }); | |||
| f(xs...); | |||
| }); | |||
| @@ -137,6 +145,10 @@ beg_namespace_ecs_core_system | |||
| }); | |||
| } | |||
| template<typename T_context, typename T_func> | |||
| inline void execute(T_context& context, T_func&& func) | |||
| { _executor(context, *this, std::forward<T_func>(func)); } | |||
| public: /* bitset */ | |||
| inline const auto& bitset() const noexcept | |||
| { return _bitset; } | |||
| @@ -162,7 +174,7 @@ beg_namespace_ecs_core_system | |||
| { return (_subscribed.find(handle) != _subscribed.end()); } | |||
| inline void subscribe(const entity_handle_type& handle) | |||
| { _subscribed.emplace(handle); } | |||
| { _subscribed.insert(handle); } | |||
| inline void unsubscribe(const entity_handle_type& handle) | |||
| { _subscribed.erase(handle); } | |||
| @@ -197,5 +209,4 @@ beg_namespace_ecs_core_system | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_system | |||
| } } } | |||
| @@ -0,0 +1,3 @@ | |||
| #pragma once | |||
| #include "./composer/fixed_threshold.h" | |||
| @@ -2,8 +2,10 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace parallelism { | |||
| template<typename T_parameters> | |||
| struct fixed_threshold | |||
| @@ -15,20 +17,13 @@ beg_namespace_ecs_core_parallelism | |||
| strategy_lower_type _strategy_lower; | |||
| strategy_greater_type _strategy_greater; | |||
| template<typename T_instance, typename... T_args> | |||
| inline void operator()(T_instance& instance, T_args&&... args) const | |||
| template<typename T_context, typename T_instance, typename T_func> | |||
| inline void operator()(T_context& context, T_instance& instance, T_func&& func) const | |||
| { | |||
| bool threshold_reached = false; // TODO | |||
| if (!threshold_reached) | |||
| { | |||
| _strategy_lower(instance, std::forward<T_args>(args)...); | |||
| } | |||
| else | |||
| { | |||
| _strategy_greater(instance, std::forward<T_args>(args)...); | |||
| } | |||
| if (!threshold_reached) _strategy_lower (instance, context, std::forward<T_func>(func)); | |||
| else _strategy_greater(instance, context, std::forward<T_func>(func)); | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_parallelism | |||
| } } } } | |||
| @@ -1,18 +1,39 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/data_proxy.h> | |||
| beg_namespace_ecs_core_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace parallelism { | |||
| struct none | |||
| { | |||
| template<typename T_instance, typename T_context, typename T_func> | |||
| inline void operator()(T_instance& instance, T_context& context, T_func&& func) const | |||
| template<typename T_context, typename T_instance> | |||
| struct executor_proxy | |||
| { | |||
| // TODO | |||
| using context_type = T_context; | |||
| using instance_type = T_instance; | |||
| context_type& context; | |||
| instance_type& instance; | |||
| template<typename T_func> | |||
| inline void for_subtasks(T_func&& func) | |||
| { | |||
| instance.prepare_states(1); | |||
| auto data = data_proxy::make_single(context, instance); | |||
| func(data); | |||
| } | |||
| }; | |||
| template<typename T_context, typename T_instance, typename T_func> | |||
| inline void operator()(T_context& context, T_instance& instance, T_func&& func) const | |||
| { | |||
| executor_proxy<T_context, T_instance> ep { context, instance }; | |||
| func(instance, ep); | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_parallelism | |||
| } } } } | |||
| @@ -2,8 +2,10 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace parallelism { | |||
| template<typename T_parameters> | |||
| struct split_evenly | |||
| @@ -13,10 +15,9 @@ beg_namespace_ecs_core_parallelism | |||
| template<typename T_context, typename T_instance, typename T_func> | |||
| inline void operator()(T_context& context, T_instance& instance, T_func&& func) const | |||
| { | |||
| // auto split_count = parameters_type::get_split_count(); | |||
| // TODO | |||
| // auto split_count = parameters_type::get_split_count(); | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_parallelism | |||
| } } } } | |||
| @@ -2,8 +2,10 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace parallelism { | |||
| template<typename T_parameters> | |||
| struct split_every | |||
| @@ -13,9 +15,9 @@ beg_namespace_ecs_core_parallelism | |||
| template<typename T_context, typename T_instance, typename T_func> | |||
| inline void operator()(T_context& context, T_instance& instance, T_func&& func) const | |||
| { | |||
| // TODO | |||
| // auto per_split = parameters_type::get_per_split_count(); | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_parallelism | |||
| } } } } | |||
| @@ -1,3 +1,5 @@ | |||
| #pragma once | |||
| #include "./scheduler/atomic_counter.h" | |||
| #include "./scheduler/atomic_counter.h" | |||
| #include "./scheduler/atomic_counter.inl" | |||
| @@ -1,26 +1,43 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| #include <ecs/tag/system.h> | |||
| #include <ecs/context/context.fwd.h> | |||
| beg_namespace_ecs_core_system_scheduler | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace scheduler { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<typename T_system_tag, typename T_execute, typename T_dependency_ids> | |||
| /** | |||
| * struct to wrap some system meta data | |||
| */ | |||
| template< | |||
| typename T_system_tag, | |||
| typename T_execute, | |||
| typename T_dependency_ids> | |||
| struct dependency_item_t | |||
| { | |||
| using system_tag_type = T_system_tag; | |||
| using execute_type = T_execute; | |||
| using dependency_ids_type = T_dependency_ids; | |||
| system_tag_type tag; | |||
| execute_type execute; | |||
| dependency_ids_type dependency_ids; | |||
| system_tag_type tag; //!< tag of system this dependency element represents | |||
| execute_type execute; //!< boolean value to determine if the system should be executed or not | |||
| dependency_ids_type dependency_ids; //!< list of system IDs that depends on this system/item | |||
| }; | |||
| template<typename T_dependency_item, typename T_dependent_ids> | |||
| /** | |||
| * defines a task to be executed (represents a system to execute in the scheduler) | |||
| * | |||
| * @tparam T_dependency_item dependency item type for this task | |||
| * @tparam T_dependent_ids id types of tasks that depends on this task | |||
| */ | |||
| template< | |||
| typename T_dependency_item, | |||
| typename T_dependent_ids> | |||
| struct task_t | |||
| { | |||
| public: | |||
| @@ -32,50 +49,76 @@ beg_namespace_ecs_core_system_scheduler | |||
| std::atomic<size_t> _dependency_count; | |||
| public: | |||
| task_t() | |||
| : _dependency_count(hana::value(hana::size(mp::unwrap(dependency_item_type { }).dependency_ids))) | |||
| { } | |||
| inline size_t dependency_count() const | |||
| { return _dependency_count; } | |||
| inline bool decrement_and_check() | |||
| { return (--_dependency_count == 0); } | |||
| mp::unwrap_t<dependency_item_type> dependency_item; | |||
| /** | |||
| * constructor | |||
| */ | |||
| inline task_t(); | |||
| /** | |||
| * get the number unfinished tasks, this task depends on | |||
| */ | |||
| inline size_t dependency_count() const; | |||
| /** | |||
| * decrepent the unfished task counter | |||
| * | |||
| * @retval TRUE if all tasks, this task depends on, are finished | |||
| * @retval FALSE if at least on task, this task depends on, is not finished | |||
| */ | |||
| inline bool decrement_and_check(); | |||
| /** | |||
| * execute the passed function for task ID that depends on this task | |||
| * | |||
| * @tparam T_func function type to execute | |||
| * | |||
| * @param func function to execute | |||
| */ | |||
| template<typename T_func> | |||
| constexpr decltype(auto) for_dependent_ids(T_func&& func) const noexcept | |||
| { hana::for_each(dependent_ids_type { }, std::forward<T_func>(func)); } | |||
| constexpr decltype(auto) for_dependent_ids(T_func&& func) const noexcept; | |||
| }; | |||
| /** | |||
| * predicate type to create a list of task IDs that depends on the given task | |||
| */ | |||
| struct dependent_ids_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_dependency_items list of dependency items | |||
| * @tparam T_dependency_item dependency item to get taks IDs for, that depends on that item | |||
| * | |||
| * @return list of task IDs that depends on the given dependency item | |||
| */ | |||
| template<typename T_dependency_items, typename T_dependency_item> | |||
| constexpr decltype(auto) operator()(T_dependency_items, T_dependency_item) const noexcept | |||
| { | |||
| auto item_id = mp::list::index_of(T_dependency_items { }, T_dependency_item { }); | |||
| return hana::fold_right(T_dependency_items { }, mp::list::empty, [=](auto other, auto acc){ | |||
| return hana::if_( | |||
| hana::contains(mp::unwrap(other).dependency_ids, item_id), | |||
| hana::append(acc, mp::list::index_of(T_dependency_items { }, other)), | |||
| acc); | |||
| }); | |||
| } | |||
| constexpr decltype(auto) operator()(T_dependency_items, T_dependency_item) const noexcept; | |||
| }; | |||
| /** | |||
| * predicate type to create a list of tasks | |||
| */ | |||
| struct make_tasks_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_dependency_items list of dependency items to create tasks from | |||
| * | |||
| * @return list of tasks | |||
| */ | |||
| template<typename T_dependency_items> | |||
| constexpr decltype(auto) operator()(T_dependency_items) const noexcept | |||
| { | |||
| return hana::transform(T_dependency_items { }, [](auto item){ | |||
| using item_type = mp::decay_t<decltype(item)>; | |||
| using dependent_ids_type = mp::decay_t<decltype((dependent_ids_t { })(T_dependency_items { }, item_type { }))>; | |||
| using task_type = task_t<item_type, dependent_ids_type>; | |||
| return hana::type_c<task_type>; | |||
| }); | |||
| } | |||
| constexpr decltype(auto) operator()(T_dependency_items) const noexcept; | |||
| }; | |||
| /** | |||
| * defines a group of tasks to execute with | |||
| * | |||
| * @tparam T_context context type | |||
| * @tparam T_dependency_items list of dependency items | |||
| */ | |||
| template<typename T_context, typename T_dependency_items> | |||
| struct task_group_t | |||
| { | |||
| @@ -89,144 +132,93 @@ beg_namespace_ecs_core_system_scheduler | |||
| using tasks_tuple_type = mp::list::unwrap_t<hana::tuple, task_types>; | |||
| private: | |||
| context_type& _context; | |||
| utils::counter_blocker& _counter; | |||
| tasks_tuple_type _tasks; | |||
| context_type& _context; //!< context to use for system execution | |||
| utils::counter_blocker& _counter; //!< counter to track running tasks | |||
| tasks_tuple_type _tasks; //!< tuple of all tasks | |||
| public: | |||
| inline task_group_t(context_type& p_context, utils::counter_blocker& p_counter) | |||
| : _context(p_context) | |||
| , _counter(p_counter) | |||
| { } | |||
| template<typename T_task_id> | |||
| inline void start_task(T_task_id id) | |||
| { | |||
| using task_type = mp::decay_t<decltype(hana::at(_tasks, id))>; | |||
| using dependency_item_type = typename task_type::dependency_item_type; | |||
| _counter.increment(); | |||
| hana::eval_if( | |||
| mp::unwrap(dependency_item_type { }).execute, | |||
| [this](auto _) { _(this)->post_task_in_thread_pool (T_task_id { }); }, | |||
| [this](auto _) { _(this)->check_and_start_dependencies(T_task_id { }); }); | |||
| } | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param p_context context to use for system execution | |||
| * @param p_counter counter to track running tasks | |||
| */ | |||
| inline task_group_t(context_type& p_context, utils::counter_blocker& p_counter); | |||
| /** | |||
| * start the given task | |||
| * | |||
| * @tparam T_task_id id type of the task to execute | |||
| * @tparam T_func overloaded function type to execute system with | |||
| * | |||
| * @param id id of task to execute | |||
| * @param func overloaded function to execute system with | |||
| */ | |||
| template<typename T_task_id, typename T_func> | |||
| inline void start_task(T_task_id id, T_func&& func); | |||
| private: | |||
| template<typename T_task_id> | |||
| inline void post_task_in_thread_pool(T_task_id) | |||
| { | |||
| _context.post_in_thread_pool([this]{ | |||
| execute_task(T_task_id { }); | |||
| }); | |||
| } | |||
| template<typename T_task_id> | |||
| inline void execute_task(T_task_id) | |||
| { | |||
| check_and_start_dependencies(T_task_id { }); | |||
| } | |||
| template<typename T_task_id> | |||
| inline void check_and_start_dependencies(T_task_id) | |||
| { | |||
| auto& task = hana::at(_tasks, T_task_id { }); | |||
| task.for_dependent_ids([this](auto id){ | |||
| auto& other = hana::at(_tasks, id); | |||
| if (other.decrement_and_check()) { | |||
| start_task(id); | |||
| } | |||
| }); | |||
| _counter.decrement_and_notify_one(); | |||
| } | |||
| /** | |||
| * execute the task using the thread pool | |||
| * | |||
| * @tparam T_task_id id type of task to execute | |||
| * @tparam T_func overloaded function type to execute system with | |||
| * | |||
| * @param func overloaded function to execute system with | |||
| */ | |||
| template<typename T_task_id, typename T_func> | |||
| inline void post_task_in_thread_pool(T_task_id, T_func&& func); | |||
| /** | |||
| * execute the actual task using the passed overloaded function | |||
| * | |||
| * @tparam T_task_id id type of task to execute | |||
| * @tparam T_func overloaded function type to execute system with | |||
| * | |||
| * @param func overloaded function to execute system with | |||
| */ | |||
| template<typename T_task_id, typename T_func> | |||
| inline void execute_task(T_task_id, T_func&& func); | |||
| /** | |||
| * update the dependencies of the current task and start the next task | |||
| * | |||
| * @tparam T_task_id id type of task to update dependencies for | |||
| * @tparam T_func overloaded function type to execute system with | |||
| * | |||
| * @param func overloaded function to execute system with | |||
| */ | |||
| template<typename T_task_id, typename T_func> | |||
| inline void check_and_start_dependencies(T_task_id, T_func&& func); | |||
| }; | |||
| /** | |||
| * create a list of dependency items | |||
| * | |||
| * @tparam T_system_signature_list system signature list type | |||
| * @tparam T_system_tag_list list of system tag types | |||
| * | |||
| * @return list of dependency items | |||
| */ | |||
| template<typename T_system_signature_list, typename T_system_tag_list> | |||
| constexpr decltype(auto) build_dependency_list(T_system_signature_list, T_system_tag_list) noexcept | |||
| { | |||
| using system_signature_list_type = T_system_signature_list; | |||
| using system_signature_tuple_type = mp::decay_t<decltype((system_signature_list_type { }).items)>; | |||
| using system_tag_list_type = T_system_tag_list; | |||
| auto filtered = (system_signature_list_type { }) | |||
| .filter([](auto ssig){ | |||
| return hana::contains(system_tag_list_type { }, mp::unwrap(ssig).tag()); | |||
| }); | |||
| using filtered_ssig_list_type = mp::decay_t<decltype(filtered)>; | |||
| (filtered_ssig_list_type { }).for_each([](auto ssig){ | |||
| using ssig_type = mp::decay_t<decltype(ssig)>; | |||
| hana::for_each(mp::unwrap(ssig).write(), [](auto tag){ | |||
| using tag_type = mp::decay_t<decltype(tag)>; | |||
| (filtered_ssig_list_type { }).for_each([](auto other){ | |||
| using check_dependencies_type = decltype( | |||
| hana::or_( | |||
| hana::equal(ssig_type { }, other), | |||
| (T_system_signature_list { }).depends_on(ssig_type { }, mp::unwrap(other).tag()), | |||
| (T_system_signature_list { }).depends_on(other, mp::unwrap(ssig_type { }).tag()), | |||
| hana::not_(hana::contains(mp::unwrap(other).write(), tag_type { })))); | |||
| static_assert( | |||
| check_dependencies_type { }, | |||
| "independent systems can not share write access to component"); | |||
| }); | |||
| }); | |||
| }); | |||
| return hana::transform(system_signature_tuple_type { }, [](auto ssig){ | |||
| using ssig_type = mp::decay_t<decltype(ssig)>; | |||
| auto tag = mp::unwrap(ssig).tag(); | |||
| auto deps = mp::unwrap(ssig).dependencies(); | |||
| auto execute = hana::contains(system_tag_list_type { }, mp::unwrap(ssig).tag()); | |||
| auto read_deps = | |||
| hana::transform(mp::unwrap(ssig).read(), [](auto read_tag){ | |||
| using read_tag_type = mp::decay_t<decltype(read_tag)>; | |||
| return hana::transform( | |||
| hana::filter((filtered_ssig_list_type { }).items, [](auto other){ | |||
| return hana::and_( | |||
| hana::not_equal(ssig_type { }, other), | |||
| hana::contains(mp::unwrap(other).write(), read_tag_type { })); | |||
| }), | |||
| [](auto other){ | |||
| return mp::unwrap(other).tag(); | |||
| }); | |||
| }); | |||
| auto final_deps = | |||
| hana::transform( | |||
| hana::fold( | |||
| hana::if_( | |||
| hana::size(read_deps) == hana::size_c<0>, | |||
| hana::make_basic_tuple(hana::make_basic_tuple()), | |||
| read_deps), | |||
| deps, | |||
| hana::concat), | |||
| [](auto tmp_tag){ | |||
| return (system_signature_list_type { }).id_by_tag(tmp_tag); | |||
| }); | |||
| using tag_type = mp::decay_t<decltype(tag)>; | |||
| using dep_type = mp::decay_t<decltype(final_deps)>; | |||
| using execute_type = mp::decay_t<decltype(execute)>; | |||
| using item_type = dependency_item_t<tag_type, execute_type, dep_type>; | |||
| return hana::type_c<item_type>; | |||
| }); | |||
| } | |||
| constexpr decltype(auto) build_dependency_list(T_system_signature_list, T_system_tag_list) noexcept; | |||
| /** | |||
| * get a list of independent items from the passed dependency list | |||
| * | |||
| * @tparam T_dependency_list dependency item list to get independent items from | |||
| * | |||
| * @return list of independent items | |||
| */ | |||
| template<typename T_dependency_list> | |||
| constexpr decltype(auto) get_independent_item_ids(T_dependency_list) noexcept | |||
| { | |||
| return | |||
| hana::transform( | |||
| hana::filter(T_dependency_list { }, [](auto item){ | |||
| return hana::size(mp::unwrap(item).dependency_ids) == hana::size_c<0>; | |||
| }), | |||
| [](auto item){ | |||
| return mp::list::index_of(T_dependency_list { }, item); | |||
| }); | |||
| } | |||
| constexpr decltype(auto) get_independent_item_ids(T_dependency_list) noexcept; | |||
| } | |||
| /** | |||
| * scheduler that resolves the dependencies between systems and then executes the systems in a save way in parallel | |||
| * | |||
| * @tparam T_settings settings type the environment is configured with | |||
| */ | |||
| template<typename T_settings> | |||
| struct atomic_counter | |||
| { | |||
| @@ -238,43 +230,24 @@ beg_namespace_ecs_core_system_scheduler | |||
| context_type& _context; | |||
| public: | |||
| atomic_counter(context_type& p_context) | |||
| : _context(p_context) | |||
| { } | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param p_context context to use for system exeution | |||
| */ | |||
| inline atomic_counter(context_type& p_context); | |||
| /** | |||
| * execute the systems for that system tags are passed | |||
| * | |||
| * @tparam T_system_tag_list list of system tag type to execute systems for | |||
| * @tparam T_func overloaded function type to use for system execution | |||
| * | |||
| * @param stl list of system tags to execute systems for | |||
| * @param func overloaded function to use for system execution | |||
| */ | |||
| template<typename T_system_tag_list, typename T_func> | |||
| inline void execute(T_system_tag_list, T_func&& func) | |||
| { | |||
| static_assert(tag::system::is_list(T_system_tag_list { })); | |||
| using dependency_list_type = mp::decay_t<decltype(__impl::build_dependency_list( | |||
| (settings_type { }).system_signatures(), | |||
| T_system_tag_list { }))>; | |||
| using independent_item_ids_type = mp::decay_t<decltype(__impl::get_independent_item_ids( | |||
| dependency_list_type { }))>;; | |||
| size_t i = 0; | |||
| std::cout << "dependency_list" << std::endl; | |||
| hana::for_each(dependency_list_type { }, [&i](auto item){ | |||
| using system_type = mp::unwrap_t<mp::decay_t<decltype(mp::unwrap(item).tag)>>; | |||
| std::cout << " " << (i++) << ": " << type_helper<system_type>::name() << " (" << hana::value(mp::unwrap(item).execute) << ")" << std::endl; | |||
| hana::for_each(mp::unwrap(item).dependency_ids, [](auto id){ | |||
| std::cout << " " << hana::value(id) << std::endl; | |||
| }); | |||
| }); | |||
| using task_group_type = __impl::task_group_t<context_type, dependency_list_type>; | |||
| utils::counter_blocker counter (1); | |||
| task_group_type task_group (_context, counter); | |||
| counter.execute_and_wait_until_zero([&task_group, &counter]{ | |||
| hana::for_each(independent_item_ids_type { }, [&task_group](auto id){ | |||
| task_group.start_task(id); | |||
| }); | |||
| counter.decrement_and_notify_one(); | |||
| }); | |||
| } | |||
| inline void execute(T_system_tag_list stl, T_func&& func); | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_system_scheduler | |||
| } } } } | |||
| @@ -0,0 +1,278 @@ | |||
| #pragma once | |||
| #include <ecs/core/system/scheduler/atomic_counter.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace scheduler { | |||
| namespace detail | |||
| { | |||
| /* task_t */ | |||
| template< | |||
| typename T_dependency_item, | |||
| typename T_dependent_ids> | |||
| inline task_t<T_dependency_item, T_dependent_ids> | |||
| ::task_t() | |||
| : _dependency_count(hana::value(hana::size(mp::unwrap(dependency_item_type { }).dependency_ids))) | |||
| { } | |||
| template< | |||
| typename T_dependency_item, | |||
| typename T_dependent_ids> | |||
| inline size_t task_t<T_dependency_item, T_dependent_ids> | |||
| ::dependency_count() const | |||
| { | |||
| return _dependency_count; | |||
| } | |||
| template< | |||
| typename T_dependency_item, | |||
| typename T_dependent_ids> | |||
| inline bool task_t<T_dependency_item, T_dependent_ids> | |||
| ::decrement_and_check() | |||
| { | |||
| return (--_dependency_count == 0); | |||
| } | |||
| template< | |||
| typename T_dependency_item, | |||
| typename T_dependent_ids> | |||
| template<typename T_func> | |||
| constexpr decltype(auto) task_t<T_dependency_item, T_dependent_ids> | |||
| ::for_dependent_ids(T_func&& func) const noexcept | |||
| { | |||
| hana::for_each(dependent_ids_type { }, std::forward<T_func>(func)); | |||
| } | |||
| /* dependent_ids_t */ | |||
| template<typename T_dependency_items, typename T_dependency_item> | |||
| constexpr decltype(auto) dependent_ids_t | |||
| ::operator()(T_dependency_items, T_dependency_item) const noexcept | |||
| { | |||
| auto item_id = mp::list::index_of(T_dependency_items { }, T_dependency_item { }); | |||
| return hana::fold_right(T_dependency_items { }, mp::list::empty, [=](auto other, auto acc){ | |||
| return hana::if_( | |||
| hana::contains(mp::unwrap(other).dependency_ids, item_id), | |||
| hana::append(acc, mp::list::index_of(T_dependency_items { }, other)), | |||
| acc); | |||
| }); | |||
| } | |||
| /* make_tasks_t */ | |||
| template<typename T_dependency_items> | |||
| constexpr decltype(auto) make_tasks_t | |||
| ::operator()(T_dependency_items) const noexcept | |||
| { | |||
| return hana::transform(T_dependency_items { }, [](auto item){ | |||
| using item_type = mp::decay_t<decltype(item)>; | |||
| using dependent_ids_type = mp::decay_t<decltype((dependent_ids_t { })(T_dependency_items { }, item_type { }))>; | |||
| using task_type = task_t<item_type, dependent_ids_type>; | |||
| return hana::type_c<task_type>; | |||
| }); | |||
| } | |||
| /* task_group_t */ | |||
| template<typename T_context, typename T_dependency_items> | |||
| inline task_group_t<T_context, T_dependency_items> | |||
| ::task_group_t(context_type& p_context, utils::counter_blocker& p_counter) | |||
| : _context(p_context) | |||
| , _counter(p_counter) | |||
| { } | |||
| template<typename T_context, typename T_dependency_items> | |||
| template<typename T_task_id, typename T_func> | |||
| inline void task_group_t<T_context, T_dependency_items> | |||
| ::start_task(T_task_id id, T_func&& func) | |||
| { | |||
| using task_type = mp::decay_t<decltype(hana::at(_tasks, id))>; | |||
| using dependency_item_type = typename task_type::dependency_item_type; | |||
| _counter.increment(); | |||
| hana::eval_if( | |||
| mp::unwrap(dependency_item_type { }).execute, | |||
| [this, &func](auto _) { _(this)->post_task_in_thread_pool (T_task_id { }, func); }, | |||
| [this, &func](auto _) { _(this)->check_and_start_dependencies(T_task_id { }, func); }); | |||
| } | |||
| template<typename T_context, typename T_dependency_items> | |||
| template<typename T_task_id, typename T_func> | |||
| inline void task_group_t<T_context, T_dependency_items> | |||
| ::post_task_in_thread_pool(T_task_id, T_func&& func) | |||
| { | |||
| std::cout << "post_task_in_thread_pool (current=" << std::this_thread::get_id() << ", task=" << T_task_id::value << ")" << std::endl; | |||
| _context.post_in_thread_pool([this, &func]{ | |||
| execute_task(T_task_id { }, func); | |||
| }); | |||
| } | |||
| template<typename T_context, typename T_dependency_items> | |||
| template<typename T_task_id, typename T_func> | |||
| inline void task_group_t<T_context, T_dependency_items> | |||
| ::execute_task(T_task_id, T_func&& func) | |||
| { | |||
| std::cout << "execute_task (current=" << std::this_thread::get_id() << ", task=" << T_task_id::value << ")" << std::endl; | |||
| auto& task = hana::at(_tasks, T_task_id { }); | |||
| auto& instance = _context.instance_by_tag(task.dependency_item.tag); | |||
| instance.execute(_context, func); | |||
| check_and_start_dependencies(T_task_id { }, func); | |||
| } | |||
| template<typename T_context, typename T_dependency_items> | |||
| template<typename T_task_id, typename T_func> | |||
| inline void task_group_t<T_context, T_dependency_items> | |||
| ::check_and_start_dependencies(T_task_id, T_func&& func) | |||
| { | |||
| std::cout << "check_and_start_dependencies (current=" << std::this_thread::get_id() << ", task=" << T_task_id::value << ")" << std::endl; | |||
| auto& task = hana::at(_tasks, T_task_id { }); | |||
| task.for_dependent_ids([this, &func](auto id){ | |||
| auto& other = hana::at(_tasks, id); | |||
| if (other.decrement_and_check()) { | |||
| start_task(id, func); | |||
| } | |||
| }); | |||
| _counter.decrement(); | |||
| } | |||
| /* misc */ | |||
| template<typename T_system_signature_list, typename T_system_tag_list> | |||
| constexpr decltype(auto) build_dependency_list(T_system_signature_list, T_system_tag_list) noexcept | |||
| { | |||
| using system_signature_list_type = T_system_signature_list; | |||
| using system_signature_tuple_type = mp::decay_t<decltype((system_signature_list_type { }).items)>; | |||
| using system_tag_list_type = T_system_tag_list; | |||
| /* filter system signature list by the passed system tags */ | |||
| auto filtered = (system_signature_list_type { }) | |||
| .filter([](auto ssig){ | |||
| return hana::contains(system_tag_list_type { }, mp::unwrap(ssig).tag()); | |||
| }); | |||
| using filtered_ssig_list_type = mp::decay_t<decltype(filtered)>; | |||
| /* check if any systems that does not depend on each other, wants to get write access to a component */ | |||
| (filtered_ssig_list_type { }).for_each([](auto ssig){ | |||
| using ssig_type = mp::decay_t<decltype(ssig)>; | |||
| hana::for_each(mp::unwrap(ssig).write(), [](auto tag){ | |||
| using tag_type = mp::decay_t<decltype(tag)>; | |||
| (filtered_ssig_list_type { }).for_each([](auto other){ | |||
| using check_dependencies_type = decltype( | |||
| hana::or_( | |||
| hana::equal(ssig_type { }, other), | |||
| (T_system_signature_list { }).depends_on(ssig_type { }, mp::unwrap(other).tag()), | |||
| (T_system_signature_list { }).depends_on(other, mp::unwrap(ssig_type { }).tag()), | |||
| hana::not_(hana::contains(mp::unwrap(other).write(), tag_type { })))); | |||
| static_assert( | |||
| check_dependencies_type { }, | |||
| "independent systems can not share write access to component"); | |||
| }); | |||
| }); | |||
| }); | |||
| /* build the actual dependency item list */ | |||
| return hana::transform(system_signature_tuple_type { }, [](auto ssig){ | |||
| using ssig_type = mp::decay_t<decltype(ssig)>; | |||
| auto tag = mp::unwrap(ssig).tag(); | |||
| auto deps = mp::unwrap(ssig).dependencies(); | |||
| auto execute = hana::contains(system_tag_list_type { }, mp::unwrap(ssig).tag()); | |||
| auto read_deps = | |||
| hana::transform(mp::unwrap(ssig).read(), [](auto read_tag){ | |||
| using read_tag_type = mp::decay_t<decltype(read_tag)>; | |||
| return hana::transform( | |||
| hana::filter((filtered_ssig_list_type { }).items, [](auto other){ | |||
| return hana::and_( | |||
| hana::not_equal(ssig_type { }, other), | |||
| hana::contains(mp::unwrap(other).write(), read_tag_type { })); | |||
| }), | |||
| [](auto other){ | |||
| return mp::unwrap(other).tag(); | |||
| }); | |||
| }); | |||
| auto final_deps = | |||
| hana::transform( | |||
| hana::fold( | |||
| hana::if_( | |||
| hana::size(read_deps) == hana::size_c<0>, | |||
| hana::make_basic_tuple(hana::make_basic_tuple()), | |||
| read_deps), | |||
| deps, | |||
| hana::concat), | |||
| [](auto tmp_tag){ | |||
| return (system_signature_list_type { }).id_by_tag(tmp_tag); | |||
| }); | |||
| using tag_type = mp::decay_t<decltype(tag)>; | |||
| using dep_type = mp::decay_t<decltype(final_deps)>; | |||
| using execute_type = mp::decay_t<decltype(execute)>; | |||
| using item_type = dependency_item_t<tag_type, execute_type, dep_type>; | |||
| return hana::type_c<item_type>; | |||
| }); | |||
| } | |||
| template<typename T_dependency_list> | |||
| constexpr decltype(auto) get_independent_item_ids(T_dependency_list) noexcept | |||
| { | |||
| return | |||
| hana::transform( | |||
| hana::filter(T_dependency_list { }, [](auto item){ | |||
| return hana::size(mp::unwrap(item).dependency_ids) == hana::size_c<0>; | |||
| }), | |||
| [](auto item){ | |||
| return mp::list::index_of(T_dependency_list { }, item); | |||
| }); | |||
| } | |||
| } | |||
| template<typename T_settings> | |||
| inline atomic_counter<T_settings> | |||
| ::atomic_counter(context_type& p_context) | |||
| : _context(p_context) | |||
| { } | |||
| template<typename T_settings> | |||
| template<typename T_system_tag_list, typename T_func> | |||
| inline void atomic_counter<T_settings> | |||
| ::execute(T_system_tag_list stl, T_func&& func) | |||
| { | |||
| static_assert(tag::system::is_list(T_system_tag_list { })); | |||
| using dependency_list_type = mp::decay_t<decltype(detail::build_dependency_list( | |||
| (settings_type { }).system_signatures(), | |||
| T_system_tag_list { }))>; | |||
| using independent_item_ids_type = mp::decay_t<decltype(detail::get_independent_item_ids( | |||
| dependency_list_type { }))>;; | |||
| /* TODO debug beg! */ | |||
| size_t i = 0; | |||
| std::cout << "dependency_list" << std::endl; | |||
| hana::for_each(dependency_list_type { }, [&i](auto item){ | |||
| using system_type = mp::unwrap_t<mp::decay_t<decltype(mp::unwrap(item).tag)>>; | |||
| std::cout << " " << (i++) << ": " << type_helper<system_type>::name() << " (" << hana::value(mp::unwrap(item).execute) << ")" << std::endl; | |||
| hana::for_each(mp::unwrap(item).dependency_ids, [](auto id){ | |||
| std::cout << " " << hana::value(id) << std::endl; | |||
| }); | |||
| }); | |||
| /* TODO debug end! */ | |||
| using task_group_type = detail::task_group_t<context_type, dependency_list_type>; | |||
| utils::counter_blocker counter (1); | |||
| task_group_type task_group (_context, counter); | |||
| counter.execute_and_wait_until_zero([&task_group, &counter, &func]{ | |||
| hana::for_each(independent_item_ids_type { }, [&task_group, &func](auto id){ | |||
| task_group.start_task(id, func); | |||
| }); | |||
| counter.decrement(); | |||
| }); | |||
| } | |||
| } } } } | |||
| @@ -4,8 +4,10 @@ | |||
| #include <ecs/tag/system.h> | |||
| #include <ecs/core/system/instance.h> | |||
| beg_namespace_ecs_core_system_storage | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace system { | |||
| namespace storage { | |||
| template<typename T_settings, typename T_entity_handle> | |||
| struct tuple | |||
| @@ -94,5 +96,4 @@ beg_namespace_ecs_core_system_storage | |||
| { return instance_by_tag(ssig.tag()); } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_system_storage | |||
| } } } } | |||
| @@ -4,6 +4,11 @@ | |||
| #include "./utils/counter_blocker.h" | |||
| #include "./utils/fixed_function.h" | |||
| #include "./utils/movable_atomic.h" | |||
| #include "./utils/ordered_vector.h" | |||
| #include "./utils/scope_guard.h" | |||
| #include "./utils/storage_cast.h" | |||
| #include "./utils/thread_pool.h" | |||
| #include "./utils/thread_pool.h" | |||
| #include "./utils/bitset.inl" | |||
| #include "./utils/counter_blocker.inl" | |||
| #include "./utils/fixed_function.inl" | |||
| @@ -4,48 +4,106 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/mp/list.h> | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * bitset to store components flags in | |||
| * | |||
| * @tparam T_settings settings type the environmant is configured with | |||
| */ | |||
| template<typename T_settings> | |||
| struct bitset | |||
| { | |||
| private: | |||
| /** | |||
| * get the component signature list | |||
| * | |||
| * @return component signature list | |||
| */ | |||
| static constexpr decltype(auto) csl() noexcept | |||
| { return (T_settings { }).component_signatures(); } | |||
| /** | |||
| * get the tags of all components | |||
| * | |||
| * @return list of all component tags | |||
| */ | |||
| static constexpr decltype(auto) all_components() noexcept | |||
| { return csl().all_components(); } | |||
| public: | |||
| /** | |||
| * get the number of components | |||
| * | |||
| * @return integral constant with number of components | |||
| */ | |||
| static constexpr decltype(auto) component_count() noexcept | |||
| { return hana::size(all_components()); } | |||
| /** | |||
| * get the ID of the given component tag | |||
| * | |||
| * @tparam T_component_tag component tag type to get ID for | |||
| * | |||
| * @param ct component tag to get the ID for | |||
| * | |||
| * @return integral constant with the ID of the requested component tag | |||
| */ | |||
| template<typename T_component_tag> | |||
| static constexpr decltype(auto) component_id(T_component_tag ct) noexcept | |||
| { return mp::list::index_of(all_components(), ct); } | |||
| static constexpr decltype(auto) component_id(T_component_tag ct) noexcept; | |||
| /** | |||
| * internal bitset type that contains all components | |||
| */ | |||
| using bitset_type = std::bitset<component_count()>; | |||
| private: | |||
| bitset_type _bitset; | |||
| public: | |||
| inline void clear() noexcept | |||
| { _bitset.reset(); } | |||
| /** | |||
| * clear all bits of the bitset | |||
| */ | |||
| inline void clear() noexcept; | |||
| /** | |||
| * check if the bitset contains another bitset | |||
| * | |||
| * @tparam T_other type of the other bitset | |||
| * | |||
| * @param other bitset to compare with | |||
| * | |||
| * @retval TRUE if this bitset contains the passed bitset | |||
| * @retval FALSE if this bitset does not contain the passed bitset | |||
| */ | |||
| template<typename T_other> | |||
| inline bool contains(const T_other& other) const noexcept | |||
| { return (_bitset & other._bitset) == _bitset; } | |||
| inline bool contains(const T_other& other) const noexcept; | |||
| /** | |||
| * check if the bitset contains the given component | |||
| * | |||
| * @tparam T_component_tag component tag type to check | |||
| * | |||
| * @param ct component tag to check | |||
| * | |||
| * @retval TRUE if this bitset contains the passed component tag | |||
| * @retval FALSE if this bitset does not contain the passed component tag | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline bool has_component(T_component_tag ct) const | |||
| { return _bitset.test(component_id(ct)); } | |||
| inline bool has_component(T_component_tag ct) const; | |||
| /** | |||
| * set the bit value of the passed component tag | |||
| * | |||
| * @tparam T_component_tag component tag type to set | |||
| * | |||
| * @param ct component tag to set | |||
| * @param value new bit value of the component inside the bitset | |||
| */ | |||
| template<typename T_component_tag> | |||
| inline void set_component(T_component_tag ct, bool value) | |||
| { _bitset.set(component_id(ct), value); } | |||
| inline void set_component(T_component_tag ct, bool value); | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -0,0 +1,48 @@ | |||
| #pragma once | |||
| #include <ecs/core/utils/bitset.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) bitset<T_settings> | |||
| ::component_id(T_component_tag ct) noexcept | |||
| { | |||
| return mp::list::index_of(all_components(), ct); | |||
| } | |||
| template<typename T_settings> | |||
| inline void bitset<T_settings> | |||
| ::clear() noexcept | |||
| { | |||
| _bitset.reset(); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_other> | |||
| inline bool bitset<T_settings> | |||
| ::contains(const T_other& other) const noexcept | |||
| { | |||
| return (_bitset & other._bitset) == _bitset; | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| inline bool bitset<T_settings> | |||
| ::has_component(T_component_tag ct) const | |||
| { | |||
| return _bitset.test(component_id(ct)); | |||
| } | |||
| template<typename T_settings> | |||
| template<typename T_component_tag> | |||
| inline void bitset<T_settings> | |||
| ::set_component(T_component_tag ct, bool value) | |||
| { | |||
| _bitset.set(component_id(ct), value); | |||
| } | |||
| } } } | |||
| @@ -2,54 +2,50 @@ | |||
| #include <mutex> | |||
| #include <condition_variable> | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * counter that blocks a given operation until it is equal zero | |||
| */ | |||
| struct counter_blocker | |||
| { | |||
| private: | |||
| std::mutex _mutex; | |||
| std::condition_variable _cond_var; | |||
| size_t _counter; | |||
| std::mutex _mutex; //!< mutex to preotect the counter | |||
| std::condition_variable _cond_var; //!< conditional variable to synchronize the counter | |||
| size_t _counter; //!< the actual counter value | |||
| public: | |||
| inline counter_blocker(size_t counter) noexcept | |||
| : _counter(counter) | |||
| { } | |||
| inline void increment() noexcept | |||
| { | |||
| std::lock_guard lock(_mutex); | |||
| assert(_counter > 0); | |||
| ++_counter; | |||
| } | |||
| inline void decrement_and_notify_one() noexcept | |||
| { | |||
| std::lock_guard lock(_mutex); | |||
| assert(_counter > 0); | |||
| --_counter; | |||
| _cond_var.notify_one(); | |||
| } | |||
| inline void decrement_and_notify_all() noexcept | |||
| { | |||
| std::lock_guard lock(_mutex); | |||
| assert(_counter > 0); | |||
| --_counter; | |||
| _cond_var.notify_all(); | |||
| } | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param counter initial counter value | |||
| */ | |||
| inline counter_blocker(size_t counter) noexcept; | |||
| /** | |||
| * increment the counter value by one | |||
| */ | |||
| inline void increment() noexcept; | |||
| /** | |||
| * decrement the counter value by one and notify all pending threads | |||
| */ | |||
| inline void decrement() noexcept; | |||
| /** | |||
| * execute the given function and wait until the counter is equal to zero | |||
| * | |||
| * @tparam T_func type of the function to execute | |||
| * | |||
| * @param func function to execute | |||
| */ | |||
| template<typename T_func> | |||
| inline void execute_and_wait_until_zero(T_func&& func) noexcept | |||
| { | |||
| func(); | |||
| std::unique_lock lock(_mutex); | |||
| _cond_var.wait(lock, [this]{ return (_counter == 0); }); | |||
| } | |||
| inline void execute_and_wait_until_zero(T_func&& func) noexcept; | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -0,0 +1,40 @@ | |||
| #pragma once | |||
| #include <ecs/core/utils/counter_blocker.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| inline counter_blocker | |||
| ::counter_blocker(size_t counter) noexcept | |||
| : _counter(counter) | |||
| { } | |||
| inline void counter_blocker | |||
| ::increment() noexcept | |||
| { | |||
| std::lock_guard lock(_mutex); | |||
| assert(_counter > 0); | |||
| ++_counter; | |||
| } | |||
| inline void counter_blocker | |||
| ::decrement() noexcept | |||
| { | |||
| std::lock_guard lock(_mutex); | |||
| assert(_counter > 0); | |||
| --_counter; | |||
| _cond_var.notify_all(); | |||
| } | |||
| template<typename T_func> | |||
| inline void counter_blocker | |||
| ::execute_and_wait_until_zero(T_func&& func) noexcept | |||
| { | |||
| func(); | |||
| std::unique_lock lock(_mutex); | |||
| _cond_var.wait(lock, [this]{ return (_counter == 0); }); | |||
| } | |||
| } } } | |||
| @@ -4,9 +4,16 @@ | |||
| #include "./storage_cast.h" | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * functor to store a function of a fixed size | |||
| * | |||
| * @tparam T_signature signature of the functor | |||
| * @tparam T_storage_size max size of the functor | |||
| */ | |||
| template<typename T_signature, size_t T_storage_size = 64> | |||
| struct fixed_function; | |||
| @@ -25,122 +32,88 @@ beg_namespace_ecs_core_utils | |||
| private: | |||
| union | |||
| { | |||
| storage_type _storage; | |||
| function_ptr_type _function_ptr; | |||
| storage_type _storage; //!< memory to store the functor | |||
| function_ptr_type _function_ptr; //!< normal function pointer | |||
| }; | |||
| method_type _method_ptr; | |||
| allocator_type _allocator_ptr; | |||
| method_type _method_ptr; //!< pointer to the function to call | |||
| allocator_type _allocator_ptr; //!< pointer to the function to allocate memory with | |||
| private: | |||
| void move_from_other(fixed_function& other) noexcept | |||
| { | |||
| assert(this != &o); | |||
| // cleanup | |||
| if (_allocator_ptr) | |||
| { | |||
| _allocator_ptr(&_storage, nullptr); | |||
| } | |||
| _allocator_ptr = nullptr; | |||
| _function_ptr = nullptr; | |||
| // move | |||
| _method_ptr = other._method_ptr; | |||
| other._method_ptr = nullptr; | |||
| _function_ptr = other._function_ptr; | |||
| _allocator_ptr = other._allocator_ptr; | |||
| if (_allocator_ptr) | |||
| { | |||
| _allocator_ptr(&_storage, &other._storage); | |||
| } | |||
| } | |||
| /** | |||
| * move the data from the passed object to this object | |||
| * | |||
| * @param other object to move data from | |||
| */ | |||
| inline void move_from_other(fixed_function& other) noexcept; | |||
| public: | |||
| inline fixed_function() noexcept | |||
| : _function_ptr (nullptr) | |||
| , _method_ptr (nullptr) | |||
| , _allocator_ptr(nullptr) | |||
| { } | |||
| /** | |||
| * default constructor | |||
| */ | |||
| inline fixed_function() noexcept; | |||
| /** | |||
| * constructor | |||
| * | |||
| * @tparam T_func type of the functor to store | |||
| * | |||
| * @param func functor to call | |||
| */ | |||
| template<typename T_func> | |||
| inline fixed_function(T_func&& func) noexcept | |||
| : fixed_function() | |||
| { | |||
| using unref_type = std::remove_reference_t<T_func>; | |||
| static_assert( | |||
| sizeof(unref_type) < storage_size, | |||
| "functional object doesn't fit into internal storage"); | |||
| static_assert( | |||
| std::is_move_constructible<unref_type> { }, | |||
| "should be move constructable"); | |||
| _method_ptr = [](const storage_type* s, function_ptr_type, T_args... args) | |||
| { | |||
| return storage_cast<const unref_type>(s)->operator()(args...); | |||
| }; | |||
| _allocator_ptr = [](storage_type* s, void* obj) | |||
| { | |||
| if (obj) | |||
| { | |||
| new(s) unref_type(std::move(*static_cast<unref_type*>(obj))); | |||
| } | |||
| else | |||
| { | |||
| storage_cast<unref_type>(s)->~unref_type(); | |||
| } | |||
| }; | |||
| _allocator_ptr(&_storage, &func); | |||
| } | |||
| inline fixed_function(T_func&& func) noexcept; | |||
| /** | |||
| * constructor | |||
| * | |||
| * @tparam TF_return return type of the function pointer | |||
| * @tparam TF_args argument types of the function parameters | |||
| * | |||
| * @param func function pointer to call | |||
| */ | |||
| template<typename TF_return, typename... TF_args> | |||
| inline fixed_function(TF_return (*func)(TF_args...)) noexcept | |||
| : fixed_function() | |||
| { | |||
| _function_ptr = func; | |||
| _method_ptr = [](const storage_type*, function_ptr_type f, T_args... args) | |||
| { | |||
| return static_cast<decltype(func)>(f)(args...); | |||
| }; | |||
| } | |||
| inline fixed_function(fixed_function&& other) noexcept | |||
| : fixed_function() | |||
| { move_from_other(other); } | |||
| fixed_function(const fixed_function&) = delete; | |||
| inline ~fixed_function() noexcept | |||
| { | |||
| if (_allocator_ptr) | |||
| { | |||
| _allocator_ptr(&_storage, nullptr); | |||
| } | |||
| _allocator_ptr = nullptr; | |||
| _function_ptr = nullptr; | |||
| _method_ptr = nullptr; | |||
| } | |||
| inline fixed_function& operator=(fixed_function&& other) noexcept | |||
| { | |||
| move_from_other(other); | |||
| return *this; | |||
| } | |||
| inline fixed_function(TF_return (*func)(TF_args...)) noexcept; | |||
| /** | |||
| * move constructor | |||
| */ | |||
| inline fixed_function(fixed_function&& other) noexcept; | |||
| /** | |||
| * copy constructor | |||
| */ | |||
| inline fixed_function(const fixed_function&) = delete; | |||
| /** | |||
| * destructor | |||
| */ | |||
| inline ~fixed_function() noexcept; | |||
| /** | |||
| * move assignment operator | |||
| * | |||
| * @param other object to assign | |||
| */ | |||
| inline fixed_function& operator=(fixed_function&& other) noexcept; | |||
| /** | |||
| * copy assignment operator | |||
| * | |||
| * @return reference to this object | |||
| */ | |||
| fixed_function& operator=(const fixed_function&) = delete; | |||
| /** | |||
| * execution operator | |||
| * | |||
| * @tparam TF_args argument types to pass to the stored function | |||
| * | |||
| * @param args arguments to pass to the stored function | |||
| * | |||
| * @return return value of the stored function | |||
| */ | |||
| template<typename... TF_args> | |||
| inline decltype(auto) operator()(TF_args&&... args) const | |||
| { | |||
| assert(_method_ptr != nullptr); | |||
| return _method_ptr(&_storage, _function_ptr, std::forward<TF_args>(args)...); | |||
| } | |||
| inline decltype(auto) operator()(TF_args&&... args) const; | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -0,0 +1,129 @@ | |||
| #pragma once | |||
| #include <cassert> | |||
| #include <ecs/core/utils/fixed_function.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| void fixed_function<T_return(T_args...), T_storage_size> | |||
| ::move_from_other(fixed_function& other) noexcept | |||
| { | |||
| assert(this != &other); | |||
| // cleanup | |||
| if (_allocator_ptr) | |||
| { | |||
| _allocator_ptr(&_storage, nullptr); | |||
| } | |||
| _allocator_ptr = nullptr; | |||
| _function_ptr = nullptr; | |||
| // move | |||
| _method_ptr = other._method_ptr; | |||
| other._method_ptr = nullptr; | |||
| _function_ptr = other._function_ptr; | |||
| _allocator_ptr = other._allocator_ptr; | |||
| if (_allocator_ptr) | |||
| { | |||
| _allocator_ptr(&_storage, &other._storage); | |||
| } | |||
| } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| inline fixed_function<T_return(T_args...), T_storage_size> | |||
| ::fixed_function() noexcept | |||
| : _function_ptr (nullptr) | |||
| , _method_ptr (nullptr) | |||
| , _allocator_ptr(nullptr) | |||
| { } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| template<typename T_func> | |||
| inline fixed_function<T_return(T_args...), T_storage_size> | |||
| ::fixed_function(T_func&& func) noexcept | |||
| : fixed_function() | |||
| { | |||
| using unref_type = std::remove_reference_t<T_func>; | |||
| static_assert( | |||
| sizeof(unref_type) < storage_size, | |||
| "functional object doesn't fit into internal storage"); | |||
| static_assert( | |||
| std::is_move_constructible<unref_type> { }, | |||
| "should be move constructable"); | |||
| _method_ptr = [](const storage_type* s, function_ptr_type, T_args... args) | |||
| { | |||
| return storage_cast<const unref_type>(s)->operator()(args...); | |||
| }; | |||
| _allocator_ptr = [](storage_type* s, void* obj) | |||
| { | |||
| if (obj) | |||
| { | |||
| new(s) unref_type(std::move(*static_cast<unref_type*>(obj))); | |||
| } | |||
| else | |||
| { | |||
| storage_cast<unref_type>(s)->~unref_type(); | |||
| } | |||
| }; | |||
| _allocator_ptr(&_storage, &func); | |||
| } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| template<typename TF_return, typename... TF_args> | |||
| inline fixed_function<T_return(T_args...), T_storage_size> | |||
| ::fixed_function(TF_return (*func)(TF_args...)) noexcept | |||
| : fixed_function() | |||
| { | |||
| _function_ptr = func; | |||
| _method_ptr = [](const storage_type*, function_ptr_type f, T_args... args) | |||
| { | |||
| return static_cast<decltype(func)>(f)(args...); | |||
| }; | |||
| } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| inline fixed_function<T_return(T_args...), T_storage_size> | |||
| ::fixed_function(fixed_function&& other) noexcept | |||
| : fixed_function() | |||
| { move_from_other(other); } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| inline fixed_function<T_return(T_args...), T_storage_size> | |||
| ::~fixed_function() noexcept | |||
| { | |||
| if (_allocator_ptr) | |||
| { | |||
| _allocator_ptr(&_storage, nullptr); | |||
| } | |||
| _allocator_ptr = nullptr; | |||
| _function_ptr = nullptr; | |||
| _method_ptr = nullptr; | |||
| } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| inline fixed_function<T_return(T_args...), T_storage_size>& fixed_function<T_return(T_args...), T_storage_size> | |||
| ::operator=(fixed_function&& other) noexcept | |||
| { | |||
| move_from_other(other); | |||
| return *this; | |||
| } | |||
| template<typename T_return, typename... T_args, size_t T_storage_size> | |||
| template<typename... TF_args> | |||
| inline decltype(auto) fixed_function<T_return(T_args...), T_storage_size> | |||
| ::operator()(TF_args&&... args) const | |||
| { | |||
| assert(_method_ptr != nullptr); | |||
| return _method_ptr(&_storage, _function_ptr, std::forward<TF_args>(args)...); | |||
| } | |||
| } } } | |||
| @@ -2,9 +2,15 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * atomic variable that implements the move constructor and the move assignment operator | |||
| * | |||
| * @tparam T inner type to store in the atomic | |||
| */ | |||
| template<typename T> | |||
| struct movable_atomic final | |||
| : std::atomic<T> | |||
| @@ -15,29 +21,46 @@ beg_namespace_ecs_core_utils | |||
| public: | |||
| using base_type::base_type; | |||
| movable_atomic() = default; | |||
| /** | |||
| * default constructor | |||
| */ | |||
| inline movable_atomic() = default; | |||
| movable_atomic(const movable_atomic&) = delete; | |||
| /** | |||
| * copy constructor | |||
| */ | |||
| inline movable_atomic(const movable_atomic&) = delete; | |||
| movable_atomic(movable_atomic&& other) noexcept | |||
| /** | |||
| * move constructor | |||
| */ | |||
| inline movable_atomic(movable_atomic&& other) noexcept | |||
| : base_type(other.load()) | |||
| { } | |||
| movable_atomic& operator=(const movable_atomic&) = delete; | |||
| /** | |||
| * copy assignment operator | |||
| */ | |||
| inline movable_atomic& operator=(const movable_atomic&) = delete; | |||
| movable_atomic& operator=(movable_atomic&& other) noexcept | |||
| /** | |||
| * move assignment operator | |||
| */ | |||
| inline movable_atomic& operator=(movable_atomic&& other) noexcept | |||
| { | |||
| this->store(other.load()); | |||
| return *this; | |||
| } | |||
| /** | |||
| * assignment constructor to assign integral constants | |||
| */ | |||
| template<T value> | |||
| movable_atomic& operator=(std::integral_constant<T, value> x) noexcept | |||
| inline movable_atomic& operator=(std::integral_constant<T, value> x) noexcept | |||
| { | |||
| this->store(decltype(x)::value); | |||
| return *this; | |||
| } | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -0,0 +1,186 @@ | |||
| #pragma once | |||
| #include <vector> | |||
| #include <ecs/config.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * vector with ordered elements | |||
| */ | |||
| template< | |||
| typename T_value, | |||
| typename T_allocator = std::allocator<T_value>, | |||
| typename T_compare = std::less<T_value>> | |||
| struct ordered_vector | |||
| { | |||
| public: | |||
| using value_type = T_value; | |||
| using allocator_type = T_allocator; | |||
| using compare_type = T_compare; | |||
| using this_type = ordered_vector<value_type, allocator_type, compare_type>; | |||
| using vector_type = std::vector<value_type, allocator_type>; | |||
| using size_type = typename vector_type::size_type; | |||
| using difference_type = typename vector_type::difference_type; | |||
| using reference = typename vector_type::reference; | |||
| using const_reference = typename vector_type::const_reference; | |||
| using pointer = typename vector_type::pointer; | |||
| using const_pointer = typename vector_type::const_pointer; | |||
| using iterator = typename vector_type::iterator; | |||
| using const_iterator = typename vector_type::const_iterator; | |||
| using reverse_iterator = typename vector_type::reverse_iterator; | |||
| using const_reverse_iterator = typename vector_type::const_reverse_iterator; | |||
| private: | |||
| vector_type _items; | |||
| compare_type _compare; | |||
| public: | |||
| /* constructor */ | |||
| ordered_vector() = default; | |||
| ordered_vector(const ordered_vector&) = default; | |||
| ordered_vector& operator=(const ordered_vector&) = default; | |||
| ordered_vector(ordered_vector&&) = default; | |||
| ordered_vector& operator=(ordered_vector&&) = default; | |||
| template<typename... T_args> | |||
| inline ordered_vector(T_args&&... args, const compare_type& p_compare = compare_type()) | |||
| : _items (std::forward<T_args>(args)...) | |||
| , _compare (p_compare) | |||
| { } | |||
| /* access */ | |||
| inline reference operator[](size_type pos) | |||
| noexcept(noexcept(_items.operator[](pos))) | |||
| { return _items[pos]; } | |||
| inline const_reference operator[](size_type pos) const | |||
| noexcept(noexcept(_items.operator[](pos))) | |||
| { return _items[pos]; } | |||
| inline reference at(size_type pos) | |||
| noexcept(noexcept(_items.at(pos))) | |||
| { return _items.at(pos); } | |||
| inline const_reference at(size_type pos) const | |||
| noexcept(noexcept(_items.at(pos))) | |||
| { return _items.at(pos); } | |||
| /* iterators */ | |||
| inline iterator begin() | |||
| noexcept(noexcept(_items.begin())) | |||
| { return _items.begin(); } | |||
| inline const_iterator begin() const | |||
| noexcept(noexcept(_items.begin())) | |||
| { return _items.begin(); } | |||
| inline const_iterator cbegin() const | |||
| noexcept(noexcept(_items.cbegin())) | |||
| { return _items.cbegin(); } | |||
| inline iterator end() | |||
| noexcept(noexcept(_items.end())) | |||
| { return _items.end(); } | |||
| inline const_iterator end() const | |||
| noexcept(noexcept(_items.end())) | |||
| { return _items.end(); } | |||
| inline const_iterator cend() const | |||
| noexcept(noexcept(_items.cend())) | |||
| { return _items.cend(); } | |||
| inline iterator rbegin() | |||
| noexcept(noexcept(_items.rbegin())) | |||
| { return _items.rbegin(); } | |||
| inline const_iterator rbegin() const | |||
| noexcept(noexcept(_items.rbegin())) | |||
| { return _items.rbegin(); } | |||
| inline const_iterator crbegin() const | |||
| noexcept(noexcept(_items.crbegin())) | |||
| { return _items.crbegin(); } | |||
| inline iterator rend() | |||
| noexcept(noexcept(_items.rend())) | |||
| { return _items.rend(); } | |||
| inline const_iterator rend() const | |||
| noexcept(noexcept(_items.rend())) | |||
| { return _items.rend(); } | |||
| inline const_iterator crend() const | |||
| noexcept(noexcept(_items.crend())) | |||
| { return _items.crend(); } | |||
| /* capacity */ | |||
| inline bool empty() const | |||
| noexcept(noexcept(_items.empty())) | |||
| { return _items.empty(); } | |||
| inline size_type size() const | |||
| noexcept(noexcept(_items.size())) | |||
| { return _items.size(); } | |||
| inline size_type max_size() const | |||
| noexcept(noexcept(_items.size())) | |||
| { return _items.max_size(); } | |||
| inline size_type capacity() const | |||
| noexcept(noexcept(_items.capacity())) | |||
| { return _items.capacity(); } | |||
| inline void reserve(size_type s) const | |||
| noexcept(noexcept(_items.reserve(s))) | |||
| { _items.reserve(s); } | |||
| inline void shrink_to_fit() const | |||
| noexcept(noexcept(_items.shrink_to_fit())) | |||
| { _items.shrink_to_fit(); } | |||
| inline decltype(auto) insert(const value_type& value) | |||
| { | |||
| auto it = std::lower_bound(begin(), end(), value); | |||
| auto is_new = _compare(value, *it); | |||
| if (it == end() || is_new) | |||
| _items.insert(it, value); | |||
| return std::make_pair(it, is_new); | |||
| } | |||
| inline decltype(auto) insert(value_type&& value) | |||
| { | |||
| auto it = std::lower_bound(begin(), end(), value, _compare); | |||
| auto is_new = (it != end() && _compare(value, *it)); | |||
| if (it == end() || is_new) | |||
| _items.insert(it, std::move(value)); | |||
| return std::make_pair(it, is_new); | |||
| } | |||
| inline decltype(auto) find(const value_type& value) const | |||
| { | |||
| auto it = std::lower_bound(begin(), end(), value, _compare); | |||
| return it == end() || _compare(value, *it) | |||
| ? end() | |||
| : it; | |||
| } | |||
| inline bool contains(const value_type& value) const | |||
| { return find(value) != end(); } | |||
| template<typename... T_args> | |||
| constexpr decltype(auto) erase(T_args&&... args) | |||
| { return _items.erase(std::forward<T_args>(args)...); } | |||
| }; | |||
| } } } | |||
| @@ -7,28 +7,52 @@ | |||
| auto scope_guard_ ## __LINE__ ( \ | |||
| ::ecs::core::utils::make_scope_guard(__VA_ARGS__)) | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * guard that executes the given function if the its scope is left | |||
| * | |||
| * @tparam T_func functor type to execute | |||
| */ | |||
| template<typename T_func> | |||
| struct scope_guard final | |||
| : T_func | |||
| { | |||
| template<typename... Xs> | |||
| inline scope_guard(Xs&&... xs) | |||
| /** | |||
| * constructor | |||
| * | |||
| * @tparam T_args argument types to initialize the functor with | |||
| * | |||
| * @param args arguments to initialize the functor with | |||
| */ | |||
| template<typename... T_args> | |||
| inline scope_guard(T_args&&... args) | |||
| noexcept(std::is_nothrow_constructible<T_func> { }) | |||
| : T_func(std::forward<Xs>(xs)...) | |||
| : T_func(std::forward<T_args>(args)...) | |||
| { } | |||
| /** | |||
| * destructor: will execute the stored functor | |||
| */ | |||
| inline ~scope_guard() noexcept( | |||
| noexcept(std::declval<T_func>().operator()())) | |||
| { this->operator()(); } | |||
| }; | |||
| /** | |||
| * helper to build a scope guard | |||
| * | |||
| * @tparam T_func functor type to execute | |||
| * | |||
| * @param f functor to execute | |||
| * | |||
| * @return created scope guard | |||
| */ | |||
| template<typename T_func> | |||
| inline decltype(auto) make_scope_guard(T_func&& f) | |||
| noexcept(std::is_nothrow_move_constructible<T_func> { }) | |||
| { return scope_guard<mp::decay_t<T_func>>(std::forward<T_func>(f)); } | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -4,9 +4,20 @@ | |||
| #include <ecs/core/mp/core/copy_qualifiers.h> | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * cast the given storage to a certain type | |||
| * | |||
| * @tparam T type to cast storage in | |||
| * @tparam T_storage storage type to cast | |||
| * | |||
| * @param storage storage to cast | |||
| * | |||
| * @return casted storage | |||
| */ | |||
| template <typename T, typename T_storage> | |||
| inline constexpr decltype(auto) storage_cast(T_storage* storage) noexcept | |||
| { | |||
| @@ -25,5 +36,4 @@ beg_namespace_ecs_core_utils | |||
| } | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -2,4 +2,7 @@ | |||
| #include "./thread_pool/pool.h" | |||
| #include "./thread_pool/types.h" | |||
| #include "./thread_pool/worker.h" | |||
| #include "./thread_pool/worker.h" | |||
| #include "./thread_pool/pool.inl" | |||
| #include "./thread_pool/worker.inl" | |||
| @@ -1,13 +1,19 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| #include <atomic> | |||
| #include <vector> | |||
| #include "./worker.h" | |||
| #include "./types.h" | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/utils/thread_pool/types.h> | |||
| #include <ecs/core/utils/thread_pool/worker.h> | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * this class implements a thread pool | |||
| */ | |||
| struct thread_pool | |||
| { | |||
| private: | |||
| @@ -15,75 +21,47 @@ beg_namespace_ecs_core_utils | |||
| using atomic_size_t = std::atomic<size_t>; | |||
| private: | |||
| task_queue _queue; | |||
| worker_vector _workers; | |||
| atomic_size_t _outstanding_inits; | |||
| auto all_workers_finished() const noexcept | |||
| { | |||
| for (const auto& w : _workers) | |||
| { | |||
| if (!w.finished()) | |||
| { | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| inline void post_dummy_task() | |||
| { post([]{ }); } | |||
| void initialize_workers(size_t count) | |||
| { | |||
| _workers.reserve(count); | |||
| for (size_t i = 0; i < count; ++i) | |||
| { | |||
| _workers.emplace_back(_queue); | |||
| } | |||
| _outstanding_inits = count; | |||
| for (auto& w : _workers) | |||
| { | |||
| w.start(_outstanding_inits); | |||
| } | |||
| } | |||
| task_queue _queue; //!< queue to store tasks to execute | |||
| worker_vector _workers; //!< vector of worker threads | |||
| atomic_size_t _outstanding_inits; //!< number of outstanding worker initializations | |||
| /** | |||
| * check if all workers are finished or not | |||
| * | |||
| * @retval TRUE if all workers are finished | |||
| * @retval FALSE if at least one worker is running | |||
| */ | |||
| auto all_workers_finished() const noexcept; | |||
| /** | |||
| * initialize worker threads | |||
| * | |||
| * @param count number of workers to create | |||
| */ | |||
| void initialize_workers(size_t count); | |||
| public: | |||
| inline thread_pool(size_t count) | |||
| { initialize_workers(count); } | |||
| ~thread_pool() | |||
| { | |||
| // wait for uninitialized workers | |||
| while (_outstanding_inits > 0) | |||
| { | |||
| std::this_thread::sleep_for(std::chrono::milliseconds(50)); | |||
| } | |||
| // stop all workers | |||
| for (auto& w : _workers) | |||
| { | |||
| w.stop(); | |||
| } | |||
| // post dummy tasks untill all workers has stopped | |||
| while (!all_workers_finished()) | |||
| { | |||
| post_dummy_task(); | |||
| } | |||
| // join the worker threads | |||
| for (auto& w : _workers) | |||
| { | |||
| w.join(); | |||
| } | |||
| } | |||
| template<typename T_func> | |||
| inline void post(T_func&& func) | |||
| { _queue.enqueue(std::forward<T_func>(func)); } | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param count number of workers to create | |||
| */ | |||
| thread_pool(size_t count); | |||
| /** | |||
| * destructor | |||
| */ | |||
| ~thread_pool(); | |||
| /** | |||
| * enqueue a new task inside the thread pool | |||
| * | |||
| * @tparam T_task type of the task | |||
| * | |||
| * @param task task to execute | |||
| */ | |||
| template<typename T_task> | |||
| inline void post(T_task&& task); | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -0,0 +1,16 @@ | |||
| #pragma once | |||
| #include <ecs/core/utils/thread_pool/pool.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| template<typename T_task> | |||
| inline void thread_pool | |||
| ::post(T_task&& task) | |||
| { | |||
| _queue.enqueue(std::forward<T_task>(task)); | |||
| } | |||
| } } } | |||
| @@ -5,11 +5,18 @@ | |||
| #include "../fixed_function.h" | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| using task = fixed_function<void(), 128>; | |||
| using task_queue = moodycamel::BlockingConcurrentQueue<task>; | |||
| /** | |||
| * Functor to execute as task inside the thread pool. The functor can have a max size of 128 bytes! | |||
| */ | |||
| using task = fixed_function<void(), 128>; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| /** | |||
| * non blocking concurrent queue to store thread pool tasks in | |||
| */ | |||
| using task_queue = moodycamel::BlockingConcurrentQueue<task>; | |||
| } } } | |||
| @@ -6,78 +6,81 @@ | |||
| #include "./types.h" | |||
| #include "../movable_atomic.h" | |||
| beg_namespace_ecs_core_utils | |||
| { | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| /** | |||
| * worker thread to execute tasks of thread pool in | |||
| */ | |||
| struct thread_pool_worker | |||
| { | |||
| private: | |||
| /** | |||
| * state of the worker | |||
| */ | |||
| enum class state | |||
| { | |||
| uninizialized, | |||
| running, | |||
| stopped, | |||
| finished, | |||
| uninizialized, //!< the thread was not initialized yes | |||
| running, //!< the thread is running normally | |||
| stopped, //!< the thread was intended to stop execution | |||
| finished, //!< the thread has stopped execution | |||
| }; | |||
| using atomic_state = movable_atomic<state>; | |||
| std::thread _thread; | |||
| task_queue& _queue; | |||
| atomic_state _state; | |||
| std::thread _thread; //!< actual thread object to use for execution | |||
| task_queue& _queue; //!< queue to poll new tasks from | |||
| atomic_state _state; //!< current state of the worker | |||
| private: | |||
| void run() | |||
| { | |||
| _state = state::running; | |||
| while (_state == state::running) | |||
| { | |||
| task t; | |||
| if (_queue.wait_dequeue_timed(t, std::chrono::milliseconds(500))) | |||
| { | |||
| t(); | |||
| } | |||
| } | |||
| _state = state::finished; | |||
| } | |||
| /** | |||
| * thread worker method | |||
| */ | |||
| void run(); | |||
| public: | |||
| thread_pool_worker(task_queue& queue) noexcept | |||
| : _queue(queue) | |||
| { } | |||
| thread_pool_worker(thread_pool_worker&&) = default; | |||
| thread_pool_worker(const thread_pool_worker&) = delete; | |||
| thread_pool_worker& operator=(thread_pool_worker&&) = default; | |||
| thread_pool_worker& operator=(const thread_pool_worker&) = delete; | |||
| /** | |||
| * constructor | |||
| * | |||
| * @param queue task queue to poll tasks from | |||
| */ | |||
| inline thread_pool_worker(task_queue& queue) noexcept; | |||
| inline thread_pool_worker(thread_pool_worker&&) = default; | |||
| inline thread_pool_worker(const thread_pool_worker&) = delete; | |||
| inline thread_pool_worker& operator=(thread_pool_worker&&) = default; | |||
| inline thread_pool_worker& operator=(const thread_pool_worker&) = delete; | |||
| /** | |||
| * start the worker | |||
| * | |||
| * @tparam T_counter type of the counter | |||
| * | |||
| * @param remaining_inits number of remaining thread initialization (is decremented by this thread) | |||
| */ | |||
| template<typename T_counter> | |||
| inline void start(T_counter& remaining_inits) | |||
| { | |||
| _thread = std::thread([this, &remaining_inits]{ | |||
| --remaining_inits; | |||
| run(); | |||
| }); | |||
| } | |||
| inline void stop() noexcept | |||
| { | |||
| assert(_state == state::running); | |||
| _state = state::stopped; | |||
| } | |||
| inline void join() noexcept | |||
| { | |||
| assert(_thread.joinable()); | |||
| assert(_state == state::finished); | |||
| _thread.join(); | |||
| } | |||
| inline bool finished() const noexcept | |||
| { return _state == state::finished; } | |||
| inline void start(T_counter& remaining_inits); | |||
| /** | |||
| * stop the thread execution | |||
| */ | |||
| inline void stop() noexcept; | |||
| /** | |||
| * wait for the thread to finish | |||
| */ | |||
| inline void join() noexcept; | |||
| /** | |||
| * check if the thread has stopped execution | |||
| * | |||
| * @retval TRUE if the thread has stopped execution | |||
| * @retval FALSE if the thread is still running | |||
| */ | |||
| inline bool finished() const noexcept; | |||
| }; | |||
| } | |||
| end_namespace_ecs_core_utils | |||
| } } } | |||
| @@ -0,0 +1,46 @@ | |||
| #pragma once | |||
| #include <ecs/core/utils/thread_pool/worker.h> | |||
| namespace ecs { | |||
| namespace core { | |||
| namespace utils { | |||
| inline thread_pool_worker | |||
| ::thread_pool_worker(task_queue& queue) noexcept | |||
| : _queue(queue) | |||
| { } | |||
| template<typename T_counter> | |||
| inline void thread_pool_worker | |||
| ::start(T_counter& remaining_inits) | |||
| { | |||
| _thread = std::thread([this, &remaining_inits]{ | |||
| --remaining_inits; | |||
| run(); | |||
| }); | |||
| } | |||
| inline void thread_pool_worker | |||
| ::stop() noexcept | |||
| { | |||
| assert(_state == state::running); | |||
| _state = state::stopped; | |||
| } | |||
| inline void thread_pool_worker | |||
| ::join() noexcept | |||
| { | |||
| assert(_thread.joinable()); | |||
| assert(_state == state::finished); | |||
| _thread.join(); | |||
| } | |||
| inline bool thread_pool_worker | |||
| ::finished() const noexcept | |||
| { | |||
| return _state == state::finished; | |||
| } | |||
| } } } | |||
| @@ -1,5 +1,9 @@ | |||
| #pragma once | |||
| #include "./settings/entity_storage.h" | |||
| #include "./settings/refresh_parallelism.h" | |||
| #include "./settings/scheduler.h" | |||
| #include "./settings/settings.h" | |||
| #include "./settings/system_storage.h" | |||
| #include "./settings/system_storage.h" | |||
| #include "./settings/settings.inl" | |||
| @@ -3,25 +3,25 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/entity/storage/dynamic.h> | |||
| beg_namespace_ecs_settings_entity_storage | |||
| { | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace entity_storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<size_t T_size> | |||
| struct dynamic_builder_t | |||
| { | |||
| template<typename T_settings, typename T_chunk_meta_data> | |||
| constexpr decltype(auto) operator()(T_settings settings, T_chunk_meta_data) const | |||
| template<typename T_settings, typename T_storage_meta_data> | |||
| constexpr decltype(auto) operator()(T_settings settings, T_storage_meta_data) const | |||
| { | |||
| using chunk_meta_data_type = core::mp::unwrap_t<T_chunk_meta_data>; | |||
| return core::entity::storage::dynamic<T_settings, chunk_meta_data_type>(T_size); | |||
| using storage_meta_data_type = core::mp::unwrap_t<T_storage_meta_data>; | |||
| return core::entity::storage::dynamic<T_settings, storage_meta_data_type>(T_size); | |||
| } | |||
| }; | |||
| } | |||
| template<size_t T_size> | |||
| constexpr decltype(auto) dynamic = __impl::dynamic_builder_t<T_size> { }; | |||
| constexpr decltype(auto) dynamic = detail::dynamic_builder_t<T_size> { }; | |||
| } | |||
| end_namespace_ecs_settings_entity_storage | |||
| } } } | |||
| @@ -3,25 +3,25 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/entity/storage/fixed.h> | |||
| beg_namespace_ecs_settings_entity_storage | |||
| { | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace entity_storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<size_t T_size> | |||
| struct fixed_builder_t | |||
| { | |||
| template<typename T_settings, typename T_chunk_meta_data> | |||
| constexpr decltype(auto) operator()(T_settings settings, T_chunk_meta_data) const | |||
| template<typename T_settings, typename T_storage_meta_data> | |||
| constexpr decltype(auto) operator()(T_settings settings, T_storage_meta_data) const | |||
| { | |||
| using chunk_meta_data_type = core::mp::unwrap_t<T_chunk_meta_data>; | |||
| return core::entity::storage::fixed<T_settings, chunk_meta_data_type, T_size>(); | |||
| using storage_meta_data_type = core::mp::unwrap_t<T_storage_meta_data>; | |||
| return core::entity::storage::fixed<T_settings, storage_meta_data_type, T_size>(); | |||
| } | |||
| }; | |||
| } | |||
| template<size_t T_size> | |||
| constexpr decltype(auto) fixed = __impl::fixed_builder_t<T_size> { }; | |||
| constexpr decltype(auto) fixed = detail::fixed_builder_t<T_size> { }; | |||
| } | |||
| end_namespace_ecs_settings_entity_storage | |||
| } } } | |||
| @@ -2,12 +2,12 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_settings_refresh_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace refresh_parallelism { | |||
| constexpr decltype(auto) enable = hana::true_c; | |||
| constexpr decltype(auto) disable = hana::false_c; | |||
| } | |||
| end_namespace_ecs_settings_refresh_parallelism | |||
| } } } | |||
| @@ -3,12 +3,13 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/scheduler/atomic_counter.h> | |||
| beg_namespace_ecs_settings_scheduler | |||
| { | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace scheduler { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct atomic_counter_t | |||
| struct atomic_counter_builder_t | |||
| { | |||
| template<typename T_settings, typename T_context> | |||
| constexpr decltype(auto) operator()(T_settings, T_context&& context) const noexcept | |||
| @@ -16,7 +17,6 @@ beg_namespace_ecs_settings_scheduler | |||
| }; | |||
| } | |||
| constexpr decltype(auto) atomic_counter = __impl::atomic_counter_t { }; | |||
| constexpr decltype(auto) atomic_counter = detail::atomic_counter_builder_t { }; | |||
| } | |||
| end_namespace_ecs_settings_scheduler | |||
| } } } | |||
| @@ -9,8 +9,8 @@ | |||
| #include "./refresh_parallelism.h" | |||
| #include "./scheduler.h" | |||
| beg_namespace_ecs_settings | |||
| { | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace keys | |||
| { | |||
| @@ -22,86 +22,128 @@ beg_namespace_ecs_settings | |||
| constexpr decltype(auto) scheduler = hana::size_c<5>; | |||
| } | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * settings of the ecs environment | |||
| * | |||
| * @tparam T_options option map for the settings | |||
| */ | |||
| template<typename T_options> | |||
| struct settings_t | |||
| { | |||
| private: | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) change(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| auto new_options = T_options { }.set(key, std::forward<T_value>(value)); | |||
| return settings_t<core::mp::decay_t<decltype(new_options)>> { }; | |||
| } | |||
| /** | |||
| * change the value of a given key | |||
| * | |||
| * @tparam T_key key type to change value for | |||
| * @tparam T_value type of the new value | |||
| * | |||
| * @param key key to change the value for | |||
| * @param value new value | |||
| * | |||
| * @return updated settings | |||
| */ | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) change(const T_key& key, T_value&& value) const noexcept; | |||
| /** | |||
| * get the value of a given option key | |||
| * | |||
| * @tparam T_key key type to get the value for | |||
| * | |||
| * @param key key to get the value for | |||
| * | |||
| * @return value stored for key | |||
| */ | |||
| template<typename T_key> | |||
| constexpr decltype(auto) get(const T_key& key) const noexcept | |||
| { return T_options { }.at(key); } | |||
| constexpr decltype(auto) get(const T_key& key) const noexcept; | |||
| public: /* getter */ | |||
| constexpr decltype(auto) component_signatures() const noexcept | |||
| { return get(keys::component_signatures); } | |||
| constexpr decltype(auto) system_signatures() const noexcept | |||
| { return get(keys::system_signatures); } | |||
| constexpr decltype(auto) system_storage() const noexcept | |||
| { return get(keys::system_storage); } | |||
| constexpr decltype(auto) entity_storage() const noexcept | |||
| { return get(keys::entity_storage); } | |||
| constexpr decltype(auto) refresh_parallelism() const noexcept | |||
| { return get(keys::refresh_parallelism); } | |||
| constexpr decltype(auto) scheduler() const noexcept | |||
| { return get(keys::scheduler); } | |||
| /** | |||
| * get the list of component signatures | |||
| * | |||
| * @return list of component signatures | |||
| */ | |||
| constexpr decltype(auto) component_signatures() const noexcept; | |||
| /** | |||
| * get the list of system signatures | |||
| * | |||
| * @return list of system signatures | |||
| */ | |||
| constexpr decltype(auto) system_signatures() const noexcept; | |||
| /** | |||
| * get the system storeage builder | |||
| * | |||
| * @return system storage builder | |||
| */ | |||
| constexpr decltype(auto) system_storage() const noexcept; | |||
| /** | |||
| * get the entity storeage builder | |||
| * | |||
| * @return entity storage builder | |||
| */ | |||
| constexpr decltype(auto) entity_storage() const noexcept; | |||
| /** | |||
| * get the refresh parallelism value | |||
| * | |||
| * @return refresh parallelism value | |||
| */ | |||
| constexpr decltype(auto) refresh_parallelism() const noexcept; | |||
| /** | |||
| * get the scheduler builder | |||
| * | |||
| * @return the scheduler builder | |||
| */ | |||
| constexpr decltype(auto) scheduler() const noexcept; | |||
| public: /* setter */ | |||
| template<typename T> | |||
| constexpr decltype(auto) component_signatures(T t) const noexcept | |||
| { return change(keys::component_signatures, t); } | |||
| template<typename T> | |||
| constexpr decltype(auto) system_signatures(T t) const noexcept | |||
| { return change(keys::system_signatures, t); } | |||
| template<typename T_value> | |||
| constexpr decltype(auto) component_signatures(T_value value) const noexcept; | |||
| template<typename T> | |||
| constexpr decltype(auto) system_storage(T t) const noexcept | |||
| { return change(keys::system_storage, t); } | |||
| template<typename T_value> | |||
| constexpr decltype(auto) system_signatures(T_value value) const noexcept; | |||
| template<typename T> | |||
| constexpr decltype(auto) entity_storage(T t) const noexcept | |||
| { return change(keys::entity_storage, t); } | |||
| template<typename T_value> | |||
| constexpr decltype(auto) system_storage(T_value value) const noexcept; | |||
| template<typename T> | |||
| constexpr decltype(auto) refresh_parallelism(T t) const noexcept | |||
| { return change(keys::refresh_parallelism, t); } | |||
| template<typename T_value> | |||
| constexpr decltype(auto) entity_storage(T_value value) const noexcept; | |||
| template<typename T> | |||
| constexpr decltype(auto) scheduler(T t) const noexcept | |||
| { return change(keys::scheduler, t); } | |||
| template<typename T_value> | |||
| constexpr decltype(auto) refresh_parallelism(T_value value) const noexcept; | |||
| template<typename T_value> | |||
| constexpr decltype(auto) scheduler(T_value value) const noexcept; | |||
| }; | |||
| /** | |||
| * predicate type to build a default settings object | |||
| */ | |||
| struct make_t | |||
| { | |||
| constexpr decltype(auto) operator()() const noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| auto options = option_map::make() | |||
| .add(keys::component_signatures, list::empty) | |||
| .add(keys::system_signatures, list::empty) | |||
| .add(keys::system_storage, system_storage::tuple) | |||
| .add(keys::entity_storage, entity_storage::dynamic<1024>) | |||
| .add(keys::refresh_parallelism, refresh_parallelism::enable) | |||
| .add(keys::scheduler, scheduler::atomic_counter); | |||
| return settings_t<decay_t<decltype(options)>> { }; | |||
| } | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @return settings object (@ref settings_t) | |||
| */ | |||
| constexpr decltype(auto) operator()() const noexcept; | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make = __impl::make_t { }; | |||
| /** | |||
| * predicate to build a default settings object | |||
| */ | |||
| constexpr decltype(auto) make = detail::make_t { }; | |||
| } | |||
| end_namespace_ecs_settings | |||
| } } | |||
| @@ -0,0 +1,134 @@ | |||
| #pragma once | |||
| #include <ecs/settings/settings.h> | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace detail { | |||
| /* settings_t */ | |||
| template<typename T_options> | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::change(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| auto new_options = T_options { }.set(key, std::forward<T_value>(value)); | |||
| return settings_t<core::mp::decay_t<decltype(new_options)>> { }; | |||
| } | |||
| template<typename T_options> | |||
| template<typename T_key> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::get(const T_key& key) const noexcept | |||
| { | |||
| return T_options { }.at(key); | |||
| } | |||
| template<typename T_options> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::component_signatures() const noexcept | |||
| { | |||
| return get(keys::component_signatures); | |||
| } | |||
| template<typename T_options> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::system_signatures() const noexcept | |||
| { | |||
| return get(keys::system_signatures); | |||
| } | |||
| template<typename T_options> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::system_storage() const noexcept | |||
| { | |||
| return get(keys::system_storage); | |||
| } | |||
| template<typename T_options> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::entity_storage() const noexcept | |||
| { | |||
| return get(keys::entity_storage); | |||
| } | |||
| template<typename T_options> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::refresh_parallelism() const noexcept | |||
| { | |||
| return get(keys::refresh_parallelism); | |||
| } | |||
| template<typename T_options> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::scheduler() const noexcept | |||
| { | |||
| return get(keys::scheduler); | |||
| } | |||
| template<typename T_options> | |||
| template<typename T> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::component_signatures(T t) const noexcept | |||
| { | |||
| return change(keys::component_signatures, t); | |||
| } | |||
| template<typename T_options> | |||
| template<typename T> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::system_signatures(T t) const noexcept | |||
| { | |||
| return change(keys::system_signatures, t); | |||
| } | |||
| template<typename T_options> | |||
| template<typename T> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::system_storage(T t) const noexcept | |||
| { | |||
| return change(keys::system_storage, t); | |||
| } | |||
| template<typename T_options> | |||
| template<typename T> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::entity_storage(T t) const noexcept | |||
| { | |||
| return change(keys::entity_storage, t); | |||
| } | |||
| template<typename T_options> | |||
| template<typename T> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::refresh_parallelism(T t) const noexcept | |||
| { | |||
| return change(keys::refresh_parallelism, t); | |||
| } | |||
| template<typename T_options> | |||
| template<typename T> | |||
| constexpr decltype(auto) settings_t<T_options> | |||
| ::scheduler(T t) const noexcept | |||
| { | |||
| return change(keys::scheduler, t); | |||
| } | |||
| /* make_t */ | |||
| constexpr decltype(auto) make_t | |||
| ::operator()() const noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| auto options = option_map::make() | |||
| .add(keys::component_signatures, list::empty) | |||
| .add(keys::system_signatures, list::empty) | |||
| .add(keys::system_storage, system_storage::tuple) | |||
| .add(keys::entity_storage, entity_storage::dynamic<1024>) | |||
| .add(keys::refresh_parallelism, refresh_parallelism::enable) | |||
| .add(keys::scheduler, scheduler::atomic_counter); | |||
| return settings_t<decay_t<decltype(options)>> { }; | |||
| } | |||
| } } } | |||
| @@ -3,10 +3,11 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/storage/tuple.h> | |||
| beg_namespace_ecs_settings_system_storage | |||
| { | |||
| namespace ecs { | |||
| namespace settings { | |||
| namespace system_storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct tuple_storage_builder | |||
| { | |||
| @@ -16,7 +17,6 @@ beg_namespace_ecs_settings_system_storage | |||
| }; | |||
| } | |||
| constexpr decltype(auto) tuple = __impl::tuple_storage_builder { }; | |||
| constexpr decltype(auto) tuple = detail::tuple_storage_builder { }; | |||
| } | |||
| end_namespace_ecs_settings_system_storage | |||
| } } } | |||
| @@ -2,4 +2,6 @@ | |||
| #include "./component/signature.h" | |||
| #include "./component/storage.h" | |||
| #include "./system/signature.h" | |||
| #include "./system/signature.h" | |||
| #include "./component/signature.inl" | |||
| @@ -7,73 +7,133 @@ | |||
| #include <ecs/tag/component.h> | |||
| #include "./storage.h" | |||
| beg_namespace_ecs_signature_component | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace keys | |||
| { | |||
| constexpr decltype(auto) storage = hana::size_c<0>; | |||
| } | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * defines a component signature | |||
| * | |||
| * @tparam T_component_tag component tag the signature is defined for | |||
| * @tparam T_options option map for the component signature | |||
| */ | |||
| template<typename T_component_tag_list, typename T_options> | |||
| struct signature_t | |||
| { | |||
| static_assert(tag::component::is_list(T_component_tag_list { }) == hana::true_c, "component signature needs a list of component tags"); | |||
| private: | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) change(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| auto new_options = T_options { }.set(key, std::forward<T_value>(value)); | |||
| return signature_t<T_component_tag_list, core::mp::decay_t<decltype(new_options)>> { }; | |||
| } | |||
| /** | |||
| * change the value of the given option key | |||
| * | |||
| * @tparam T_key key type to set the value for | |||
| * @tparam T_value value type of the new value | |||
| * | |||
| * @param key key to set the value for | |||
| * @param value new value | |||
| * | |||
| * @return updated component signature | |||
| */ | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) change(const T_key& key, T_value&& value) const noexcept; | |||
| /** | |||
| * get the value of the given option key | |||
| * | |||
| * @tparam T_key key type to get the value for | |||
| * | |||
| * @param key key to get the value for | |||
| * | |||
| * @return value stored for the given key | |||
| */ | |||
| template<typename T_key> | |||
| constexpr decltype(auto) get(const T_key& key) const noexcept | |||
| { return T_options { }.at(key); } | |||
| public: | |||
| constexpr decltype(auto) tags() const noexcept | |||
| { return T_component_tag_list { }; } | |||
| constexpr decltype(auto) get(const T_key& key) const noexcept; | |||
| public: /* tags */ | |||
| /** | |||
| * get a list of all component tags for this signature | |||
| * | |||
| * @return list of component tags managed by this signature | |||
| */ | |||
| constexpr decltype(auto) tags() const noexcept; | |||
| /** | |||
| * check if this signature has a certain component tag | |||
| * | |||
| * @tparam T_component_tag component tag type to check | |||
| * | |||
| * @param ct component tag to check | |||
| * | |||
| * @retval integral constant TRUE if the given component tag is managed by this signature | |||
| * @retval integral constant FALSE if the given component tag is not managed by this signature | |||
| */ | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) has(T_component_tag ct) const noexcept | |||
| { return hana::contains(T_component_tag_list { }, ct); } | |||
| constexpr decltype(auto) has(T_component_tag ct) const noexcept; | |||
| public: /* options */ | |||
| /** | |||
| * set the storage option of this component signature | |||
| * | |||
| * @tparam T_storage storage builder type (specialization of @ref storage::storage_builder_interface_t) | |||
| * | |||
| * @param storage storage builder (specialized instance of @ref storage::storage_builder_interface_t) | |||
| * | |||
| * @return updated component signature | |||
| */ | |||
| template<typename T_storage> | |||
| constexpr decltype(auto) storage(T_storage&& storage) const noexcept | |||
| { return change(keys::storage, std::forward<T_storage>(storage)); } | |||
| constexpr decltype(auto) storage() const noexcept | |||
| { return get(keys::storage); } | |||
| constexpr decltype(auto) storage(T_storage&& storage) const noexcept; | |||
| /** | |||
| * get the storage option of this component signature | |||
| * | |||
| * @return storage builder (specialized instance of @ref storage::storage_builder_interface_t) | |||
| */ | |||
| constexpr decltype(auto) storage() const noexcept; | |||
| }; | |||
| /** | |||
| * predicate class to create a component signature | |||
| */ | |||
| struct make_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_component_tags component tag types to build component signature for | |||
| * | |||
| * @param cts component tags to build component signature for | |||
| * | |||
| * @return component signature (@ref detail::signature_t) | |||
| */ | |||
| template<typename... T_component_tags> | |||
| constexpr decltype(auto) operator()(T_component_tags... cts) const noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| auto ctl = list::make(cts...); | |||
| auto options = option_map::make() | |||
| .add(keys::storage, storage::default_); | |||
| using signature_type = signature_t<decay_t<decltype(ctl)>, decay_t<decltype(options)>>; | |||
| return signature_type { }; | |||
| } | |||
| constexpr decltype(auto) operator()(T_component_tags... cts) const noexcept; | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make = __impl::make_t { }; | |||
| /** | |||
| * predicate to create a new component signature (see @ref detail::signature_t) | |||
| */ | |||
| constexpr decltype(auto) make = detail::make_t { }; | |||
| constexpr decltype(auto) is_valid = core::mp::is_valid<__impl::signature_t>; | |||
| /** | |||
| * predicate to check if a component signature is valid | |||
| */ | |||
| constexpr decltype(auto) is_valid = core::mp::is_valid<detail::signature_t>; | |||
| constexpr decltype(auto) is_list = core::mp::is_list<__impl::signature_t>; | |||
| /** | |||
| * predicate to check if all component signatures in a list are valid | |||
| */ | |||
| constexpr decltype(auto) is_list = core::mp::is_list<detail::signature_t>; | |||
| } | |||
| end_namespace_ecs_signature_component | |||
| } } } | |||
| @@ -0,0 +1,81 @@ | |||
| #pragma once | |||
| #include "signature.h" | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace detail | |||
| { | |||
| /* signature_t */ | |||
| template<typename T_component_tag_list, typename T_options> | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) signature_t<T_component_tag_list, T_options> | |||
| ::change(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| auto new_options = T_options { }.set(key, std::forward<T_value>(value)); | |||
| return signature_t<T_component_tag_list, core::mp::decay_t<decltype(new_options)>> { }; | |||
| } | |||
| template<typename T_component_tag_list, typename T_options> | |||
| template<typename T_key> | |||
| constexpr decltype(auto) signature_t<T_component_tag_list, T_options> | |||
| ::get(const T_key& key) const noexcept | |||
| { | |||
| return T_options { }.at(key); | |||
| } | |||
| template<typename T_component_tag_list, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_component_tag_list, T_options> | |||
| ::tags() const noexcept | |||
| { | |||
| return T_component_tag_list { }; | |||
| } | |||
| template<typename T_component_tag_list, typename T_options> | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) signature_t<T_component_tag_list, T_options> | |||
| ::has(T_component_tag ct) const noexcept | |||
| { | |||
| return hana::contains(T_component_tag_list { }, ct); | |||
| } | |||
| template<typename T_component_tag_list, typename T_options> | |||
| template<typename T_storage> | |||
| constexpr decltype(auto) signature_t<T_component_tag_list, T_options> | |||
| ::storage(T_storage&& storage) const noexcept | |||
| { | |||
| return change(keys::storage, std::forward<T_storage>(storage)); | |||
| } | |||
| template<typename T_component_tag_list, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_component_tag_list, T_options> | |||
| ::storage() const noexcept | |||
| { | |||
| return get(keys::storage); | |||
| } | |||
| /* make_t */ | |||
| template<typename... T_component_tags> | |||
| constexpr decltype(auto) make_t | |||
| ::operator()(T_component_tags... cts) const noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| auto ctl = list::make(cts...); | |||
| auto options = option_map::make() | |||
| .add(keys::storage, storage::default_); | |||
| using signature_type = signature_t<decay_t<decltype(ctl)>, decay_t<decltype(options)>>; | |||
| return signature_type { }; | |||
| } | |||
| } | |||
| } } } | |||
| @@ -3,4 +3,5 @@ | |||
| #include "./storage/default.h" | |||
| #include "./storage/dynamic.h" | |||
| #include "./storage/empty.h" | |||
| #include "./storage/fixed.h" | |||
| #include "./storage/fixed.h" | |||
| #include "./storage/interface.h" | |||
| @@ -3,11 +3,20 @@ | |||
| #include "./empty.h" | |||
| #include "./dynamic.h" | |||
| beg_namespace_ecs_signature_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * Default storage builder. | |||
| * Specializes @ref ::ecs::signature::component::storage::storage_builder_interface_t. | |||
| * | |||
| * Will redirect to the empty storage builder (@ref empty_builder_t) if the components of the component | |||
| * signature are all empty. Will use the dynamic builder (@ref dynamic_builder_t) otherwise. | |||
| */ | |||
| struct default_builder_t | |||
| { | |||
| template<typename T_settings, typename T_component_tag_list> | |||
| @@ -23,7 +32,9 @@ beg_namespace_ecs_signature_component_storage | |||
| }; | |||
| } | |||
| constexpr decltype(auto) default_ = __impl::default_builder_t { }; | |||
| /** | |||
| * predicate to build a default component storage | |||
| */ | |||
| constexpr decltype(auto) default_ = detail::default_builder_t { }; | |||
| } | |||
| end_namespace_ecs_signature_component_storage | |||
| } } } } | |||
| @@ -3,11 +3,21 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/component/storage/dynamic.h> | |||
| beg_namespace_ecs_signature_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * Dynamic storage builder. | |||
| * Specializes @ref ::ecs::signature::component::storage::storage_builder_interface_t. | |||
| * | |||
| * Will build a component storage that will automatically increase in size if needed. | |||
| * | |||
| * @tparam T_size initial size of the storage | |||
| */ | |||
| template<size_t T_size> | |||
| struct dynamic_builder_t | |||
| { | |||
| @@ -17,8 +27,12 @@ beg_namespace_ecs_signature_component_storage | |||
| }; | |||
| } | |||
| template<size_t T_size> | |||
| constexpr decltype(auto) dynamic = __impl::dynamic_builder_t<T_size> { }; | |||
| /** | |||
| * predicate to build a dynamic component storage | |||
| * | |||
| * @tparam T_size initial size of the storage | |||
| */ | |||
| template<size_t T_size = 1024> | |||
| constexpr decltype(auto) dynamic = detail::dynamic_builder_t<T_size> { }; | |||
| } | |||
| end_namespace_ecs_signature_component_storage | |||
| } } } } | |||
| @@ -3,11 +3,19 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/component/storage/empty.h> | |||
| beg_namespace_ecs_signature_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * Empty storage builder. | |||
| * Specializes @ref ::ecs::signature::component::storage::storage_builder_interface_t. | |||
| * | |||
| * Will build a component storage that does not store any component. | |||
| */ | |||
| struct empty_builder_t | |||
| { | |||
| template<typename T_settings, typename T_component_tag_list> | |||
| @@ -16,7 +24,9 @@ beg_namespace_ecs_signature_component_storage | |||
| }; | |||
| } | |||
| constexpr decltype(auto) empty = __impl::empty_builder_t { }; | |||
| /** | |||
| * predicate to build an empty component storage | |||
| */ | |||
| constexpr decltype(auto) empty = detail::empty_builder_t { }; | |||
| } | |||
| end_namespace_ecs_signature_component_storage | |||
| } } } } | |||
| @@ -3,11 +3,21 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/component/storage/fixed.h> | |||
| beg_namespace_ecs_signature_component_storage | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace storage { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * Fixed storage builder. | |||
| * Specializes @ref ::ecs::signature::component::storage::storage_builder_interface_t. | |||
| * | |||
| * Will build a component storage that will store components in memory of fixed size. | |||
| * | |||
| * @tparam T_size size of the storage | |||
| */ | |||
| template<size_t T_size> | |||
| struct fixed_builder_t | |||
| { | |||
| @@ -17,8 +27,12 @@ beg_namespace_ecs_signature_component_storage | |||
| }; | |||
| } | |||
| template<size_t T_size> | |||
| constexpr decltype(auto) fixed = __impl::fixed_builder_t<T_size> { }; | |||
| /** | |||
| * predicate to build a fixed component storage | |||
| * | |||
| * @tparam T_size size of the storage | |||
| */ | |||
| template<size_t T_size = 1024> | |||
| constexpr decltype(auto) fixed = detail::fixed_builder_t<T_size> { }; | |||
| } | |||
| end_namespace_ecs_signature_component_storage | |||
| } } } } | |||
| @@ -0,0 +1,31 @@ | |||
| #pragma once | |||
| #include "./empty.h" | |||
| #include "./dynamic.h" | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace component { | |||
| namespace storage { | |||
| /** | |||
| * predicate type to build a @ref ::ecs::core::component::storage_iterface_t | |||
| */ | |||
| struct storage_builder_interface_t | |||
| { | |||
| /** | |||
| * execute the predicate | |||
| * | |||
| * @tparam T_settings settings type (specialization of @ref ::ecs::settings::detail::settings_t) | |||
| * @tparam T_component_tag_list list of component tags type to build storage for | |||
| * | |||
| * @param settings ecs system settings (instnace of @ref ::ecs::settings::detail::settings_t) | |||
| * @param ctl list of component tags to build storage for | |||
| * | |||
| * @return component storage (instance of type @ref ::ecs::core::component::storage_iterface_t) | |||
| */ | |||
| template<typename T_settings, typename T_component_tag_list> | |||
| constexpr decltype(auto) operator()(T_settings settings, T_component_tag_list ctl) const; | |||
| }; | |||
| } } } } | |||
| @@ -2,4 +2,6 @@ | |||
| #include "./system/output.h" | |||
| #include "./system/parallelism.h" | |||
| #include "./system/signature.h" | |||
| #include "./system/signature.h" | |||
| #include "./system/signature.inl" | |||
| @@ -2,10 +2,12 @@ | |||
| #include <ecs/config.h> | |||
| beg_namespace_ecs_signature_system_output | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace output { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct empty_output | |||
| { }; | |||
| @@ -17,12 +19,11 @@ beg_namespace_ecs_signature_system_output | |||
| } | |||
| template<typename T_output> | |||
| using type = __impl::output_t<T_output>; | |||
| using type = detail::output_t<T_output>; | |||
| template<typename T_output> | |||
| constexpr decltype(auto) value = type<T_output> { }; | |||
| constexpr decltype(auto) none = value<__impl::empty_output>; | |||
| constexpr decltype(auto) none = value<detail::empty_output>; | |||
| } | |||
| end_namespace_ecs_signature_system_output | |||
| } } } } | |||
| @@ -3,10 +3,12 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/parallelism/composer/fixed_threshold.h> | |||
| beg_namespace_ecs_signature_system_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace parallelism { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<typename T_threshold, typename T_lower_strategy, typename T_greater_strategy> | |||
| struct fixed_threshold_builder_t | |||
| @@ -33,7 +35,7 @@ beg_namespace_ecs_signature_system_parallelism | |||
| }; | |||
| } | |||
| constexpr decltype(auto) fixed_threshold = __impl::fixed_threshold_t { }; | |||
| /** fuuu */ | |||
| constexpr decltype(auto) fixed_threshold = detail::fixed_threshold_t { }; | |||
| } | |||
| end_namespace_ecs_signature_system_parallelism | |||
| } } } } | |||
| @@ -5,10 +5,12 @@ | |||
| #include "./fixed_threshold.h" | |||
| #include "../strategy/none.h" | |||
| beg_namespace_ecs_signature_system_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace parallelism { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct none_below_threshold_t | |||
| { | |||
| @@ -18,7 +20,6 @@ beg_namespace_ecs_signature_system_parallelism | |||
| }; | |||
| } | |||
| constexpr decltype(auto) none_below_threshold = __impl::none_below_threshold_t { }; | |||
| constexpr decltype(auto) none_below_threshold = detail::none_below_threshold_t { }; | |||
| } | |||
| end_namespace_ecs_signature_system_parallelism | |||
| } } } } | |||
| @@ -3,10 +3,12 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/parallelism/strategy/none.h> | |||
| beg_namespace_ecs_signature_system_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace parallelism { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct none_builder_t | |||
| { | |||
| @@ -21,7 +23,6 @@ beg_namespace_ecs_signature_system_parallelism | |||
| }; | |||
| } | |||
| constexpr decltype(auto) none = __impl::none_t { }; | |||
| constexpr decltype(auto) none = detail::none_t { }; | |||
| } | |||
| end_namespace_ecs_signature_system_parallelism | |||
| } } } } | |||
| @@ -3,10 +3,12 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/parallelism/strategy/split_evenly.h> | |||
| beg_namespace_ecs_signature_system_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace parallelism { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<typename T_split> | |||
| struct split_evenly_builder_t | |||
| @@ -29,7 +31,6 @@ beg_namespace_ecs_signature_system_parallelism | |||
| }; | |||
| } | |||
| constexpr decltype(auto) split_evenly = __impl::split_evenly_t { }; | |||
| constexpr decltype(auto) split_evenly = detail::split_evenly_t { }; | |||
| } | |||
| end_namespace_ecs_signature_system_parallelism | |||
| } } } } | |||
| @@ -3,10 +3,12 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/parallelism/strategy/split_evenly.h> | |||
| beg_namespace_ecs_signature_system_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace parallelism { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| struct split_evenly_cores_builder_t | |||
| { | |||
| @@ -32,7 +34,6 @@ beg_namespace_ecs_signature_system_parallelism | |||
| }; | |||
| } | |||
| constexpr decltype(auto) split_evenly_cores = __impl::split_evenly_cores_t { }; | |||
| constexpr decltype(auto) split_evenly_cores = detail::split_evenly_cores_t { }; | |||
| } | |||
| end_namespace_ecs_signature_system_parallelism | |||
| } } } } | |||
| @@ -3,10 +3,12 @@ | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/system/parallelism/strategy/split_every.h> | |||
| beg_namespace_ecs_signature_system_parallelism | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace parallelism { | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| template<typename T_split> | |||
| struct split_every_builder_t | |||
| @@ -29,7 +31,6 @@ beg_namespace_ecs_signature_system_parallelism | |||
| }; | |||
| } | |||
| constexpr decltype(auto) split_every = __impl::split_every_t { }; | |||
| constexpr decltype(auto) split_every = detail::split_every_t { }; | |||
| } | |||
| end_namespace_ecs_signature_system_parallelism | |||
| } } } } | |||
| @@ -8,8 +8,9 @@ | |||
| #include "./output.h" | |||
| #include "./parallelism.h" | |||
| beg_namespace_ecs_signature_system | |||
| { | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace keys | |||
| { | |||
| @@ -20,8 +21,15 @@ beg_namespace_ecs_signature_system | |||
| constexpr decltype(auto) output = hana::size_c<4>; | |||
| } | |||
| namespace __impl | |||
| namespace detail | |||
| { | |||
| /** | |||
| * defines a system signature | |||
| * | |||
| * @tparam T_system_tag system tag the signature is defined for | |||
| * @tparam T_options option map for the system signature | |||
| */ | |||
| template<typename T_system_tag, typename T_options> | |||
| struct signature_t | |||
| { | |||
| @@ -30,89 +38,130 @@ beg_namespace_ecs_signature_system | |||
| using tag_type = T_system_tag; | |||
| private: | |||
| /** | |||
| * change the value of an option key | |||
| * | |||
| * @tparam T_key key type to change value for | |||
| * @tparam T_value value type of the new value | |||
| * | |||
| * @param key key to change the value for | |||
| * @param value new value | |||
| * | |||
| * @return updated system signature | |||
| */ | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) change(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| auto new_options = T_options { }.set(key, std::forward<T_value>(value)); | |||
| using impl_type = signature_t<T_system_tag, core::mp::decay_t<decltype(new_options)>>; | |||
| return impl_type { }; | |||
| } | |||
| constexpr decltype(auto) change(const T_key& key, T_value&& value) const noexcept; | |||
| /** | |||
| * get the value of an option key | |||
| * | |||
| * @tparam T_key key type to get value for | |||
| * | |||
| * @param key key to get the value for | |||
| * | |||
| * @return value stored for key | |||
| */ | |||
| template<typename T_key> | |||
| constexpr decltype(auto) get(const T_key& key) const noexcept | |||
| { return T_options { }.at(key); } | |||
| constexpr decltype(auto) get(const T_key& key) const noexcept; | |||
| public: /* misc */ | |||
| constexpr decltype(auto) tag() const noexcept | |||
| { return T_system_tag { }; } | |||
| /** | |||
| * get the system tag of this system signature | |||
| * | |||
| * @return system tag of this system signature | |||
| */ | |||
| constexpr decltype(auto) tag() const noexcept; | |||
| /** | |||
| * check if the given component tag can be written by this system | |||
| * | |||
| * @tparam T_component_tag component tag type to check write access for | |||
| * | |||
| * @param ct component tag to check write access for | |||
| * | |||
| * @retval integral constant TRUE if the given component tag can be written | |||
| * @retval integral constant FALSE if the given component tag can not be written | |||
| */ | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) can_write(T_component_tag ct) const noexcept; | |||
| /** | |||
| * check if the given component tag can be read by this system | |||
| * | |||
| * @tparam T_component_tag component tag type to check read access for | |||
| * | |||
| * @param ct component tag to check read access for | |||
| * | |||
| * @retval integral constant TRUE if the given component tag can be read | |||
| * @retval integral constant FALSE if the given component tag can not be read | |||
| */ | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) can_read(T_component_tag ct) const noexcept; | |||
| public: /* getter */ | |||
| constexpr decltype(auto) parallelism() const noexcept | |||
| { return get(keys::parallelism); } | |||
| constexpr decltype(auto) dependencies() const noexcept | |||
| { return get(keys::dependencies); } | |||
| /** | |||
| * get the value of the parallelism option | |||
| */ | |||
| constexpr decltype(auto) parallelism() const noexcept; | |||
| /** get a list of system tags that system depends on */ | |||
| constexpr decltype(auto) dependencies() const noexcept; | |||
| constexpr decltype(auto) read() const noexcept | |||
| { return get(keys::read_components); } | |||
| /** get a list of component tags this system wants to read */ | |||
| constexpr decltype(auto) read() const noexcept; | |||
| constexpr decltype(auto) write() const noexcept | |||
| { return get(keys::write_components); } | |||
| /** get a list of component tags this system wants to write */ | |||
| constexpr decltype(auto) write() const noexcept; | |||
| constexpr decltype(auto) output() const noexcept | |||
| { return get(keys::output); } | |||
| /** get the output type of the system */ | |||
| constexpr decltype(auto) output() const noexcept; | |||
| public: /* setter */ | |||
| /** set the parallelism options | |||
| * @param parallelism is a predicate the takes no parameters and returns a | |||
| * system executor interface */ | |||
| template<typename T_parallelism> | |||
| constexpr decltype(auto) parallelism(T_parallelism parallelism) const noexcept | |||
| { return change(keys::parallelism, parallelism); } | |||
| constexpr decltype(auto) parallelism(T_parallelism parallelism) const noexcept; | |||
| /** set a list of system tags, that system depends on */ | |||
| template<typename... T_system_tags> | |||
| constexpr decltype(auto) dependencies(T_system_tags... tags) const noexcept | |||
| { return change(keys::dependencies, core::mp::list::make(tags...)); } | |||
| constexpr decltype(auto) dependencies(T_system_tags... tags) const noexcept; | |||
| /** set a list of component tags, this system wants to read */ | |||
| template<typename... T_component_tags> | |||
| constexpr decltype(auto) read(T_component_tags... tags) const noexcept | |||
| { return change(keys::read_components, core::mp::list::make(tags...)); } | |||
| constexpr decltype(auto) read(T_component_tags... tags) const noexcept; | |||
| /** set a list of component tags, this system wants to write */ | |||
| template<typename... T_component_tags> | |||
| constexpr decltype(auto) write(T_component_tags... tags) const noexcept | |||
| { return change(keys::write_components, core::mp::list::make(tags...)); } | |||
| constexpr decltype(auto) write(T_component_tags... tags) const noexcept; | |||
| /** set the output type of this system */ | |||
| template<typename T_output> | |||
| constexpr decltype(auto) output(T_output output) const noexcept | |||
| { return change(keys::output, output); } | |||
| constexpr decltype(auto) output(T_output output) const noexcept; | |||
| }; | |||
| /** predicate class to create a system signature */ | |||
| struct make_t | |||
| { | |||
| template<typename T_system_tag> | |||
| constexpr decltype(auto) operator()(T_system_tag) const noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| auto options = option_map::make() | |||
| .add(keys::parallelism, parallelism::none()) | |||
| .add(keys::dependencies, list::empty) | |||
| .add(keys::read_components, list::empty) | |||
| .add(keys::write_components, list::empty) | |||
| .add(keys::output, output::none); | |||
| using signature_type = signature_t<T_system_tag, decay_t<decltype(options)>>; | |||
| return signature_type { }; | |||
| } | |||
| constexpr decltype(auto) operator()(T_system_tag) const noexcept; | |||
| }; | |||
| } | |||
| constexpr decltype(auto) make = __impl::make_t { }; | |||
| /** predicate to create a system signature */ | |||
| constexpr decltype(auto) make = detail::make_t { }; | |||
| constexpr decltype(auto) is_valid = core::mp::is_valid<__impl::signature_t>; | |||
| /** predicate to check if a system signature is valid */ | |||
| constexpr decltype(auto) is_valid = core::mp::is_valid<detail::signature_t>; | |||
| constexpr decltype(auto) is_list = core::mp::is_list<__impl::signature_t>; | |||
| /** predicate to check if a list of system signatures is valid */ | |||
| constexpr decltype(auto) is_list = core::mp::is_list<detail::signature_t>; | |||
| /** get the system tag type of a system signature type */ | |||
| template<typename T_system_sig> | |||
| using tag_type = typename core::mp::unwrap_t<T_system_sig>::tag_type; | |||
| using tag_type_t = typename core::mp::unwrap_t<T_system_sig>::tag_type; | |||
| } | |||
| end_namespace_ecs_signature_system | |||
| } } } | |||
| @@ -0,0 +1,158 @@ | |||
| #pragma once | |||
| #include <ecs/config.h> | |||
| #include <ecs/core/mp/core.h> | |||
| #include <ecs/core/mp/list.h> | |||
| #include <ecs/core/mp/option_map.h> | |||
| #include <ecs/tag/system.h> | |||
| #include "./output.h" | |||
| #include "./parallelism.h" | |||
| namespace ecs { | |||
| namespace signature { | |||
| namespace system { | |||
| namespace detail | |||
| { | |||
| /* signature_t */ | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename T_key, typename T_value> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::change(const T_key& key, T_value&& value) const noexcept | |||
| { | |||
| auto new_options = T_options { }.set(key, std::forward<T_value>(value)); | |||
| using impl_type = signature_t<T_system_tag, core::mp::decay_t<decltype(new_options)>>; | |||
| return impl_type { }; | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename T_key> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::get(const T_key& key) const noexcept | |||
| { | |||
| return T_options { }.at(key); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::tag() const noexcept | |||
| { | |||
| return T_system_tag { }; | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::can_write(T_component_tag ct) const noexcept | |||
| { | |||
| return hana::contains(write(), ct); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename T_component_tag> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::can_read(T_component_tag ct) const noexcept | |||
| { | |||
| return hana::contains(read(), ct); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::parallelism() const noexcept | |||
| { | |||
| return get(keys::parallelism); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::dependencies() const noexcept | |||
| { | |||
| return get(keys::dependencies); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::read() const noexcept | |||
| { | |||
| return get(keys::read_components); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::write() const noexcept | |||
| { | |||
| return get(keys::write_components); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::output() const noexcept | |||
| { | |||
| return get(keys::output); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename T_parallelism> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::parallelism(T_parallelism parallelism) const noexcept | |||
| { | |||
| return change(keys::parallelism, parallelism); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename... T_system_tags> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::dependencies(T_system_tags... tags) const noexcept | |||
| { | |||
| return change(keys::dependencies, core::mp::list::make(tags...)); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename... T_component_tags> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::read(T_component_tags... tags) const noexcept | |||
| { | |||
| return change(keys::read_components, core::mp::list::make(tags...)); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename... T_component_tags> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::write(T_component_tags... tags) const noexcept | |||
| { | |||
| return change(keys::write_components, core::mp::list::make(tags...)); | |||
| } | |||
| template<typename T_system_tag, typename T_options> | |||
| template<typename T_output> | |||
| constexpr decltype(auto) signature_t<T_system_tag, T_options> | |||
| ::output(T_output output) const noexcept | |||
| { | |||
| return change(keys::output, output); | |||
| } | |||
| /* make_t */ | |||
| template<typename T_system_tag> | |||
| constexpr decltype(auto) make_t | |||
| ::operator()(T_system_tag) const noexcept | |||
| { | |||
| using namespace ::ecs::core::mp; | |||
| auto options = option_map::make() | |||
| .add(keys::parallelism, parallelism::none()) | |||
| .add(keys::dependencies, list::empty) | |||
| .add(keys::read_components, list::empty) | |||
| .add(keys::write_components, list::empty) | |||
| .add(keys::output, output::none); | |||
| using signature_type = signature_t<T_system_tag, decay_t<decltype(options)>>; | |||
| return signature_type { }; | |||
| } | |||
| } | |||
| } } } | |||