Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd83fba6cd | ||
|
|
15b9255a25 | ||
|
|
6794d21487 | ||
|
|
118c4432ec | ||
|
|
ccda429bf1 | ||
|
|
590937d2a0 | ||
|
|
241827dd80 | ||
|
|
77b4e0b4bd | ||
|
|
6a53cb32d8 | ||
|
|
f12ff3b15e | ||
|
|
69ad8676b7 | ||
|
|
d0fd756f6b | ||
|
|
7f8ab67e9e | ||
|
|
0e68bb3d2c | ||
|
|
d5b3933752 | ||
|
|
0b7206a92d | ||
|
|
e875f306fd | ||
|
|
8ed5e5ee28 | ||
|
|
948b0d40f6 | ||
|
|
48f4feb7a7 | ||
|
|
ddc0a32bbc | ||
|
|
10a7c54364 | ||
|
|
a66fa9d844 | ||
|
|
a16f2ac15c | ||
|
|
b699797a40 | ||
|
|
a6e9520d06 | ||
|
|
f8310b1296 | ||
|
|
353bf99cd5 | ||
|
|
a478e4acc9 | ||
|
|
d810e0ba7d | ||
|
|
1e51ffdb72 | ||
|
|
7da1d1fc64 | ||
|
|
957697c383 | ||
|
|
107eb72225 | ||
|
|
810b77f9da | ||
|
|
5e3bc2049b | ||
|
|
229500347d | ||
|
|
1f461db0a1 | ||
|
|
6c55aafee3 | ||
|
|
d57e55b719 | ||
|
|
bf772e5fe5 | ||
|
|
413f3356ce | ||
|
|
f147326fe0 | ||
|
|
30c59644b6 | ||
|
|
3fa5acf2e6 | ||
|
|
3e6ded8823 | ||
|
|
612017aaa2 | ||
|
|
ef57d7e7b6 | ||
|
|
a8d0db5036 | ||
|
|
ad6b5f8fc1 | ||
|
|
e3cb6a0aec | ||
|
|
cbf18a7dc4 | ||
|
|
01559410a9 | ||
|
|
f2ab94fa7f | ||
|
|
d7394a8369 | ||
|
|
9feef11d6f | ||
|
|
dc4e5ddc3c | ||
|
|
8600781bb6 | ||
|
|
bdc7bbdc9d | ||
|
|
73badef594 | ||
|
|
9474e6c08c |
@@ -37,7 +37,7 @@ matrix:
|
||||
compiler: clang
|
||||
env: COMPILER=clang++
|
||||
- os: osx
|
||||
osx_image: xcode9.1
|
||||
osx_image: xcode9.4
|
||||
compiler: clang
|
||||
env: COMPILER=clang++
|
||||
- os: linux
|
||||
@@ -71,3 +71,9 @@ script:
|
||||
- mkdir -p build && cd build
|
||||
- cmake .. && make -j4
|
||||
- CTEST_OUTPUT_ON_FAILURE=1 make test
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: scripts/update_packages.sh $TRAVIS_TAG
|
||||
on:
|
||||
tags: true
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -7,3 +7,5 @@ Michele Caini aka skypjack
|
||||
Paolo Monteverde aka morbo84
|
||||
David Nerjes aka DavidHamburg
|
||||
Indi Kernick aka Kerndog73
|
||||
Malte Müller-Rowold aka m-waka
|
||||
Richard Caseres aka richardbmx
|
||||
|
||||
169
CMakeLists.txt
169
CMakeLists.txt
@@ -2,7 +2,7 @@
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.7.2)
|
||||
|
||||
#
|
||||
# Building in-tree is not allowed (we take care of your craziness).
|
||||
@@ -16,7 +16,9 @@ endif()
|
||||
# Project configuration
|
||||
#
|
||||
|
||||
project(entt VERSION 2.6.1)
|
||||
project(EnTT VERSION 2.7.2)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
@@ -29,22 +31,24 @@ set(PROJECT_AUTHOR_EMAIL "michele.caini@gmail.com")
|
||||
|
||||
message("*")
|
||||
message("* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message("* Copyright (c) 2018 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
|
||||
message("* Copyright (c) 2017-2018 ${PROJECT_AUTHOR} <${PROJECT_AUTHOR_EMAIL}>")
|
||||
message("*")
|
||||
|
||||
option(USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if availbale." ON)
|
||||
option(USE_ASAN "Use address sanitizer by adding -fsanitize=address -fno-omit-frame-pointer flags" OFF)
|
||||
option(USE_COMPILE_OPTIONS "Use compile options from EnTT." ON)
|
||||
|
||||
#
|
||||
# Compiler stuff
|
||||
#
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(NOT MSVC)
|
||||
if(NOT MSVC AND USE_LIBCPP)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(CMakePushCheckState)
|
||||
|
||||
set(OLD_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
cmake_push_check_state()
|
||||
|
||||
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include<type_traits>
|
||||
@@ -52,28 +56,107 @@ if(NOT MSVC)
|
||||
" HAS_LIBCPP)
|
||||
|
||||
if(NOT HAS_LIBCPP)
|
||||
set(CMAKE_CXX_FLAGS "${OLD_CMAKE_CXX_FLAGS}")
|
||||
message(WARNING "The option USE_LIBCPP is set (by default) but libc++ is not available. The flag will not be added to the target.")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -DRELEASE")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -DDEBUG")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# it seems that -O3 ruins the performance when using clang ...
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
|
||||
else()
|
||||
# ... on the other side, GCC is incredibly comfortable with it.
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
endif()
|
||||
cmake_pop_check_state()
|
||||
endif()
|
||||
|
||||
#
|
||||
# Include EnTT
|
||||
# Add EnTT target
|
||||
#
|
||||
|
||||
include_directories(${entt_SOURCE_DIR}/src)
|
||||
add_library(EnTT INTERFACE)
|
||||
|
||||
target_include_directories(
|
||||
EnTT INTERFACE
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src>
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
target_compile_definitions(
|
||||
EnTT
|
||||
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:DEBUG>
|
||||
INTERFACE $<$<AND:$<CONFIG:Release>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:RELEASE>
|
||||
)
|
||||
|
||||
if(USE_ASAN)
|
||||
target_compile_options(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-fsanitize=address -fno-omit-frame-pointer>)
|
||||
target_link_libraries(EnTT INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-fsanitize=address -fno-omit-frame-pointer>)
|
||||
endif()
|
||||
|
||||
if(USE_COMPILE_OPTIONS)
|
||||
target_compile_options(
|
||||
EnTT
|
||||
INTERFACE $<$<AND:$<CONFIG:Debug>,$<NOT:$<CXX_COMPILER_ID:MSVC>>>:-O0 -g>
|
||||
# it seems that -O3 ruins a bit the performance when using clang ...
|
||||
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:Clang>>:-O2>
|
||||
# ... on the other side, GCC is incredibly comfortable with it.
|
||||
INTERFACE $<$<AND:$<CONFIG:Release>,$<CXX_COMPILER_ID:GNU>>:-O3>
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
endif()
|
||||
|
||||
target_compile_features(EnTT INTERFACE cxx_std_14)
|
||||
|
||||
#
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
set(CUSTOM_INSTALL_CONFIGDIR cmake)
|
||||
else()
|
||||
set(CUSTOM_INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/entt)
|
||||
endif()
|
||||
|
||||
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
install(TARGETS EnTT EXPORT EnTTTargets)
|
||||
|
||||
export(EXPORT EnTTTargets FILE ${EnTT_BINARY_DIR}/EnTTTargets.cmake)
|
||||
|
||||
install(
|
||||
EXPORT EnTTTargets
|
||||
FILE EnTTTargets.cmake
|
||||
DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
|
||||
)
|
||||
|
||||
#
|
||||
# Build tree package config file
|
||||
#
|
||||
|
||||
configure_file(cmake/in/EnTTBuildConfig.cmake.in EnTTConfig.cmake @ONLY)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
#
|
||||
# Install tree package config file
|
||||
#
|
||||
|
||||
configure_package_config_file(
|
||||
cmake/in/EnTTConfig.cmake.in
|
||||
${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake
|
||||
INSTALL_DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
|
||||
PATH_VARS CMAKE_INSTALL_INCLUDEDIR
|
||||
NO_CHECK_REQUIRED_COMPONENTS_MACRO
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
install(
|
||||
FILES
|
||||
${EnTT_BINARY_DIR}/${CUSTOM_INSTALL_CONFIGDIR}/EnTTConfig.cmake
|
||||
${EnTT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
DESTINATION ${CUSTOM_INSTALL_CONFIGDIR}
|
||||
)
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
|
||||
#
|
||||
# Tests
|
||||
@@ -85,18 +168,25 @@ if(BUILD_TESTING)
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
option(FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
|
||||
|
||||
if(FIND_GTEST_PACKAGE)
|
||||
find_package(GTest REQUIRED)
|
||||
else()
|
||||
# gtest, gtest_main, gmock and gmock_main targets are available from now on
|
||||
set(GOOGLETEST_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/googletest)
|
||||
configure_file(${EnTT_SOURCE_DIR}/cmake/in/googletest.in ${GOOGLETEST_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(${GOOGLETEST_DEPS_DIR}/src ${GOOGLETEST_DEPS_DIR}/build)
|
||||
add_library(GTest::Main ALIAS gtest_main)
|
||||
endif()
|
||||
|
||||
option(BUILD_BENCHMARK "Build benchmark." OFF)
|
||||
option(BUILD_MOD "Build mod example." OFF)
|
||||
option(BUILD_SNAPSHOT "Build snapshot example." OFF)
|
||||
|
||||
# gtest, gtest_main, gmock and gmock_main targets are available from now on
|
||||
set(GOOGLETEST_DEPS_DIR ${entt_SOURCE_DIR}/deps/googletest)
|
||||
configure_file(${entt_SOURCE_DIR}/cmake/in/googletest.in ${GOOGLETEST_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${GOOGLETEST_DEPS_DIR})
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(${GOOGLETEST_DEPS_DIR}/src ${GOOGLETEST_DEPS_DIR}/build)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
@@ -105,10 +195,14 @@ endif()
|
||||
# Documentation
|
||||
#
|
||||
|
||||
find_package(Doxygen 1.8)
|
||||
option(BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
if(BUILD_DOCS)
|
||||
find_package(Doxygen 1.8)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
@@ -120,9 +214,10 @@ add_custom_target(
|
||||
SOURCES
|
||||
appveyor.yml
|
||||
AUTHORS
|
||||
CONTRIBUTING
|
||||
LICENSE
|
||||
README.md
|
||||
TODO
|
||||
.travis.yml
|
||||
docs/CONTRIBUTING.md
|
||||
docs/extra.dox
|
||||
)
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Michele Caini
|
||||
Copyright (c) 2017-2018 Michele Caini
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
568
README.md
568
README.md
@@ -1,8 +1,12 @@
|
||||
# EnTT Framework
|
||||

|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
[](https://travis-ci.org/skypjack/entt)
|
||||
[](https://ci.appveyor.com/project/skypjack/entt)
|
||||
[](https://coveralls.io/github/skypjack/entt?branch=master)
|
||||
[](https://gitter.im/skypjack/entt)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=W2HF9FESD5LJY&lc=IT&item_name=Michele%20Caini¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
|
||||
|
||||
# Table of Contents
|
||||
@@ -29,18 +33,23 @@
|
||||
* [Prototype](#prototype)
|
||||
* [Helpers](#helpers)
|
||||
* [Dependency function](#dependency-function)
|
||||
* [Labels](#labels)
|
||||
* [Null entity](#null-entity)
|
||||
* [View: to persist or not to persist?](#view-to-persist-or-not-to-persist)
|
||||
* [Standard View](#standard-view)
|
||||
* [Single component standard view](#single-component-standard-view)
|
||||
* [Multi component standard view](#multi-component-standard-view)
|
||||
* [Persistent View](#persistent-view)
|
||||
* [Raw View](#raw-view)
|
||||
* [Runtime View](#runtime-view)
|
||||
* [Give me everything](#give-me-everything)
|
||||
* [Side notes](#side-notes)
|
||||
* [Iterations: what is allowed and what is not](#iterations-what-is-allowed-and-what-is-not)
|
||||
* [Multithreading](#multithreading)
|
||||
* [Crash Course: core functionalities](#crash-course-core-functionalities)
|
||||
* [Compile-time identifiers](#compile-time-identifiers)
|
||||
* [Runtime identifiers](#runtime-identifiers)
|
||||
* [Hashed strings](#hashed-strings)
|
||||
* [Monostate](#monostate)
|
||||
* [Crash Course: service locator](#crash-course-service-locator)
|
||||
* [Crash Course: cooperative scheduler](#crash-course-cooperative-scheduler)
|
||||
* [The process](#the-process)
|
||||
@@ -58,13 +67,16 @@
|
||||
* [Support](#support)
|
||||
* [Donation](#donation)
|
||||
* [Hire me](#hire-me)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
`EnTT` is a header-only, tiny and easy to use framework written in modern
|
||||
C++.<br/>
|
||||
It was originally designed entirely around an architectural pattern called _ECS_
|
||||
that is used mostly in game development. For further details:
|
||||
`EnTT` is a header-only, tiny and easy to use entity-component system (and much
|
||||
more) written in modern C++.<br/>
|
||||
The entity-component-system (also known as _ECS_) is an architectural pattern
|
||||
used mostly in game development. For further details:
|
||||
|
||||
* [Entity Systems Wiki](http://entity-systems.wikidot.com/)
|
||||
* [Evolve Your Hierarchy](http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/)
|
||||
@@ -73,35 +85,12 @@ that is used mostly in game development. For further details:
|
||||
A long time ago, the sole entity-component system was part of the project. After
|
||||
a while the codebase has grown and more and more classes have become part of the
|
||||
repository.<br/>
|
||||
That's why today it's called _the EnTT Framework_.
|
||||
|
||||
Currently, `EnTT` is tested on Linux, Microsoft Windows and OS X. It has proven
|
||||
to work also on both Android and iOS.<br/>
|
||||
Most likely it will not be problematic on other systems as well, but has not
|
||||
been sufficiently tested so far.
|
||||
|
||||
## The framework
|
||||
|
||||
`EnTT` was written initially as a faster alternative to other well known and
|
||||
open source entity-component systems. Nowadays the `EnTT` framework is moving
|
||||
its first steps. Much more will come in the future and hopefully I'm going to
|
||||
work on it for a long time.<br/>
|
||||
Requests for feature, PR, suggestions ad feedback are highly appreciated.
|
||||
|
||||
If you find you can help me and want to contribute to the `EnTT` framework with
|
||||
your experience or you do want to get part of the project for some other
|
||||
reasons, feel free to contact me directly (you can find the mail in the
|
||||
[profile](https://github.com/skypjack)).<br/>
|
||||
I can't promise that each and every contribution will be accepted, but I can
|
||||
assure that I'll do my best to take them all seriously.
|
||||
|
||||
### State of the art
|
||||
|
||||
Here is a brief list of what it offers today:
|
||||
Here is a brief, yet incomplete list of what it offers today:
|
||||
|
||||
* Statically generated integer identifiers for types (assigned either at
|
||||
compile-time or at runtime).
|
||||
* A constexpr utility for human readable resource identifiers.
|
||||
* A minimal configuration system built on top of the monostate pattern.
|
||||
* An incredibly fast entity-component system based on sparse sets, with its own
|
||||
views and a _pay for what you use_ policy to adjust performance and memory
|
||||
usage according to users' requirements.
|
||||
@@ -116,12 +105,13 @@ Here is a brief list of what it offers today:
|
||||
* ...
|
||||
* Any other business.
|
||||
|
||||
Consider it a work in progress. For more details and an updated list, please
|
||||
refer to the [online documentation](https://skypjack.github.io/entt/). It
|
||||
probably contains much more. Moreover, the whole API is fully documented in-code
|
||||
for those who are brave enough to read it.<br/>
|
||||
Continue reading to know how the different parts of the project work or follow
|
||||
the link above to take a look at the API reference.
|
||||
Consider it a work in progress. The whole API is also fully documented in-code
|
||||
for those who are brave enough to read it.
|
||||
|
||||
Currently, `EnTT` is tested on Linux, Microsoft Windows and OS X. It has proven
|
||||
to work also on both Android and iOS.<br/>
|
||||
Most likely it will not be problematic on other systems as well, but has not
|
||||
been sufficiently tested so far.
|
||||
|
||||
## Code Example
|
||||
|
||||
@@ -193,7 +183,7 @@ satisfying at all. The fastest and nothing more, fairly little indeed. When I
|
||||
realized it, I tried hard to keep intact the great performance of `EnTT` and to
|
||||
add all the features I wanted to see in *my own library* at the same time.
|
||||
|
||||
Today `EnTT` is finally what I was looking for: still faster than its
|
||||
Nowadays, `EnTT` is finally what I was looking for: still faster than its
|
||||
_competitors_, lower memory usage in the average case, a really good API and an
|
||||
amazing set of features. And even more, of course.
|
||||
|
||||
@@ -208,18 +198,14 @@ Dell XPS 13 out of the mid 2014):
|
||||
|-----------|-------------|-------------|
|
||||
| Create 1M entities | 0.0147s | **0.0046s** |
|
||||
| Destroy 1M entities | 0.0053s | **0.0045s** |
|
||||
| Standard view, 1M entities, one component | 0.0012s | **1.9e-07s** |
|
||||
| Standard view, 1M entities, two components | 0.0012s | **3.8e-07s** |
|
||||
| Standard view, 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
|
||||
| Standard view, 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
|
||||
| Persistent view, 1M entities, two components | 0.0012s | **2.8e-07s** |
|
||||
| Standard view, 1M entities, five components | 0.0010s | **7.0e-07s** |
|
||||
| Persistent view, 1M entities, five components | 0.0010s | **2.8e-07s** |
|
||||
| Standard view, 1M entities, ten components | 0.0011s | **1.2e-06s** |
|
||||
| Standard view, 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
|
||||
| Standard view, 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
|
||||
| Persistent view, 1M entities, ten components | 0.0011s | **3.0e-07s** |
|
||||
| Raw view, 1M entities | - | **2.2e-07s** |
|
||||
| 1M entities, one component | 0.0012s | **1.9e-07s** |
|
||||
| 1M entities, two components | 0.0012s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
|
||||
| 1M entities, five components | 0.0010s | **7.0e-07s** |
|
||||
| 1M entities, ten components | 0.0011s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are in reverse order | - | **0.0036s** |
|
||||
| Sort 150k entities, enforce permutation<br/>Arrays are in reverse order | - | **0.0005s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are almost sorted, std::sort | - | **0.0035s** |
|
||||
@@ -265,8 +251,8 @@ documentation:
|
||||
|
||||
## Library
|
||||
|
||||
`EnTT` is a header-only library. This means that including the `entt.hpp`
|
||||
header is enough to include the whole framework and use it. For those who are
|
||||
`EnTT` is a header-only library. This means that including the `entt.hpp` header
|
||||
is enough to include the library as a whole and use it. For those who are
|
||||
interested only in the entity-component system, consider to include the sole
|
||||
`entity/registry.hpp` header instead.<br/>
|
||||
It's a matter of adding the following line to the top of a file:
|
||||
@@ -284,14 +270,54 @@ Use the line below to include only the entity-component system instead:
|
||||
Then pass the proper `-I` argument to the compiler to add the `src` directory to
|
||||
the include paths.
|
||||
|
||||
### Side note: shared libraries
|
||||
|
||||
To make sure that an application and a shared library that use both `EnTT` can
|
||||
interact correctly when symbols are hidden by default, there are some tricks to
|
||||
follow.<br/>
|
||||
In particular and in order to avoid undefined behaviors, all the instantiation
|
||||
of the `Family` class template shall be made explicit along with the system-wide
|
||||
specifier to use to export them.
|
||||
|
||||
At the time I'm writing this document, the classes that use internally the above
|
||||
mentioned class template are `Dispatcher`, `Emitter` and `Registry`. Therefore
|
||||
and as an example, if you use the `Registry` class template in your shared
|
||||
library and want to set symbols visibility to _hidden_ by default, the following
|
||||
lines are required to allow it to function properly with a client that also uses
|
||||
the `Registry` somehow:
|
||||
|
||||
* On GNU/Linux:
|
||||
|
||||
```cpp
|
||||
namespace entt {
|
||||
template class __attribute__((visibility("default"))) Family<struct InternalRegistryTagFamily>;
|
||||
template class __attribute__((visibility("default"))) Family<struct InternalRegistryComponentFamily>;
|
||||
template class __attribute__((visibility("default"))) Family<struct InternalRegistryHandlerFamily>;
|
||||
}
|
||||
```
|
||||
|
||||
* On Windows:
|
||||
|
||||
```cpp
|
||||
namespace entt {
|
||||
template class __declspec(dllexport) Family<struct InternalRegistryTagFamily>;
|
||||
template class __declspec(dllexport) Family<struct InternalRegistryComponentFamily>;
|
||||
template class __declspec(dllexport) Family<struct InternalRegistryHandlerFamily>;
|
||||
}
|
||||
```
|
||||
|
||||
Otherwise, the risk is that type identifiers are different between the shared
|
||||
library and the application and this will prevent the whole thing from
|
||||
functioning correctly for obvious reasons.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation is based on [doxygen](http://www.stack.nl/~dimitri/doxygen/).
|
||||
To build it:
|
||||
|
||||
$ cd build
|
||||
$ cmake ..
|
||||
$ make docs
|
||||
$ cmake .. -DBUILD_DOCS=ON
|
||||
$ make
|
||||
|
||||
The API reference will be created in HTML format within the directory
|
||||
`build/docs/html`. To navigate it with your favorite browser:
|
||||
@@ -299,13 +325,20 @@ The API reference will be created in HTML format within the directory
|
||||
$ cd build
|
||||
$ your_favorite_browser docs/html/index.html
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
The API reference is also available [online](https://skypjack.github.io/entt/)
|
||||
for the latest version.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
## Tests
|
||||
|
||||
To compile and run the tests, `EnTT` requires *googletest*.<br/>
|
||||
`cmake` will download and compile the library before to compile anything else.
|
||||
`cmake` will download and compile the library before compiling anything else.
|
||||
In order to build without tests set CMake option `BUILD_TESTING=OFF`.
|
||||
|
||||
To build the most basic set of tests:
|
||||
|
||||
@@ -379,8 +412,8 @@ them the way they prefer. No need to register systems or their types neither
|
||||
with the registry nor with the entity-component system at all.
|
||||
|
||||
The following sections will explain in short how to use the entity-component
|
||||
system, the core part of the whole framework.<br/>
|
||||
In fact, the framework is composed of many other classes in addition to those
|
||||
system, the core part of the whole library.<br/>
|
||||
In fact, the project is composed of many other classes in addition to those
|
||||
describe below. For more details, please refer to the inline documentation.
|
||||
|
||||
## The Registry, the Entity and the Component
|
||||
@@ -406,6 +439,17 @@ auto entity = registry.create();
|
||||
registry.destroy(entity);
|
||||
```
|
||||
|
||||
Entities can also be destroyed _by type_, that is by specifying the types of the
|
||||
tags or components that identify them:
|
||||
|
||||
```cpp
|
||||
// destroys the entity that owns the given tag, if any
|
||||
registry.destroy<MyTag>(entt::tag_t{});
|
||||
|
||||
// destroys the entities that own the given components, if any
|
||||
registry.destroy<AComponent, AnotherComponent>();
|
||||
```
|
||||
|
||||
When an entity is destroyed, the registry can freely reuse it internally with a
|
||||
slightly different identifier. In particular, the version of an entity is
|
||||
increased each and every time it's discarded.<br/>
|
||||
@@ -424,19 +468,6 @@ auto version = registry.version(entity);
|
||||
auto curr = registry.current(entity);
|
||||
```
|
||||
|
||||
Finally, there is also a sort of _null identifier_ made available to users. It's
|
||||
treated as if it were a _null pointer_ that doesn't identify any entity. A
|
||||
registry will reject this identifier in all cases because it isn't considered
|
||||
valid.<br/>
|
||||
The rules that define a _null identifier_ are a bit tricky to explain. However,
|
||||
being `Entity` the type of the entities (for example, `std::uint32_t`), users
|
||||
can easily construct a _null identifier_ by flipping all the bits of the _zero_:
|
||||
|
||||
```cpp
|
||||
using Entity = std::uint32_t;
|
||||
const auto null = ~Entity{};
|
||||
```
|
||||
|
||||
Components can be assigned to or removed from entities at any time with a few
|
||||
calls to member functions of the registry. As for the entities, the registry
|
||||
offers also a set of functionalities users can use to work with the components.
|
||||
@@ -713,9 +744,9 @@ created or destroyed.
|
||||
|
||||
### Runtime components
|
||||
|
||||
Defining components at runtime is useful to support plugins and mods in general.
|
||||
However, it seems impossible with a tool designed around a bunch of templates.
|
||||
Indeed it's not that difficult.<br/>
|
||||
Defining components at runtime is useful to support plugin systems and mods in
|
||||
general. However, it seems impossible with a tool designed around a bunch of
|
||||
templates. Indeed it's not that difficult.<br/>
|
||||
Of course, some features cannot be easily exported into a runtime
|
||||
environment. As an example, sorting a group of components defined at runtime
|
||||
isn't for free if compared to most of the other operations. However, the basic
|
||||
@@ -731,10 +762,10 @@ In `EnTT`, identifiers are easily accessible:
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
// standard component identifier
|
||||
auto ctype = registry.component<Position>();
|
||||
auto ctype = registry.type<Position>();
|
||||
|
||||
// single instance component identifier
|
||||
auto ttype = registry.tag<PlayingCharacter>();
|
||||
auto ttype = registry.type<PlayingCharacter>(entt::tag_t{});
|
||||
```
|
||||
|
||||
Once the identifiers are made available, almost everything becomes pretty
|
||||
@@ -1135,6 +1166,48 @@ A dependency can easily be broken by means of the same function template:
|
||||
entt::dependency<AType, AnotherType>(entt::break_t{}, registry.construction<MyType>());
|
||||
```
|
||||
|
||||
#### Labels
|
||||
|
||||
There's nothing magical about the way labels can be assigned to entities while
|
||||
avoiding a performance hit at runtime. Nonetheless, the syntax can be annoying
|
||||
and that's why a more user-friendly shortcut is provided to do it.<br/>
|
||||
This shortcut is the alias template `entt::label`.
|
||||
|
||||
If used in combination with hashed strings, it helps to use labels where types
|
||||
would be required otherwise. As an example:
|
||||
|
||||
```cpp
|
||||
registry.assign<entt::label<"enemy"_hs>>(entity);
|
||||
```
|
||||
|
||||
### Null entity
|
||||
|
||||
In `EnTT`, there exists a sort of _null entity_ made available to users that is
|
||||
accessible via the `entt::null` variable.<br/>
|
||||
The library guarantees that the following expression always returns false:
|
||||
|
||||
```cpp
|
||||
registry.valid(entt::null);
|
||||
```
|
||||
|
||||
In other terms, a registry will reject the null entity in all cases because it
|
||||
isn't considered valid. It means that the null entity cannot own components or
|
||||
tags for obvious reasons.<br/>
|
||||
The type of the null entity is internal and should not be used for any purpose
|
||||
other than defining the null entity itself. However, there exist implicit
|
||||
conversions from the null entity to identifiers of any allowed type:
|
||||
|
||||
```cpp
|
||||
typename entt::DefaultRegistry::entity_type null = entt::null;
|
||||
```
|
||||
|
||||
Similarly, the null entity can be compared to any other identifier:
|
||||
|
||||
```cpp
|
||||
const auto entity = registry.create();
|
||||
const bool null = (entity == entt::null);
|
||||
```
|
||||
|
||||
## View: to persist or not to persist?
|
||||
|
||||
First of all, it is worth answering an obvious question: why views?<br/>
|
||||
@@ -1145,9 +1218,9 @@ view can only iterate entities and their components, then read or update the
|
||||
data members of the latter.<br/>
|
||||
It is a subtle difference that can help designing a better software sometimes.
|
||||
|
||||
There are mainly three kinds of views: standard (also known as `View`),
|
||||
persistent (also known as `PersistentView`) and raw (also known as
|
||||
`RawView`).<br/>
|
||||
There are mainly four kinds of views: standard (also known as `View`),
|
||||
persistent (also known as `PersistentView`), raw (also known as `RawView`) and
|
||||
runtime (also known as `RuntimeView`).<br/>
|
||||
All of them have pros and cons to take in consideration. In particular:
|
||||
|
||||
* Standard views:
|
||||
@@ -1202,6 +1275,21 @@ All of them have pros and cons to take in consideration. In particular:
|
||||
* They can be used to iterate only one type of component at a time.
|
||||
* They don't return the entity to which a component belongs to the caller.
|
||||
|
||||
* Runtime views:
|
||||
|
||||
Pros:
|
||||
|
||||
* Their lists of components are defined at runtime and not at compile-time.
|
||||
* Creating and destroying them isn't expensive at all because they don't have
|
||||
any type of initialization.
|
||||
* They are the best tool for things like plugin systems and mods in general.
|
||||
* They don't affect any other operations of the registry.
|
||||
|
||||
Cons:
|
||||
|
||||
* Their performances are definitely lower than those of all the other views,
|
||||
although they are still usable and sufficient for most of the purposes.
|
||||
|
||||
To sum up and as a rule of thumb:
|
||||
|
||||
* Use a raw view to iterate components only (no entities) for a given type.
|
||||
@@ -1220,6 +1308,8 @@ To sum up and as a rule of thumb:
|
||||
entities but the intersection between the sets of entities is small.
|
||||
* Prepare and use a persistent view in all the cases where a standard view
|
||||
wouldn't fit well otherwise.
|
||||
* Finally, in case you don't know at compile-time what are the components to
|
||||
use, choose a runtime view and set them during execution.
|
||||
|
||||
To easily iterate entities and components, all the views offer the common
|
||||
`begin` and `end` member functions that allow users to use a view in a typical
|
||||
@@ -1258,7 +1348,7 @@ There is no need to store views around for they are extremely cheap to
|
||||
construct, even though they can be copied without problems and reused freely. In
|
||||
fact, they return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` are invoked.<br/>
|
||||
To iterate a single component standard view, either use it in range-for loop:
|
||||
To iterate a single component standard view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Renderable>();
|
||||
@@ -1279,8 +1369,8 @@ registry.view<Renderable>().each([](auto entity, auto &renderable) {
|
||||
});
|
||||
```
|
||||
|
||||
Performance are more or less the same. The best approach depends mainly on
|
||||
whether all the components have to be accessed or not.
|
||||
The `each` member function is highly optimized. Unless users want to iterate
|
||||
only entities, using `each` should be the preferred approach.
|
||||
|
||||
**Note**: prefer the `get` member function of a view instead of the `get` member
|
||||
function template of a registry during iterations, if possible. However, keep in
|
||||
@@ -1303,7 +1393,7 @@ There is no need to store views around for they are extremely cheap to
|
||||
construct, even though they can be copied without problems and reused freely. In
|
||||
fact, they return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` are invoked.<br/>
|
||||
To iterate a multi component standard view, either use it in range-for loop:
|
||||
To iterate a multi component standard view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Position, Velocity>();
|
||||
@@ -1329,8 +1419,9 @@ registry.view<Position, Velocity>().each([](auto entity, auto &position, auto &v
|
||||
});
|
||||
```
|
||||
|
||||
Performance are more or less the same. The best approach depends mainly on
|
||||
whether all the components have to be accessed or not.
|
||||
The `each` member function is highly optimized. Unless users want to iterate
|
||||
only entities or get only some of the components, using `each` should be the
|
||||
preferred approach.
|
||||
|
||||
**Note**: prefer the `get` member function of a view instead of the `get` member
|
||||
function template of a registry during iterations, if possible. However, keep in
|
||||
@@ -1376,7 +1467,7 @@ of the components for which it has been constructed. It's also possible to ask a
|
||||
view if it contains a given entity.<br/>
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
To iterate a persistent view, either use it in range-for loop:
|
||||
To iterate a persistent view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Position, Velocity>(entt::persistent_t{});
|
||||
@@ -1431,7 +1522,7 @@ There is no need to store views around for they are extremely cheap to
|
||||
construct, even though they can be copied without problems and reused freely. In
|
||||
fact, they return newly created and correctly initialized iterators whenever
|
||||
`begin` or `end` are invoked.<br/>
|
||||
To iterate a raw view, use it in range-for loop:
|
||||
To iterate a raw view, use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<Renderable>(entt::raw_t{});
|
||||
@@ -1441,9 +1532,74 @@ for(auto &&component: raw) {
|
||||
}
|
||||
```
|
||||
|
||||
**Note**: raw views don't have the `each` nor the `get` member function for
|
||||
obvious reasons. The former would only return the components and therefore it
|
||||
would be redundant, the latter isn't required at all.
|
||||
Or rely on the `each` member function:
|
||||
|
||||
```cpp
|
||||
registry.view<Renderable>(entt::raw_t{}).each([](auto &renderable) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Performance are exactly the same in both cases.
|
||||
|
||||
**Note**: raw views don't have a `get` member function for obvious reasons.
|
||||
|
||||
### Runtime View
|
||||
|
||||
Runtime views iterate entities that have at least all the given components in
|
||||
their bags. During construction, these views look at the number of entities
|
||||
available for each component and pick up a reference to the smallest
|
||||
set of candidates in order to speed up iterations.<br/>
|
||||
They offer more or less the same functionalities of a multi component standard
|
||||
view. However, they don't expose a `get` member function and users should refer
|
||||
to the registry that generated the view to access components. In particular, a
|
||||
runtime view exposes utility functions to get the estimated number of entities
|
||||
it is going to return and to know whether it's empty or not. It's also possible
|
||||
to ask a view if it contains a given entity.<br/>
|
||||
Refer to the inline documentation for all the details.
|
||||
|
||||
Runtime view are extremely cheap to construct and should not be stored around in
|
||||
any case. They should be used immediately after creation and then they should be
|
||||
thrown away. The reasons for this go far beyond the scope of this document.<br/>
|
||||
To iterate a runtime view, either use it in a range-for loop:
|
||||
|
||||
```cpp
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
auto view = registry.view(std::cbegin(types), std::cend(types));
|
||||
|
||||
for(auto entity: view) {
|
||||
// a component at a time ...
|
||||
Position &position = registry.get<Position>(entity);
|
||||
Velocity &velocity = registry.get<Velocity>(entity);
|
||||
|
||||
// ... or multiple components at once
|
||||
std::tuple<Position &, Velocity &> tup = view.get<Position, Velocity>(entity);
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Or rely on the `each` member function to iterate entities:
|
||||
|
||||
```cpp
|
||||
using component_type = typename decltype(registry)::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
auto view = registry.view(std::cbegin(types), std::cend(types)).each([](auto entity) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
Performance are exactly the same in both cases.
|
||||
|
||||
**Note**: runtime views are meant for all those cases where users don't know at
|
||||
compile-time what components to use to iterate entities. This is particularly
|
||||
well suited to plugin systems and mods in general. Where possible, don't use
|
||||
runtime views, as their performance are slightly inferior to those of the other
|
||||
views.
|
||||
|
||||
|
||||
### Give me everything
|
||||
|
||||
@@ -1486,70 +1642,72 @@ In general, all these functions can result in poor performance.<br/>
|
||||
entity. For similar reasons, `orphans` can be even slower. Both functions should
|
||||
not be used frequently to avoid the risk of a performance hit.
|
||||
|
||||
## Side notes
|
||||
## Iterations: what is allowed and what is not
|
||||
|
||||
* Entity identifiers are numbers and nothing more. They are not classes and they
|
||||
have no member functions at all. As already mentioned, do no try to inspect or
|
||||
modify an entity descriptor in any way.
|
||||
Most of the _ECS_ available out there have some annoying limitations (at least
|
||||
from my point of view): entities and components cannot be created nor destroyed
|
||||
during iterations.<br/>
|
||||
`EnTT` partially solves the problem with a few limitations:
|
||||
|
||||
* As shown in the examples above, the preferred way to get references to the
|
||||
components while iterating a view is by using the view itself. It's a faster
|
||||
alternative to the `get` member function template that is part of the API of
|
||||
the `Registry`. This is because the registry must ensure that a pool for the
|
||||
given component exists before to use it; on the other side, views force the
|
||||
construction of the pools for all their components and access them directly,
|
||||
thus avoiding all the checks.
|
||||
* Creating entities and components is allowed during iterations.
|
||||
* Deleting an entity or removing its components is allowed during iterations if
|
||||
it's the one currently returned by the view. For all the other entities,
|
||||
destroying them or removing their components isn't allowed and it can result
|
||||
in undefined behavior.
|
||||
|
||||
* Most of the _ECS_ available out there have some annoying limitations (at least
|
||||
from my point of view): entities and components cannot be created and/or
|
||||
destroyed during iterations.<br/>
|
||||
`EnTT` partially solves the problem with a few limitations:
|
||||
Iterators are invalidated and the behavior is undefined if an entity is modified
|
||||
or destroyed and it's not the one currently returned by the view nor a newly
|
||||
created one.<br/>
|
||||
To work around it, possible approaches are:
|
||||
|
||||
* Creating entities and components is allowed during iterations.
|
||||
* Deleting an entity or removing its components is allowed during iterations
|
||||
if it's the one currently returned by a view. For all the other entities,
|
||||
destroying them or removing their components isn't allowed and it can result
|
||||
in undefined behavior.
|
||||
* Store aside the entities and the components to be removed and perform the
|
||||
operations at the end of the iteration.
|
||||
* Mark entities and components with a proper tag component that indicates they
|
||||
must be purged, then perform a second iteration to clean them up one by one.
|
||||
|
||||
Iterators are invalidated and the behavior is undefined if an entity is
|
||||
modified or destroyed and it's not the one currently returned by the
|
||||
view.<br/>
|
||||
To work around it, possible approaches are:
|
||||
A notable side effect of this feature is that the number of required allocations
|
||||
is further reduced in most of the cases.
|
||||
|
||||
* Store aside the entities and the components to be removed and perform the
|
||||
operations at the end of the iteration.
|
||||
* Mark entities and components with a proper tag component that indicates
|
||||
they must be purged, then perform a second iteration to clean them up one
|
||||
by one.
|
||||
## Multithreading
|
||||
|
||||
* Views and thus their iterators aren't thread safe. Do no try to iterate a set
|
||||
of components and modify the same set concurrently.<br/>
|
||||
That being said, as long as a thread iterates the entities that have the
|
||||
component `X` or assign and removes that component from a set of entities,
|
||||
another thread can safely do the same with components `Y` and `Z` and
|
||||
everything will work like a charm.<br/>
|
||||
As a trivial example, users can freely execute the rendering system and
|
||||
In general, the entire registry isn't thread safe as it is. Thread safety isn't
|
||||
something that users should want out of the box for several reasons. Just to
|
||||
mention one of them: performance.<br/>
|
||||
Views and consequently the approach adopted by `EnTT` are the great exception to
|
||||
the rule. It's true that views and thus their iterators aren't thread safe by
|
||||
themselves. Because of this users shouldn't try to iterate a set of components
|
||||
and modify the same set concurrently. However:
|
||||
|
||||
* As long as a thread iterates the entities that have the component `X` or
|
||||
assign and removes that component from a set of entities, another thread can
|
||||
safely do the same with components `Y` and `Z` and everything will work like a
|
||||
charm. As a trivial example, users can freely execute the rendering system and
|
||||
iterate the renderable entities while updating a physic component concurrently
|
||||
on a separate thread.
|
||||
|
||||
* In general, the entire registry isn't thread safe as it is. Thread safety
|
||||
isn't something that users should want out of the box for several reasons.
|
||||
Just to mention one of them: performance.<br/>
|
||||
This kind of entity-component systems can be used in single threaded
|
||||
applications as well as along with async stuff. Moreover, typical thread based
|
||||
models for ECS don't require a fully thread safe registry to work. Actually
|
||||
one could reach the goal with the registry as it is while working with most of
|
||||
the common models, after all.<br/>
|
||||
Because of the few reasons mentioned above and many others not mentioned,
|
||||
users are completely responsible for synchronization whether required.
|
||||
* Similarly, a single set of components can be iterated by multiple threads as
|
||||
long as the components are neither assigned nor removed in the meantime. In
|
||||
other words, a hypothetical movement system can start multiple threads, each
|
||||
of which will access the components that carry information about velocity and
|
||||
position for its entities.
|
||||
|
||||
This kind of entity-component systems can be used in single threaded
|
||||
applications as well as along with async stuff or multiple threads. Moreover,
|
||||
typical thread based models for _ECS_ don't require a fully thread safe registry
|
||||
to work. Actually, users can reach the goal with the registry as it is while
|
||||
working with most of the common models.
|
||||
|
||||
Because of the few reasons mentioned above and many others not mentioned, users
|
||||
are completely responsible for synchronization whether required. On the other
|
||||
hand, they could get away with it without having to resort to particular
|
||||
expedients.
|
||||
|
||||
# Crash Course: core functionalities
|
||||
|
||||
The `EnTT` framework comes with a bunch of core functionalities mostly used by
|
||||
the other parts of the library itself.<br/>
|
||||
Hardly users of the framework will include these features in their code, but
|
||||
it's worth describing what `EnTT` offers so as not to reinvent the wheel in case
|
||||
of need.
|
||||
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
|
||||
of the library itself.<br/>
|
||||
Hardly users will include these features in their code, but it's worth
|
||||
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
|
||||
|
||||
## Compile-time identifiers
|
||||
|
||||
@@ -1559,21 +1717,21 @@ There are plenty of different solutions out there and I could have used one of
|
||||
them. However, I decided to spend my time to define a compact and versatile tool
|
||||
that fully embraces what the modern C++ has to offer.
|
||||
|
||||
The _result of my efforts_ is the `ident` `constexpr` variable:
|
||||
The _result of my efforts_ is the `Identifier` class template:
|
||||
|
||||
```cpp
|
||||
#include <ident.hpp>
|
||||
|
||||
// defines the identifiers for the given types
|
||||
constexpr auto identifiers = entt::ident<AType, AnotherType>;
|
||||
using ID = entt::Identifier<AType, AnotherType>;
|
||||
|
||||
// ...
|
||||
|
||||
switch(aTypeIdentifier) {
|
||||
case identifers.get<AType>():
|
||||
case ID::get<AType>():
|
||||
// ...
|
||||
break;
|
||||
case identifers.get<AnotherType>():
|
||||
case ID::get<AnotherType>():
|
||||
// ...
|
||||
break;
|
||||
default:
|
||||
@@ -1581,9 +1739,9 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
This is all what the variable has to offer: a `get` member function that returns
|
||||
a numerical identifier for the given type. It can be used in any context where
|
||||
constant expressions are required.
|
||||
This is all what the class template has to offer: a static `get` member function
|
||||
that returns a numerical identifier for the given type. It can be used in any
|
||||
context where constant expressions are required.
|
||||
|
||||
As long as the list remains unchanged, identifiers are also guaranteed to be the
|
||||
same for every run. In case they have been used in a production environment and
|
||||
@@ -1593,7 +1751,7 @@ identifiers unchanged:
|
||||
```cpp
|
||||
template<typename> struct IgnoreType {};
|
||||
|
||||
constexpr auto identifiers = entt::ident<
|
||||
using ID = entt::Identifier<
|
||||
ATypeStillValid,
|
||||
IgnoreType<ATypeNoLongerValid>,
|
||||
AnotherTypeStillValid
|
||||
@@ -1608,7 +1766,7 @@ Sometimes it's useful to be able to give unique identifiers to types at
|
||||
runtime.<br/>
|
||||
There are plenty of different solutions out there and I could have used one of
|
||||
them. In fact, I adapted the most common one to my requirements and used it
|
||||
extensively within the entire framework.
|
||||
extensively within the entire library.
|
||||
|
||||
It's the `Family` class. Here is an example of use directly from the
|
||||
entity-component system:
|
||||
@@ -1652,6 +1810,13 @@ auto load(entt::HashedString::hash_type resource) {
|
||||
auto resource = load(entt::HashedString{"gui/background"});
|
||||
```
|
||||
|
||||
There is also a _user defined literal_ dedicated to hashed strings to make them
|
||||
more user-friendly:
|
||||
|
||||
```cpp
|
||||
constexpr auto str = "text"_hs;
|
||||
```
|
||||
|
||||
### Conflicts
|
||||
|
||||
The hashed string class uses internally FNV-1a to compute the numeric
|
||||
@@ -1665,6 +1830,29 @@ and over which users have not the control. Choosing a slightly different
|
||||
identifier is probably the best solution to make the conflict disappear in this
|
||||
case.
|
||||
|
||||
## Monostate
|
||||
|
||||
The monostate pattern is often presented as an alternative to a singleton based
|
||||
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
|
||||
implementation is thread safe by design (hopefully).<br/>
|
||||
Keys are represented by hashed strings, values are basic types like `int`s or
|
||||
`bool`s. Values of different types can be associated to each key, even more than
|
||||
one at a time. Because of this, users must pay attention to use the same type
|
||||
both during an assignment and when they try to read back their data. Otherwise,
|
||||
they will probably incur in unexpected results.
|
||||
|
||||
Example of use:
|
||||
|
||||
```cpp
|
||||
entt::Monostate<entt::HashedString{"mykey"}>{} = true;
|
||||
entt::Monostate<"mykey"_hs>{} = 42;
|
||||
|
||||
// ...
|
||||
|
||||
const bool b = entt::Monostate<"mykey"_hs>{};
|
||||
const int i = entt::Monostate<entt::HashedString{"mykey"}>{};
|
||||
```
|
||||
|
||||
# Crash Course: service locator
|
||||
|
||||
Usually service locators are tightly bound to the services they expose and it's
|
||||
@@ -1732,8 +1920,8 @@ Sometimes processes are a useful tool to work around the strict definition of a
|
||||
system and introduce logic in a different way, usually without resorting to the
|
||||
introduction of other components.
|
||||
|
||||
The `EnTT` framework offers a minimal support to this paradigm by introducing a
|
||||
few classes that users can use to define and execute cooperative processes.
|
||||
`EnTT` offers a minimal support to this paradigm by introducing a few classes
|
||||
that users can use to define and execute cooperative processes.
|
||||
|
||||
## The process
|
||||
|
||||
@@ -1858,7 +2046,7 @@ It has member functions to query its internal data structures, like `empty` or
|
||||
|
||||
```cpp
|
||||
// checks if there are processes still running
|
||||
bool empty = scheduler.empty();
|
||||
const auto empty = scheduler.empty();
|
||||
|
||||
// gets the number of processes still running
|
||||
Scheduler<std::uint32_t>::size_type size = scheduler.size();
|
||||
@@ -1932,9 +2120,9 @@ requirements of the piece of software in which they are used.<br/>
|
||||
Examples are loading everything on start, loading on request, predictive
|
||||
loading, and so on.
|
||||
|
||||
The `EnTT` framework doesn't pretend to offer a _one-fits-all_ solution for the
|
||||
different cases. Instead, it offers a minimal and perhaps trivial cache that can
|
||||
be useful most of the time during prototyping and sometimes even in a production
|
||||
`EnTT` doesn't pretend to offer a _one-fits-all_ solution for the different
|
||||
cases. Instead, it offers a minimal and perhaps trivial cache that can be useful
|
||||
most of the time during prototyping and sometimes even in a production
|
||||
environment.<br/>
|
||||
For those interested in the subject, the plan is to improve it considerably over
|
||||
time in terms of performance, memory usage and functionalities. Hoping to make
|
||||
@@ -2006,10 +2194,10 @@ _organize_ it:
|
||||
|
||||
```cpp
|
||||
// gets the number of resources managed by a cache
|
||||
auto size = cache.size();
|
||||
const auto size = cache.size();
|
||||
|
||||
// checks if a cache contains at least a valid resource
|
||||
auto empty = cache.empty();
|
||||
const auto empty = cache.empty();
|
||||
|
||||
// clears a cache and discards its content
|
||||
cache.clear();
|
||||
@@ -2166,8 +2354,8 @@ it solves a problem but may not adapt well to other requirements that may arise
|
||||
from time to time.
|
||||
|
||||
In case that the flexibility and potential of an `std::function` are not
|
||||
required or where you are looking for something different, the `EnTT` framework
|
||||
offers a full set of classes to solve completely different problems.
|
||||
required or where you are looking for something different, `EnTT` offers a full
|
||||
set of classes to solve completely different problems.
|
||||
|
||||
## Signals
|
||||
|
||||
@@ -2205,7 +2393,7 @@ listeners they contain (`size`) or if they contain at least a listener (`empty`)
|
||||
and even to swap two signal handlers (`swap`).
|
||||
|
||||
Besides them, there are member functions to use both to connect and disconnect
|
||||
listeners in all their forms by means of a sink::
|
||||
listeners in all their forms by means of a sink:
|
||||
|
||||
```cpp
|
||||
void foo(int, char) { /* ... */ }
|
||||
@@ -2321,6 +2509,12 @@ delegate.connect<MyStruct, &MyStruct::f>(&instance);
|
||||
|
||||
It hasn't a `disconnect` counterpart. Instead, there exists a `reset` member
|
||||
function to clear it.<br/>
|
||||
The `empty` member function can be used to know if a delegate is empty:
|
||||
|
||||
```cpp
|
||||
const auto empty = delegate.empty();
|
||||
```
|
||||
|
||||
Finally, to invoke a delegate, the function call operator is the way to go as
|
||||
usual:
|
||||
|
||||
@@ -2330,7 +2524,7 @@ auto ret = delegate(42);
|
||||
|
||||
Probably too much small and pretty poor of functionalities, but the delegate
|
||||
class can help in a lot of cases and it has shown that it is worth keeping it
|
||||
within the framework.
|
||||
within the library.
|
||||
|
||||
## Event dispatcher
|
||||
|
||||
@@ -2567,9 +2761,13 @@ projects based on EnTT and did not hold back when it came to documenting them.
|
||||
|
||||
Below an incomplete list of projects and articles:
|
||||
|
||||
* [EnttPong](https://github.com/reworks/EnttPong): example game for `EnTT`
|
||||
framework.
|
||||
* [ECS_SpaceBattle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
|
||||
* [Minecraft](https://minecraft.net/en-us/attribution/): of course, **that** Minecraft, by Mojang (see the open source attributions page).
|
||||
* [Face Smash](https://play.google.com/store/apps/details?id=com.gamee.facesmash):
|
||||
the emojis dominate the world, destroy them all with your facial expressions.
|
||||
* [EnttPong](https://github.com/reworks/EnttPong): example game with `EnTT`.
|
||||
* [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html):
|
||||
huge space battle built entirely from scratch.
|
||||
* [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space
|
||||
battle built on `UE4`.
|
||||
* [Experimenting with ECS in UE4](http://victor.madtriangles.com/code%20experiment/2018/03/25/post-ue4-ecs-battle.html):
|
||||
interesting article about `UE4` and `EnTT`.
|
||||
@@ -2584,30 +2782,55 @@ Below an incomplete list of projects and articles:
|
||||
If you know of other resources out there that are about `EnTT`, feel free to
|
||||
open an issue or a PR and I'll be glad to add them to the list.
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Contributors
|
||||
|
||||
If you want to participate, please see the guidelines for
|
||||
[contributing](https://github.com/skypjack/entt/blob/master/CONTRIBUTING)
|
||||
before to create issues or pull requests.<br/>
|
||||
`EnTT` was written initially as a faster alternative to other well known and
|
||||
open source entity-component systems. Nowadays this library is moving its first
|
||||
steps. Much more will come in the future and hopefully I'm going to work on it
|
||||
for a long time.<br/>
|
||||
Requests for features, PR, suggestions ad feedback are highly appreciated.
|
||||
|
||||
If you find you can help me and want to contribute to the project with your
|
||||
experience or you do want to get part of the project for some other reasons,
|
||||
feel free to contact me directly (you can find the mail in the
|
||||
[profile](https://github.com/skypjack)).<br/>
|
||||
I can't promise that each and every contribution will be accepted, but I can
|
||||
assure that I'll do my best to take them all seriously.
|
||||
|
||||
If you decide to participate, please see the guidelines for
|
||||
[contributing](docs/CONTRIBUTING.md) before to create issues or pull requests.<br/>
|
||||
Take also a look at the
|
||||
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
|
||||
know who has participated so far.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2018 Michele Caini.<br/>
|
||||
Code and documentation Copyright (c) 2017-2018 Michele Caini.<br/>
|
||||
Logo Copyright (c) 2018 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
[the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).
|
||||
Docs released under
|
||||
[Creative Commons](https://github.com/skypjack/entt/blob/master/docs/LICENSE).
|
||||
Documentation released under
|
||||
[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).<br/>
|
||||
Logo released under
|
||||
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Support
|
||||
|
||||
## Donation
|
||||
|
||||
Developing and maintaining `EnTT` takes some time and lots of coffee. I'd like
|
||||
to add more and more functionalities in future and turn it in a full-featured
|
||||
framework.<br/>
|
||||
solution.<br/>
|
||||
If you want to support this project, you can offer me an espresso. I'm from
|
||||
Italy, we're used to turning the best coffee ever in code. If you find that
|
||||
it's not enough, feel free to support me the way you prefer.<br/>
|
||||
@@ -2621,3 +2844,6 @@ to give it the highest priority, if you have any other reason to contact me:
|
||||
do not hesitate. I'm available for hiring.<br/>
|
||||
Feel free to take a look at my [profile](https://github.com/skypjack) and
|
||||
contact me by mail.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
12
TODO
12
TODO
@@ -2,12 +2,14 @@
|
||||
* scene management (I prefer the concept of spaces, that is a kind of scene anyway)
|
||||
* review doc: separate it in multiple md/dox files, reduce the readme to a minimum and provide users with links to the online documentation on gh-pages
|
||||
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
|
||||
* dynamic view, useful for runtime ecs, can be filled with the desired pool at runtime and are not constrained to a compile-time list of components
|
||||
* define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on)
|
||||
* define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc)
|
||||
* create dedicated flat map based on types implementation (sort of "type map") for types to use within the registry and so on...
|
||||
* ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene)
|
||||
* improve CMake interface, see mail from Malte
|
||||
* is registry/utility.hpp really required?
|
||||
* "singleton mode" for tags (see #66)
|
||||
* registry::create with a "hint" on the entity identifier to use, it should ease combining multiple registries
|
||||
* optimize for empty components, it would be a mid improvement in terms of memory usage
|
||||
* add some lazy iterative sorters like "single bubble sort loop"
|
||||
* can we do more for shared libraries? who knows... see #144
|
||||
* work stealing job system (see #100)
|
||||
* reflection system (maybe)
|
||||
* C++17. That's all.
|
||||
* AOB
|
||||
|
||||
6
cmake/in/EnTTBuildConfig.cmake.in
Normal file
6
cmake/in/EnTTBuildConfig.cmake.in
Normal file
@@ -0,0 +1,6 @@
|
||||
set(ENTT_VERSION "@PROJECT_VERSION@")
|
||||
set(ENTT_INCLUDE_DIRS "@CMAKE_CURRENT_SOURCE_DIR@/src")
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS "3.0")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
endif()
|
||||
11
cmake/in/EnTTConfig.cmake.in
Normal file
11
cmake/in/EnTTConfig.cmake.in
Normal file
@@ -0,0 +1,11 @@
|
||||
set(ENTT_VERSION "@PROJECT_VERSION@")
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
set_and_check(ENTT_INCLUDE_DIRS "@PACKAGE_CMAKE_INSTALL_INCLUDEDIR@")
|
||||
|
||||
if(NOT CMAKE_VERSION VERSION_LESS "3.0")
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||
endif()
|
||||
|
||||
check_required_components("@PROJECT_NAME@")
|
||||
@@ -6,7 +6,7 @@ include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG release-1.8.0
|
||||
GIT_TAG master
|
||||
DOWNLOAD_DIR ${GOOGLETEST_DEPS_DIR}
|
||||
TMP_DIR ${GOOGLETEST_DEPS_DIR}/tmp
|
||||
STAMP_DIR ${GOOGLETEST_DEPS_DIR}/stamp
|
||||
|
||||
@@ -2,18 +2,16 @@
|
||||
# Doxygen configuration (documentation)
|
||||
#
|
||||
|
||||
set(TARGET_DOCS docs)
|
||||
|
||||
set(DOXY_SOURCE_DIRECTORY ${entt_SOURCE_DIR}/src)
|
||||
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
|
||||
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
configure_file(doxy.in doxy.cfg @ONLY)
|
||||
|
||||
add_custom_target(
|
||||
${TARGET_DOCS}
|
||||
docs ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
|
||||
WORKING_DIRECTORY ${entt_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
SOURCES doxy.in
|
||||
)
|
||||
|
||||
395
docs/LICENSE
395
docs/LICENSE
@@ -1,395 +0,0 @@
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More_considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
60
scripts/update_homebrew.sh
Executable file
60
scripts/update_homebrew.sh
Executable file
@@ -0,0 +1,60 @@
|
||||
#!/bin/sh
|
||||
|
||||
# only argument should be the version to upgrade to
|
||||
if [ $# != 1 ]
|
||||
then
|
||||
echo "Expected a version tag like v2.7.1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="$1"
|
||||
URL="https://github.com/skypjack/entt/archive/$VERSION.tar.gz"
|
||||
FORMULA="entt.rb"
|
||||
|
||||
echo "Updating homebrew package to $VERSION"
|
||||
|
||||
echo "Cloning..."
|
||||
git clone https://github.com/skypjack/homebrew-entt.git
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
cd homebrew-entt
|
||||
|
||||
# download the repo at the version
|
||||
# exit with error messages if curl fails
|
||||
echo "Curling..."
|
||||
curl "$URL" --location --fail --silent --show-error --output archive.tar.gz
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# compute sha256 hash
|
||||
echo "Hashing..."
|
||||
HASH="$(openssl sha256 archive.tar.gz | cut -d " " -f 2)"
|
||||
|
||||
# delete the archive
|
||||
rm archive.tar.gz
|
||||
|
||||
echo "Sedding..."
|
||||
|
||||
# change the url in the formula file
|
||||
# the slashes in the URL must be escaped
|
||||
ESCAPED_URL="$(sed -e 's/[\/&]/\\&/g' <<< "$URL")"
|
||||
sed -i -e '/url/s/".*"/"'$ESCAPED_URL'"/' $FORMULA
|
||||
|
||||
# change the hash in the formula file
|
||||
sed -i -e '/sha256/s/".*"/"'$HASH'"/' $FORMULA
|
||||
|
||||
# delete temporary file created by sed
|
||||
rm "$FORMULA-e"
|
||||
|
||||
# update remote repo
|
||||
echo "Gitting..."
|
||||
git add entt.rb
|
||||
git commit -m "Update to $VERSION"
|
||||
git push origin master
|
||||
|
||||
# out of homebrew-entt dir
|
||||
cd ..
|
||||
3
scripts/update_packages.sh
Executable file
3
scripts/update_packages.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
scripts/update_homebrew.sh $1
|
||||
@@ -4,7 +4,13 @@
|
||||
|
||||
#ifndef ENTT_NOEXCEPT
|
||||
#define ENTT_NOEXCEPT noexcept
|
||||
#endif
|
||||
#endif // ENTT_NOEXCEPT
|
||||
|
||||
|
||||
#ifndef ENTT_HS_SUFFIX
|
||||
#define ENTT_HS_SUFFIX _hs
|
||||
#endif // ENTT_HS_SUFFIX
|
||||
|
||||
|
||||
|
||||
#endif // ENTT_CONFIG_CONFIG_H
|
||||
|
||||
@@ -18,31 +18,33 @@ namespace entt {
|
||||
* This class fills the gap by wrapping some flavors of `std::sort` in a
|
||||
* function object.
|
||||
*/
|
||||
struct StdSort {
|
||||
struct StdSort final {
|
||||
/**
|
||||
* @brief Sorts the element in a range.
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the element in a range using the given binary comparison function.
|
||||
* Sorts the elements in a range using the given binary comparison function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param args Arguments to forward to the sort function, if any.
|
||||
*/
|
||||
template<typename It, typename Compare = std::less<>>
|
||||
void operator()(It first, It last, Compare compare = Compare{}) const {
|
||||
std::sort(std::move(first), std::move(last), std::move(compare));
|
||||
template<typename It, typename Compare = std::less<>, typename... Args>
|
||||
void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const {
|
||||
std::sort(std::forward<Args>(args)..., std::move(first), std::move(last), std::move(compare));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Function object for performing insertion sort. */
|
||||
struct InsertionSort {
|
||||
struct InsertionSort final {
|
||||
/**
|
||||
* @brief Sorts the element in a range.
|
||||
* @brief Sorts the elements in a range.
|
||||
*
|
||||
* Sorts the element in a range using the given binary comparison function.
|
||||
* Sorts the elements in a range using the given binary comparison function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
@@ -70,6 +72,39 @@ struct InsertionSort {
|
||||
};
|
||||
|
||||
|
||||
/*! @brief Function object for performing bubble sort (single iteration). */
|
||||
struct OneShotBubbleSort final {
|
||||
/**
|
||||
* @brief Tries to sort the elements in a range.
|
||||
*
|
||||
* Performs a single iteration to sort the elements in a range using the
|
||||
* given binary comparison function. The range may not be completely sorted
|
||||
* after running this function.
|
||||
*
|
||||
* @tparam It Type of random access iterator.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param compare A valid comparison function object.
|
||||
*/
|
||||
template<typename It, typename Compare = std::less<>>
|
||||
void operator()(It first, It last, Compare compare = Compare{}) const {
|
||||
if(first != last) {
|
||||
auto it = first++;
|
||||
|
||||
while(first != last) {
|
||||
if(compare(*first, *it)) {
|
||||
using std::swap;
|
||||
std::swap(*first, *it);
|
||||
}
|
||||
|
||||
it = first++;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -108,4 +108,14 @@ constexpr bool operator!=(const HashedString &lhs, const HashedString &rhs) ENTT
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief User defined literal for hashed strings.
|
||||
* @param str The literal without its suffix.
|
||||
* @return A properly initialized hashed string.
|
||||
*/
|
||||
constexpr entt::HashedString operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return entt::HashedString{str};
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_HASHED_STRING_HPP
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <type_traits>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include "../config/config.h"
|
||||
|
||||
|
||||
@@ -20,38 +21,14 @@ namespace internal {
|
||||
*/
|
||||
|
||||
|
||||
template<typename... Types>
|
||||
struct Identifier final: Identifier<Types>... {
|
||||
using identifier_type = std::size_t;
|
||||
|
||||
template<std::size_t... Indexes>
|
||||
constexpr Identifier(std::index_sequence<Indexes...>) ENTT_NOEXCEPT
|
||||
: Identifier<Types>{std::index_sequence<Indexes>{}}...
|
||||
{}
|
||||
|
||||
template<typename Type>
|
||||
constexpr std::size_t get() const ENTT_NOEXCEPT {
|
||||
return Identifier<std::decay_t<Type>>::get();
|
||||
}
|
||||
};
|
||||
template<typename...>
|
||||
struct IsPartOf;
|
||||
|
||||
template<typename Type, typename Current, typename... Other>
|
||||
struct IsPartOf<Type, Current, Other...>: std::conditional_t<std::is_same<Type, Current>::value, std::true_type, IsPartOf<Type, Other...>> {};
|
||||
|
||||
template<typename Type>
|
||||
struct Identifier<Type> {
|
||||
using identifier_type = std::size_t;
|
||||
|
||||
template<std::size_t Index>
|
||||
constexpr Identifier(std::index_sequence<Index>) ENTT_NOEXCEPT
|
||||
: index{Index}
|
||||
{}
|
||||
|
||||
constexpr std::size_t get() const ENTT_NOEXCEPT {
|
||||
return index;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::size_t index;
|
||||
};
|
||||
struct IsPartOf<Type>: std::false_type {};
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,23 +41,23 @@ private:
|
||||
|
||||
|
||||
/**
|
||||
* @brief Types identifers.
|
||||
* @brief Types identifiers.
|
||||
*
|
||||
* Variable template used to generate identifiers at compile-time for the given
|
||||
* types. Use the `constexpr` `get` member function to know what's the
|
||||
* identifier associated to the specific type.
|
||||
* types. Use the `get` member function to know what's the identifier associated
|
||||
* to the specific type.
|
||||
*
|
||||
* @note
|
||||
* Identifiers are constant expression and can be used in any context where such
|
||||
* an expression is required. As an example:
|
||||
* @code{.cpp}
|
||||
* constexpr auto identifiers = entt::ident<AType, AnotherType>;
|
||||
* using ID = entt::Identifier<AType, AnotherType>;
|
||||
*
|
||||
* switch(aTypeIdentifier) {
|
||||
* case identifers.get<AType>():
|
||||
* case ID::get<AType>():
|
||||
* // ...
|
||||
* break;
|
||||
* case identifers.get<AnotherType>():
|
||||
* case ID::get<AnotherType>():
|
||||
* // ...
|
||||
* break;
|
||||
* default:
|
||||
@@ -88,19 +65,37 @@ private:
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @note
|
||||
* In case of single type list, `get` isn't a member function template:
|
||||
* @code{.cpp}
|
||||
* func(std::integral_constant<
|
||||
* entt::ident<AType>::identifier_type,
|
||||
* entt::ident<AType>::get()
|
||||
* >{});
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Types List of types for which to generate identifiers.
|
||||
*/
|
||||
template<typename... Types>
|
||||
constexpr auto ident = internal::Identifier<std::decay_t<Types>...>{std::make_index_sequence<sizeof...(Types)>{}};
|
||||
class Identifier final {
|
||||
using tuple_type = std::tuple<std::decay_t<Types>...>;
|
||||
|
||||
template<typename Type, std::size_t... Indexes>
|
||||
static constexpr std::size_t get(std::index_sequence<Indexes...>) ENTT_NOEXCEPT {
|
||||
static_assert(internal::IsPartOf<Type, Types...>::value, "!");
|
||||
|
||||
std::size_t max{};
|
||||
using accumulator_type = std::size_t[];
|
||||
accumulator_type accumulator = { (max = std::is_same<Type, std::tuple_element_t<Indexes, tuple_type>>::value ? Indexes : max)... };
|
||||
(void)accumulator;
|
||||
return max;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using identifier_type = std::size_t;
|
||||
|
||||
/**
|
||||
* @brief Returns the identifier associated with a given type.
|
||||
* @tparam Type of which to return the identifier.
|
||||
* @return The identifier associated with the given type.
|
||||
*/
|
||||
template<typename Type>
|
||||
static constexpr identifier_type get() ENTT_NOEXCEPT {
|
||||
return get<std::decay_t<Type>>(std::make_index_sequence<sizeof...(Types)>{});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
61
src/entt/core/monostate.hpp
Normal file
61
src/entt/core/monostate.hpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef ENTT_CORE_MONOSTATE_HPP
|
||||
#define ENTT_CORE_MONOSTATE_HPP
|
||||
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include "family.hpp"
|
||||
#include "hashed_string.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
/**
|
||||
* @brief Minimal implementation of the monostate pattern.
|
||||
*
|
||||
* A minimal, yet complete configuration system built on top of the monostate
|
||||
* pattern. Thread safe by design, it works only with basic types like `int`s or
|
||||
* `bool`s.<br/>
|
||||
* Multiple types and therefore more than one value can be associated with a
|
||||
* single key. Because of this, users must pay attention to use the same type
|
||||
* both during an assignment and when they try to read back their data.
|
||||
* Otherwise, they can incur in unexpected results.
|
||||
*/
|
||||
template<HashedString::hash_type>
|
||||
struct Monostate {
|
||||
/**
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
* @tparam Type Type of the value to assign.
|
||||
* @param val User data to assign to the given key.
|
||||
*/
|
||||
template<typename Type>
|
||||
void operator=(Type val) const ENTT_NOEXCEPT {
|
||||
Monostate::value<Type> = val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Gets a value of a specific type for a given key.
|
||||
* @tparam Type Type of the value to get.
|
||||
* @return Stored value, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
operator Type() const ENTT_NOEXCEPT {
|
||||
return Monostate::value<Type>;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
static std::atomic<Type> value;
|
||||
};
|
||||
|
||||
|
||||
template<HashedString::hash_type ID>
|
||||
template<typename Type>
|
||||
std::atomic<Type> Monostate<ID>::value{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_CORE_MONOSTATE_HPP
|
||||
@@ -170,7 +170,7 @@ struct Actor {
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry.
|
||||
*/
|
||||
const registry_type & registry() const ENTT_NOEXCEPT {
|
||||
inline const registry_type & registry() const ENTT_NOEXCEPT {
|
||||
return reg;
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ struct Actor {
|
||||
* @brief Returns the entity associated with an actor.
|
||||
* @return The entity associated with the actor.
|
||||
*/
|
||||
entity_type entity() const ENTT_NOEXCEPT {
|
||||
inline entity_type entity() const ENTT_NOEXCEPT {
|
||||
return entt;
|
||||
}
|
||||
|
||||
|
||||
87
src/entt/entity/entity.hpp
Normal file
87
src/entt/entity/entity.hpp
Normal file
@@ -0,0 +1,87 @@
|
||||
#ifndef ENTT_ENTITY_ENTITY_HPP
|
||||
#define ENTT_ENTITY_ENTITY_HPP
|
||||
|
||||
|
||||
#include "../config/config.h"
|
||||
#include "entt_traits.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
namespace internal {
|
||||
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
|
||||
template<typename Entity>
|
||||
static constexpr auto null = ~typename entt_traits<Entity>::entity_type{};
|
||||
|
||||
|
||||
struct Null {
|
||||
explicit constexpr Null() = default;
|
||||
|
||||
template<typename Entity>
|
||||
constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
return null<Entity>;
|
||||
}
|
||||
|
||||
constexpr bool operator==(Null) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(Null) const ENTT_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Entity>
|
||||
constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return entity == null<Entity>;
|
||||
}
|
||||
|
||||
template<typename Entity>
|
||||
constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return entity != null<Entity>;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename Entity>
|
||||
constexpr bool operator==(const Entity entity, Null null) ENTT_NOEXCEPT {
|
||||
return null == entity;
|
||||
}
|
||||
|
||||
|
||||
template<typename Entity>
|
||||
constexpr bool operator!=(const Entity entity, Null null) ENTT_NOEXCEPT {
|
||||
return null != entity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond TURN_OFF_DOXYGEN
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Null entity.
|
||||
*
|
||||
* There exist implicit conversions from this variable to entity identifiers of
|
||||
* any allowed type. Similarly, there exist comparision operators between the
|
||||
* null entity and any other entity identifier.
|
||||
*/
|
||||
constexpr auto null = internal::Null{};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // ENTT_ENTITY_ENTITY_HPP
|
||||
@@ -32,6 +32,8 @@ struct entt_traits<std::uint16_t> {
|
||||
using entity_type = std::uint16_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint8_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int32_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr auto entity_mask = 0xFFF;
|
||||
@@ -47,8 +49,8 @@ struct entt_traits<std::uint16_t> {
|
||||
*
|
||||
* A 32 bits entity identifier guarantees:
|
||||
*
|
||||
* * 24 bits for the entity number (suitable for almost all the games).
|
||||
* * 8 bit for the version (resets in [0-255]).
|
||||
* * 20 bits for the entity number (suitable for almost all the games).
|
||||
* * 12 bit for the version (resets in [0-4095]).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint32_t> {
|
||||
@@ -56,6 +58,8 @@ struct entt_traits<std::uint32_t> {
|
||||
using entity_type = std::uint32_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint16_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int64_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr auto entity_mask = 0xFFFFF;
|
||||
@@ -71,8 +75,8 @@ struct entt_traits<std::uint32_t> {
|
||||
*
|
||||
* A 64 bits entity identifier guarantees:
|
||||
*
|
||||
* * 40 bits for the entity number (an indecently large number).
|
||||
* * 24 bit for the version (an indecently large number).
|
||||
* * 32 bits for the entity number (an indecently large number).
|
||||
* * 32 bit for the version (an indecently large number).
|
||||
*/
|
||||
template<>
|
||||
struct entt_traits<std::uint64_t> {
|
||||
@@ -80,13 +84,15 @@ struct entt_traits<std::uint64_t> {
|
||||
using entity_type = std::uint64_t;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = std::uint32_t;
|
||||
/*! @brief Difference type. */
|
||||
using difference_type = std::int64_t;
|
||||
|
||||
/*! @brief Mask to use to get the entity number out of an identifier. */
|
||||
static constexpr auto entity_mask = 0xFFFFFFFFFF;
|
||||
static constexpr auto entity_mask = 0xFFFFFFFF;
|
||||
/*! @brief Mask to use to get the version out of an identifier. */
|
||||
static constexpr auto version_mask = 0xFFFFFF;
|
||||
static constexpr auto version_mask = 0xFFFFFFFF;
|
||||
/*! @brief Extent of the entity number within an identifier. */
|
||||
static constexpr auto entity_shift = 40;
|
||||
static constexpr auto entity_shift = 32;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define ENTT_ENTITY_HELPER_HPP
|
||||
|
||||
|
||||
#include <type_traits>
|
||||
#include "../core/hashed_string.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "utility.hpp"
|
||||
@@ -78,6 +80,25 @@ void dependency(break_t, Sink<void(Registry<Entity> &, const Entity)> sink) {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Alias template to ease the assignment of labels to entities.
|
||||
*
|
||||
* If used in combination with hashed strings, it simplifies the assignment of
|
||||
* labels to entities and the use of labels in general where a type would be
|
||||
* required otherwise.<br/>
|
||||
* As an example and where the user defined literal for hashed strings hasn't
|
||||
* been changed:
|
||||
* @code{.cpp}
|
||||
* entt::DefaultRegistry registry;
|
||||
* registry.assign<entt::label<"enemy"_hs>>(entity);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Value The numeric representation of an instance of hashed string.
|
||||
*/
|
||||
template<typename HashedString::hash_type Value>
|
||||
using label = std::integral_constant<typename HashedString::hash_type, Value>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -37,17 +37,23 @@ namespace entt {
|
||||
*/
|
||||
template<typename Entity>
|
||||
class Prototype final {
|
||||
using fn_type = void(*)(const Prototype &, Registry<Entity> &, const Entity);
|
||||
using basic_fn_type = void(const Prototype &, Registry<Entity> &, const Entity);
|
||||
using component_type = typename Registry<Entity>::component_type;
|
||||
|
||||
template<typename Component>
|
||||
struct Wrapper { Component component; };
|
||||
|
||||
struct Handler {
|
||||
fn_type accommodate;
|
||||
fn_type assign;
|
||||
basic_fn_type *accommodate;
|
||||
basic_fn_type *assign;
|
||||
};
|
||||
|
||||
void release() {
|
||||
if(registry->valid(entity)) {
|
||||
registry->destroy(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Registry type. */
|
||||
using registry_type = Registry<Entity>;
|
||||
@@ -61,7 +67,7 @@ public:
|
||||
* @param registry A valid reference to a registry.
|
||||
*/
|
||||
Prototype(Registry<Entity> ®istry)
|
||||
: registry{registry},
|
||||
: registry{®istry},
|
||||
entity{registry.create()}
|
||||
{}
|
||||
|
||||
@@ -69,7 +75,51 @@ public:
|
||||
* @brief Releases all its resources.
|
||||
*/
|
||||
~Prototype() {
|
||||
registry.destroy(entity);
|
||||
release();
|
||||
}
|
||||
|
||||
/*! @brief Copying a prototype isn't allowed. */
|
||||
Prototype(const Prototype &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
*
|
||||
* After prototype move construction, instances that have been moved from
|
||||
* are placed in a valid but unspecified state. It's highly discouraged to
|
||||
* continue using them.
|
||||
*
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
Prototype(Prototype &&other)
|
||||
: handlers{std::move(other.handlers)},
|
||||
registry{other.registry},
|
||||
entity{other.entity}
|
||||
{
|
||||
other.entity = ~entity_type{};
|
||||
}
|
||||
|
||||
/*! @brief Copying a prototype isn't allowed. @return This Prototype. */
|
||||
Prototype & operator=(const Prototype &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
*
|
||||
* After prototype move assignment, instances that have been moved from are
|
||||
* placed in a valid but unspecified state. It's highly discouraged to
|
||||
* continue using them.
|
||||
*
|
||||
* @param other The instance to move from.
|
||||
* @return This Prototype.
|
||||
*/
|
||||
Prototype & operator=(Prototype &&other) {
|
||||
if(this != &other) {
|
||||
auto tmp{std::move(other)};
|
||||
handlers.swap(tmp.handlers);
|
||||
std::swap(registry, tmp.registry);
|
||||
std::swap(entity, tmp.entity);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,20 +131,20 @@ public:
|
||||
*/
|
||||
template<typename Component, typename... Args>
|
||||
Component & set(Args &&... args) {
|
||||
fn_type accommodate = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
|
||||
const auto &wrapper = prototype.registry.template get<Wrapper<Component>>(prototype.entity);
|
||||
basic_fn_type *accommodate = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
|
||||
const auto &wrapper = prototype.registry->template get<Wrapper<Component>>(prototype.entity);
|
||||
other.template accommodate<Component>(dst, wrapper.component);
|
||||
};
|
||||
|
||||
fn_type assign = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
|
||||
basic_fn_type *assign = [](const Prototype &prototype, Registry<Entity> &other, const Entity dst) {
|
||||
if(!other.template has<Component>(dst)) {
|
||||
const auto &wrapper = prototype.registry.template get<Wrapper<Component>>(prototype.entity);
|
||||
const auto &wrapper = prototype.registry->template get<Wrapper<Component>>(prototype.entity);
|
||||
other.template accommodate<Component>(dst, wrapper.component);
|
||||
}
|
||||
};
|
||||
|
||||
handlers[registry.template type<Component>()] = Handler{accommodate, assign};
|
||||
auto &wrapper = registry.template accommodate<Wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
|
||||
handlers[registry->template type<Component>()] = Handler{accommodate, assign};
|
||||
auto &wrapper = registry->template accommodate<Wrapper<Component>>(entity, Component{std::forward<Args>(args)...});
|
||||
return wrapper.component;
|
||||
}
|
||||
|
||||
@@ -104,8 +154,8 @@ public:
|
||||
*/
|
||||
template<typename Component>
|
||||
void unset() ENTT_NOEXCEPT {
|
||||
registry.template reset<Wrapper<Component>>(entity);
|
||||
handlers.erase(registry.template type<Component>());
|
||||
registry->template reset<Wrapper<Component>>(entity);
|
||||
handlers.erase(registry->template type<Component>());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +165,7 @@ public:
|
||||
*/
|
||||
template<typename... Component>
|
||||
bool has() const ENTT_NOEXCEPT {
|
||||
return registry.template has<Wrapper<Component>...>(entity);
|
||||
return registry->template has<Wrapper<Component>...>(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +182,7 @@ public:
|
||||
*/
|
||||
template<typename Component>
|
||||
const Component & get() const ENTT_NOEXCEPT {
|
||||
return registry.template get<Wrapper<Component>>(entity).component;
|
||||
return registry->template get<Wrapper<Component>>(entity).component;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,10 +279,8 @@ public:
|
||||
*
|
||||
* @return A valid entity identifier.
|
||||
*/
|
||||
entity_type create() const {
|
||||
const auto entity = registry.create();
|
||||
assign(registry, entity);
|
||||
return entity;
|
||||
inline entity_type create() const {
|
||||
return create(*registry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -282,10 +330,8 @@ public:
|
||||
*
|
||||
* @param dst A valid entity identifier.
|
||||
*/
|
||||
void assign(const entity_type dst) const {
|
||||
for(auto &handler: handlers) {
|
||||
handler.second.assign(*this, registry, dst);
|
||||
}
|
||||
inline void assign(const entity_type dst) const {
|
||||
assign(*registry, dst);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -331,10 +377,8 @@ public:
|
||||
*
|
||||
* @param dst A valid entity identifier.
|
||||
*/
|
||||
void accommodate(const entity_type dst) const {
|
||||
for(auto &handler: handlers) {
|
||||
handler.second.accommodate(*this, registry, dst);
|
||||
}
|
||||
inline void accommodate(const entity_type dst) const {
|
||||
accommodate(*registry, dst);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,7 +427,7 @@ public:
|
||||
* @param dst A valid entity identifier.
|
||||
*/
|
||||
inline void operator()(const entity_type dst) const ENTT_NOEXCEPT {
|
||||
assign(registry, dst);
|
||||
assign(*registry, dst);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -426,12 +470,12 @@ public:
|
||||
* @return A valid entity identifier.
|
||||
*/
|
||||
inline entity_type operator()() const ENTT_NOEXCEPT {
|
||||
return create(registry);
|
||||
return create(*registry);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<component_type, Handler> handlers;
|
||||
Registry<Entity> ®istry;
|
||||
Registry<Entity> *registry;
|
||||
entity_type entity;
|
||||
};
|
||||
|
||||
@@ -443,7 +487,7 @@ private:
|
||||
* applications.<br/>
|
||||
* Users should have a really good reason to choose something different.
|
||||
*/
|
||||
using DefaultPrototype = Prototype<std::uint32_t>;
|
||||
using DefaultPrototype = Prototype<DefaultRegistry::entity_type>;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -43,10 +44,10 @@ class Registry {
|
||||
using signal_type = SigH<void(Registry &, const Entity)>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<typename... Component>
|
||||
template<typename handler_family::family_type(*Type)(), typename... Component>
|
||||
static void creating(Registry ®istry, const Entity entity) {
|
||||
if(registry.has<Component...>(entity)) {
|
||||
registry.handlers[handler_family::type<Component...>()]->construct(entity);
|
||||
registry.handlers[Type()]->construct(entity);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ class Registry {
|
||||
}
|
||||
|
||||
struct Attachee {
|
||||
Attachee(const Entity entity): entity{entity} {}
|
||||
Attachee(const Entity entity) ENTT_NOEXCEPT: entity{entity} {}
|
||||
virtual ~Attachee() = default;
|
||||
Entity entity;
|
||||
};
|
||||
@@ -73,14 +74,43 @@ class Registry {
|
||||
Tag tag;
|
||||
};
|
||||
|
||||
template<typename Comp, std::size_t Pivot, typename... Component, std::size_t... Indexes>
|
||||
void connect(std::index_sequence<Indexes...>) {
|
||||
auto &cpool = pools[component_family::type<Comp>()];
|
||||
std::get<1>(cpool).sink().template connect<&Registry::creating<&handler_family::type<Component...>, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple<Component...>>...>>();
|
||||
std::get<2>(cpool).sink().template connect<&Registry::destroying<Component...>>();
|
||||
}
|
||||
|
||||
template<typename... Component, std::size_t... Indexes>
|
||||
void connect(std::index_sequence<Indexes...>) {
|
||||
using accumulator_type = int[];
|
||||
accumulator_type accumulator = { (assure<Component>(), connect<Component, Indexes, Component...>(std::make_index_sequence<sizeof...(Component)-1>{}), 0)... };
|
||||
(void)accumulator;
|
||||
}
|
||||
|
||||
template<typename Comp, std::size_t Pivot, typename... Component, std::size_t... Indexes>
|
||||
void disconnect(std::index_sequence<Indexes...>) {
|
||||
auto &cpool = pools[component_family::type<Comp>()];
|
||||
std::get<1>(cpool).sink().template disconnect<&Registry::creating<&handler_family::type<Component...>, std::tuple_element_t<(Indexes < Pivot ? Indexes : (Indexes+1)), std::tuple<Component...>>...>>();
|
||||
std::get<2>(cpool).sink().template disconnect<&Registry::destroying<Component...>>();
|
||||
}
|
||||
|
||||
template<typename... Component, std::size_t... Indexes>
|
||||
void disconnect(std::index_sequence<Indexes...>) {
|
||||
using accumulator_type = int[];
|
||||
// if a set exists, pools have already been created for it
|
||||
accumulator_type accumulator = { (disconnect<Component, Indexes, Component...>(std::make_index_sequence<sizeof...(Component)-1>{}), 0)... };
|
||||
(void)accumulator;
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
bool managed() const ENTT_NOEXCEPT {
|
||||
inline bool managed() const ENTT_NOEXCEPT {
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return ctype < pools.size() && std::get<0>(pools[ctype]);
|
||||
}
|
||||
|
||||
template<typename Component>
|
||||
const SparseSet<Entity, Component> & pool() const ENTT_NOEXCEPT {
|
||||
inline const SparseSet<Entity, Component> & pool() const ENTT_NOEXCEPT {
|
||||
assert(managed<Component>());
|
||||
const auto ctype = component_family::type<Component>();
|
||||
return static_cast<SparseSet<Entity, Component> &>(*std::get<0>(pools[ctype]));
|
||||
@@ -186,16 +216,24 @@ public:
|
||||
return managed<Component>() ? pool<Component>().size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities created so far.
|
||||
* @return Number of entities created so far.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
return entities.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities still in use.
|
||||
* @return Number of entities still in use.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
size_type alive() const ENTT_NOEXCEPT {
|
||||
return entities.size() - available;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of the pool for a given component.
|
||||
* @brief Increases the capacity of the pool for the given component.
|
||||
*
|
||||
* If the new capacity is greater than the current capacity, new storage is
|
||||
* allocated, otherwise the method does nothing.
|
||||
@@ -222,17 +260,28 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities ever created.
|
||||
* @return Number of entities ever created.
|
||||
* @brief Returns the capacity of the pool for the given component.
|
||||
* @tparam Component Type of component in which one is interested.
|
||||
* @return Capacity of the pool of the given component.
|
||||
*/
|
||||
template<typename Component>
|
||||
size_type capacity() const ENTT_NOEXCEPT {
|
||||
return entities.size();
|
||||
return managed<Component>() ? pool<Component>().capacity() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether the pool for the given component is empty.
|
||||
* @brief Returns the number of entities that a registry has currently
|
||||
* allocated space for.
|
||||
* @return Capacity of the registry.
|
||||
*/
|
||||
size_type capacity() const ENTT_NOEXCEPT {
|
||||
return entities.capacity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether the pool of the given component is empty.
|
||||
* @tparam Component Type of component in which one is interested.
|
||||
* @return True if the pool for the given component is empty, false
|
||||
* @return True if the pool of the given component is empty, false
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Component>
|
||||
@@ -338,10 +387,19 @@ public:
|
||||
return (entities[pos] == entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity identifier without the version.
|
||||
* @param entity An entity identifier, either valid or not.
|
||||
* @return The entity identifier without the version.
|
||||
*/
|
||||
entity_type entity(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
return entity & traits_type::entity_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the version stored along with an entity identifier.
|
||||
* @param entity An entity identifier, either valid or not.
|
||||
* @return Version stored along with the given entity identifier.
|
||||
* @return The version stored along with the given entity identifier.
|
||||
*/
|
||||
version_type version(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
return version_type((entity >> traits_type::entity_shift) & traits_type::version_mask);
|
||||
@@ -383,17 +441,16 @@ public:
|
||||
* function can be used to know if they are still valid or the entity has
|
||||
* been destroyed and potentially recycled.
|
||||
*
|
||||
* The returned entity has no components assigned.
|
||||
* The returned entity has no components nor tags assigned.
|
||||
*
|
||||
* @return A valid entity identifier.
|
||||
*/
|
||||
entity_type create() ENTT_NOEXCEPT {
|
||||
entity_type create() {
|
||||
entity_type entity;
|
||||
|
||||
if(available) {
|
||||
const auto entt = next;
|
||||
const auto version = entities[entt] & (~traits_type::entity_mask);
|
||||
|
||||
entity = entt | version;
|
||||
next = entities[entt] & traits_type::entity_mask;
|
||||
entities[entt] = entity;
|
||||
@@ -408,6 +465,25 @@ public:
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity that owns the given tag, if any.
|
||||
*
|
||||
* Convenient shortcut to destroy an entity by means of a tag type.<br/>
|
||||
* Syntactic sugar for the following snippet:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* if(registry.has<Tag>()) {
|
||||
* registry.destroy(registry.attachee<Tag>());
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Tag Type of tag to use to search for the entity.
|
||||
*/
|
||||
template<typename Tag>
|
||||
void destroy(tag_t) {
|
||||
return has<Tag>() ? destroy(attachee<Tag>()) : void();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys an entity and lets the registry recycle the identifier.
|
||||
*
|
||||
@@ -428,7 +504,7 @@ public:
|
||||
* An assertion will abort the execution at runtime in debug mode in case of
|
||||
* invalid entity.
|
||||
*
|
||||
* @param entity A valid entity identifier
|
||||
* @param entity A valid entity identifier.
|
||||
*/
|
||||
void destroy(const entity_type entity) {
|
||||
assert(valid(entity));
|
||||
@@ -456,15 +532,37 @@ public:
|
||||
// just a way to protect users from listeners that attach components
|
||||
assert(orphan(entity));
|
||||
|
||||
// lengthens the implicit list of destroyed entities
|
||||
const auto entt = entity & traits_type::entity_mask;
|
||||
const auto version = (((entity >> traits_type::entity_shift) + 1) & traits_type::version_mask) << traits_type::entity_shift;
|
||||
const auto node = (available ? next : ((entt + 1) & traits_type::entity_mask)) | version;
|
||||
|
||||
entities[entt] = node;
|
||||
next = entt;
|
||||
++available;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entities that own the given components, if any.
|
||||
*
|
||||
* Convenient shortcut to destroy a set of entities at once.<br/>
|
||||
* Syntactic sugar for the following snippet:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* for(const auto entity: registry.view<Component...>(Type{}...)) {
|
||||
* registry.destroy(entity);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Component Types of components to use to search for the entities.
|
||||
* @tparam Type Type of view to use or empty to use a standard view.
|
||||
*/
|
||||
template<typename... Component, typename... Type>
|
||||
void destroy(Type...) {
|
||||
for(const auto entity: view<Component...>(Type{}...)) {
|
||||
destroy(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Attaches the given tag to an entity.
|
||||
*
|
||||
@@ -482,7 +580,7 @@ public:
|
||||
*
|
||||
* @tparam Tag Type of tag to create.
|
||||
* @tparam Args Types of arguments to use to construct the tag.
|
||||
* @param entity A valid entity identifier
|
||||
* @param entity A valid entity identifier.
|
||||
* @param args Parameters to use to initialize the tag.
|
||||
* @return A reference to the newly created tag.
|
||||
*/
|
||||
@@ -801,7 +899,7 @@ public:
|
||||
* @return A valid entity identifier.
|
||||
*/
|
||||
template<typename Tag>
|
||||
entity_type move(const entity_type entity) {
|
||||
entity_type move(const entity_type entity) ENTT_NOEXCEPT {
|
||||
assert(valid(entity));
|
||||
assert(has<Tag>());
|
||||
auto &tag = std::get<0>(tags[tag_family::type<Tag>()]);
|
||||
@@ -988,7 +1086,7 @@ public:
|
||||
* maximize the performance during iterations and users should not make any
|
||||
* assumption on the order.<br/>
|
||||
* This function can be used to impose an order to the elements in the pool
|
||||
* for the given component. The order is kept valid until a component of the
|
||||
* of the given component. The order is kept valid until a component of the
|
||||
* given type is assigned or removed from an entity.
|
||||
*
|
||||
* The comparison function object must return `true` if the first element
|
||||
@@ -1016,13 +1114,15 @@ public:
|
||||
* @tparam Component Type of components to sort.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param sort A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Component, typename Compare, typename Sort = StdSort>
|
||||
void sort(Compare compare, Sort sort = Sort{}) {
|
||||
template<typename Component, typename Compare, typename Sort = StdSort, typename... Args>
|
||||
void sort(Compare compare, Sort sort = Sort{}, Args &&... args) {
|
||||
assure<Component>();
|
||||
pool<Component>().sort(std::move(compare), std::move(sort));
|
||||
pool<Component>().sort(std::move(compare), std::move(sort), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1120,7 +1220,8 @@ public:
|
||||
*/
|
||||
void reset() {
|
||||
each([this](const auto entity) {
|
||||
destroy(entity);
|
||||
// useless this-> used to suppress a warning with clang
|
||||
this->destroy(entity);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1148,7 +1249,7 @@ public:
|
||||
void each(Func func) const {
|
||||
if(available) {
|
||||
for(auto pos = entities.size(); pos; --pos) {
|
||||
const entity_type curr = pos - 1;
|
||||
const auto curr = entity_type(pos - 1);
|
||||
const auto entity = entities[curr];
|
||||
const auto entt = entity & traits_type::entity_mask;
|
||||
|
||||
@@ -1243,6 +1344,7 @@ public:
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @return A newly created standard view.
|
||||
@@ -1278,22 +1380,13 @@ public:
|
||||
}
|
||||
|
||||
if(!handlers[htype]) {
|
||||
connect<Component...>(std::make_index_sequence<sizeof...(Component)>{});
|
||||
handlers[htype] = std::make_unique<SparseSet<entity_type>>();
|
||||
auto &handler = handlers[htype];
|
||||
auto &handler = *handlers[htype];
|
||||
|
||||
for(auto entity: view<Component...>()) {
|
||||
handler->construct(entity);
|
||||
handler.construct(entity);
|
||||
}
|
||||
|
||||
auto connect = [this](const auto ctype) {
|
||||
auto &cpool = pools[ctype];
|
||||
std::get<1>(cpool).sink().template connect<&Registry::creating<Component...>>();
|
||||
std::get<2>(cpool).sink().template connect<&Registry::destroying<Component...>>();
|
||||
};
|
||||
|
||||
using accumulator_type = int[];
|
||||
accumulator_type accumulator = { (assure<Component>(), connect(component_family::type<Component>()), 0)... };
|
||||
(void)accumulator;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1315,19 +1408,8 @@ public:
|
||||
template<typename... Component>
|
||||
void discard() {
|
||||
if(contains<Component...>()) {
|
||||
const auto htype = handler_family::type<Component...>();
|
||||
|
||||
auto disconnect = [this](const auto ctype) {
|
||||
auto &cpool = pools[ctype];
|
||||
std::get<1>(cpool).sink().template disconnect<&Registry::creating<Component...>>();
|
||||
std::get<2>(cpool).sink().template disconnect<&Registry::destroying<Component...>>();
|
||||
};
|
||||
|
||||
// if a set exists, pools have already been created for it
|
||||
using accumulator_type = int[];
|
||||
accumulator_type accumulator = { (disconnect(component_family::type<Component>()), 0)... };
|
||||
handlers[htype].reset();
|
||||
(void)accumulator;
|
||||
disconnect<Component...>(std::make_index_sequence<sizeof...(Component)>{});
|
||||
handlers[handler_family::type<Component...>()].reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1377,6 +1459,7 @@ public:
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam Component Types of components used to construct the view.
|
||||
* @return A newly created persistent view.
|
||||
@@ -1406,6 +1489,7 @@ public:
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam Component Type of component used to construct the view.
|
||||
* @return A newly created raw view.
|
||||
@@ -1416,6 +1500,44 @@ public:
|
||||
return RawView<Entity, Component>{pool<Component>()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a runtime view for the given components.
|
||||
*
|
||||
* This kind of views are created on the fly and share with the registry its
|
||||
* internal data structures.<br/>
|
||||
* Users should throw away the view after use. Fortunately, creating and
|
||||
* destroying a view is an incredibly cheap operation because they do not
|
||||
* require any type of initialization.<br/>
|
||||
* As a rule of thumb, storing a view should never be an option.
|
||||
*
|
||||
* Runtime views are well suited when users want to construct a view from
|
||||
* some external inputs and don't know at compile-time what are the required
|
||||
* components.<br/>
|
||||
* This is particularly well suited to plugin systems and mods in general.
|
||||
*
|
||||
* @see View
|
||||
* @see View<Entity, Component>
|
||||
* @see PersistentView
|
||||
* @see RawView
|
||||
* @see RuntimeView
|
||||
*
|
||||
* @tparam It Type of forward iterator.
|
||||
* @param first An iterator to the first element of the range of components.
|
||||
* @param last An iterator past the last element of the range of components.
|
||||
* @return A newly created runtime view.
|
||||
*/
|
||||
template<typename It>
|
||||
RuntimeView<Entity> view(It first, It last) {
|
||||
static_assert(std::is_convertible<typename std::iterator_traits<It>::value_type, component_type>::value, "!");
|
||||
std::vector<const SparseSet<Entity> *> set(last - first);
|
||||
|
||||
std::transform(first, last, set.begin(), [this](const component_type ctype) {
|
||||
return ctype < pools.size() ? std::get<0>(pools[ctype]).get() : nullptr;
|
||||
});
|
||||
|
||||
return RuntimeView<Entity>{std::move(set)};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a temporary object to use to create snapshots.
|
||||
*
|
||||
@@ -1426,11 +1548,11 @@ public:
|
||||
*
|
||||
* @return A temporary object to use to take snasphosts.
|
||||
*/
|
||||
Snapshot<Entity> snapshot() const {
|
||||
using follow_fn_type = entity_type(*)(const Registry &, const entity_type);
|
||||
Snapshot<Entity> snapshot() const ENTT_NOEXCEPT {
|
||||
using follow_fn_type = entity_type(const Registry &, const entity_type);
|
||||
const entity_type seed = available ? (next | (entities[next] & ~traits_type::entity_mask)) : next;
|
||||
|
||||
follow_fn_type follow = [](const Registry ®istry, const entity_type entity) -> entity_type {
|
||||
follow_fn_type *follow = [](const Registry ®istry, const entity_type entity) -> entity_type {
|
||||
const auto &entities = registry.entities;
|
||||
const auto entt = entity & traits_type::entity_mask;
|
||||
const auto next = entities[entt] & traits_type::entity_mask;
|
||||
@@ -1455,10 +1577,10 @@ public:
|
||||
*
|
||||
* @return A temporary object to use to load snasphosts.
|
||||
*/
|
||||
SnapshotLoader<Entity> restore() {
|
||||
using assure_fn_type = void(*)(Registry &, const entity_type, const bool);
|
||||
SnapshotLoader<Entity> restore() ENTT_NOEXCEPT {
|
||||
using assure_fn_type = void(Registry &, const entity_type, const bool);
|
||||
|
||||
assure_fn_type assure = [](Registry ®istry, const entity_type entity, const bool destroyed) {
|
||||
assure_fn_type *assure = [](Registry ®istry, const entity_type entity, const bool destroyed) {
|
||||
using promotion_type = std::conditional_t<sizeof(size_type) >= sizeof(entity_type), size_type, entity_type>;
|
||||
// explicit promotion to avoid warnings with std::uint16_t
|
||||
const auto entt = promotion_type{entity} & traits_type::entity_mask;
|
||||
|
||||
@@ -39,9 +39,9 @@ class Snapshot final {
|
||||
/*! @brief A registry is allowed to create snapshots. */
|
||||
friend class Registry<Entity>;
|
||||
|
||||
using follow_fn_type = Entity(*)(const Registry<Entity> &, const Entity);
|
||||
using follow_fn_type = Entity(const Registry<Entity> &, const Entity);
|
||||
|
||||
Snapshot(const Registry<Entity> ®istry, Entity seed, follow_fn_type follow) ENTT_NOEXCEPT
|
||||
Snapshot(const Registry<Entity> ®istry, Entity seed, follow_fn_type *follow) ENTT_NOEXCEPT
|
||||
: registry{registry},
|
||||
seed{seed},
|
||||
follow{follow}
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
*/
|
||||
template<typename Archive>
|
||||
const Snapshot & entities(Archive &archive) const {
|
||||
archive(static_cast<Entity>(registry.size()));
|
||||
archive(static_cast<Entity>(registry.alive()));
|
||||
registry.each([&archive](const auto entity) { archive(entity); });
|
||||
return *this;
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
*/
|
||||
template<typename Archive>
|
||||
const Snapshot & destroyed(Archive &archive) const {
|
||||
auto size = registry.capacity() - registry.size();
|
||||
auto size = registry.size() - registry.alive();
|
||||
archive(static_cast<Entity>(size));
|
||||
auto curr = seed;
|
||||
|
||||
@@ -243,7 +243,7 @@ public:
|
||||
private:
|
||||
const Registry<Entity> ®istry;
|
||||
const Entity seed;
|
||||
follow_fn_type follow;
|
||||
follow_fn_type *follow;
|
||||
};
|
||||
|
||||
|
||||
@@ -262,9 +262,9 @@ class SnapshotLoader final {
|
||||
/*! @brief A registry is allowed to create snapshot loaders. */
|
||||
friend class Registry<Entity>;
|
||||
|
||||
using assure_fn_type = void(*)(Registry<Entity> &, const Entity, const bool);
|
||||
using assure_fn_type = void(Registry<Entity> &, const Entity, const bool);
|
||||
|
||||
SnapshotLoader(Registry<Entity> ®istry, assure_fn_type assure_fn) ENTT_NOEXCEPT
|
||||
SnapshotLoader(Registry<Entity> ®istry, assure_fn_type *assure_fn) ENTT_NOEXCEPT
|
||||
: registry{registry},
|
||||
assure_fn{assure_fn}
|
||||
{
|
||||
@@ -406,7 +406,7 @@ public:
|
||||
|
||||
private:
|
||||
Registry<Entity> ®istry;
|
||||
assure_fn_type assure_fn;
|
||||
assure_fn_type *assure_fn;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "entt_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
|
||||
|
||||
namespace entt {
|
||||
@@ -59,19 +60,30 @@ template<typename Entity>
|
||||
class SparseSet<Entity> {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
struct Iterator final {
|
||||
using difference_type = std::size_t;
|
||||
using value_type = Entity;
|
||||
using pointer = const value_type *;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
class Iterator final {
|
||||
friend class SparseSet<Entity>;
|
||||
|
||||
Iterator(pointer direct, std::size_t pos)
|
||||
: direct{direct}, pos{pos}
|
||||
using direct_type = const std::vector<Entity>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
Iterator(direct_type *direct, index_type index) ENTT_NOEXCEPT
|
||||
: direct{direct}, index{index}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = const Entity;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
Iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --pos, *this;
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) ENTT_NOEXCEPT {
|
||||
@@ -79,39 +91,80 @@ class SparseSet<Entity> {
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
Iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
Iterator operator--(int) ENTT_NOEXCEPT {
|
||||
Iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
Iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
pos -= value;
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return Iterator{direct, pos-value};
|
||||
return Iterator{direct, index-value};
|
||||
}
|
||||
|
||||
inline Iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
inline Iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*direct)[index-value-1];
|
||||
}
|
||||
|
||||
bool operator==(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.pos == pos;
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference operator*() const ENTT_NOEXCEPT {
|
||||
return direct[pos-1];
|
||||
bool operator<(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
inline bool operator<=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
inline bool operator>=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return &(*direct)[index-1];
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
pointer direct;
|
||||
std::size_t pos;
|
||||
direct_type *direct;
|
||||
index_type index;
|
||||
};
|
||||
|
||||
static constexpr auto pending = ~typename traits_type::entity_type{};
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Entity dependent position type. */
|
||||
using pos_type = entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Input iterator type. */
|
||||
@@ -147,6 +200,15 @@ public:
|
||||
direct.reserve(cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements that a sparse set has currently
|
||||
* allocated space for.
|
||||
* @return Capacity of the sparse set.
|
||||
*/
|
||||
size_type capacity() const ENTT_NOEXCEPT {
|
||||
return direct.capacity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the extent of a sparse set.
|
||||
*
|
||||
@@ -215,7 +277,8 @@ public:
|
||||
* @return An iterator to the first entity of the internal packed array.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
return const_iterator_type{direct.data(), direct.size()};
|
||||
const typename traits_type::difference_type pos = direct.size();
|
||||
return const_iterator_type{&direct, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,7 +327,7 @@ public:
|
||||
* internal packed array.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
return const_iterator_type{direct.data(), {}};
|
||||
return const_iterator_type{&direct, {}};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,6 +364,15 @@ public:
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
inline const entity_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return cbegin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a sparse set contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
@@ -308,8 +380,8 @@ public:
|
||||
*/
|
||||
bool has(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(entity & traits_type::entity_mask);
|
||||
// testing against pending permits to avoid accessing the direct vector
|
||||
return (pos < reverse.size()) && (reverse[pos] != pending);
|
||||
// testing against null permits to avoid accessing the direct vector
|
||||
return (pos < reverse.size()) && (reverse[pos] != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,8 +404,8 @@ public:
|
||||
bool fast(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
const auto pos = size_type(entity & traits_type::entity_mask);
|
||||
assert(pos < reverse.size());
|
||||
// testing against pending permits to avoid accessing the direct vector
|
||||
return (reverse[pos] != pending);
|
||||
// testing against null permits to avoid accessing the direct vector
|
||||
return (reverse[pos] != null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -348,7 +420,7 @@ public:
|
||||
* @param entity A valid entity identifier.
|
||||
* @return The position of the entity in the sparse set.
|
||||
*/
|
||||
pos_type get(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
size_type get(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
assert(has(entity));
|
||||
return reverse[entity & traits_type::entity_mask];
|
||||
}
|
||||
@@ -369,11 +441,11 @@ public:
|
||||
const auto pos = size_type(entity & traits_type::entity_mask);
|
||||
|
||||
if(!(pos < reverse.size())) {
|
||||
const auto value = pending;
|
||||
reverse.resize(pos+1, value);
|
||||
// null is safe in all cases for our purposes
|
||||
reverse.resize(pos+1, null);
|
||||
}
|
||||
|
||||
reverse[pos] = pos_type(direct.size());
|
||||
reverse[pos] = entity_type(direct.size());
|
||||
direct.push_back(entity);
|
||||
}
|
||||
|
||||
@@ -395,7 +467,7 @@ public:
|
||||
// swapping isn't required here, we are getting rid of the last element
|
||||
reverse[back & traits_type::entity_mask] = candidate;
|
||||
direct[candidate] = back;
|
||||
candidate = pending;
|
||||
candidate = null;
|
||||
direct.pop_back();
|
||||
}
|
||||
|
||||
@@ -414,7 +486,7 @@ public:
|
||||
* @param lhs A valid position within the sparse set.
|
||||
* @param rhs A valid position within the sparse set.
|
||||
*/
|
||||
void swap(const pos_type lhs, const pos_type rhs) ENTT_NOEXCEPT {
|
||||
void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT {
|
||||
assert(lhs < direct.size());
|
||||
assert(rhs < direct.size());
|
||||
auto &src = direct[lhs];
|
||||
@@ -446,7 +518,7 @@ public:
|
||||
auto from = other.cbegin();
|
||||
auto to = other.cend();
|
||||
|
||||
pos_type pos = direct.size() - 1;
|
||||
size_type pos = direct.size() - 1;
|
||||
|
||||
while(pos && from != to) {
|
||||
if(has(*from)) {
|
||||
@@ -470,7 +542,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<pos_type> reverse;
|
||||
std::vector<entity_type> reverse;
|
||||
std::vector<entity_type> direct;
|
||||
};
|
||||
|
||||
@@ -500,21 +572,33 @@ private:
|
||||
template<typename Entity, typename Type>
|
||||
class SparseSet<Entity, Type>: public SparseSet<Entity> {
|
||||
using underlying_type = SparseSet<Entity>;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
template<bool Const>
|
||||
struct Iterator final {
|
||||
using difference_type = std::size_t;
|
||||
class Iterator final {
|
||||
friend class SparseSet<Entity, Type>;
|
||||
|
||||
using instance_type = std::conditional_t<Const, const std::vector<Type>, std::vector<Type>>;
|
||||
using index_type = typename traits_type::difference_type;
|
||||
|
||||
Iterator(instance_type *instances, index_type index) ENTT_NOEXCEPT
|
||||
: instances{instances}, index{index}
|
||||
{}
|
||||
|
||||
public:
|
||||
using difference_type = index_type;
|
||||
using value_type = std::conditional_t<Const, const Type, Type>;
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
Iterator(pointer instances, std::size_t pos)
|
||||
: instances{instances}, pos{pos}
|
||||
{}
|
||||
Iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator & operator++() ENTT_NOEXCEPT {
|
||||
return --pos, *this;
|
||||
return --index, *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) ENTT_NOEXCEPT {
|
||||
@@ -522,34 +606,75 @@ class SparseSet<Entity, Type>: public SparseSet<Entity> {
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
Iterator & operator--() ENTT_NOEXCEPT {
|
||||
return ++index, *this;
|
||||
}
|
||||
|
||||
Iterator operator--(int) ENTT_NOEXCEPT {
|
||||
Iterator orig = *this;
|
||||
return --(*this), orig;
|
||||
}
|
||||
|
||||
Iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
pos -= value;
|
||||
index -= value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return Iterator{instances, pos-value};
|
||||
return Iterator{instances, index-value};
|
||||
}
|
||||
|
||||
inline Iterator & operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
inline Iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
difference_type operator-(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.index - index;
|
||||
}
|
||||
|
||||
reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*instances)[index-value-1];
|
||||
}
|
||||
|
||||
bool operator==(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.pos == pos;
|
||||
return other.index == index;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
reference operator*() const ENTT_NOEXCEPT {
|
||||
return instances[pos-1];
|
||||
bool operator<(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
inline bool operator<=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
inline bool operator>=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this < other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return (instances+pos-1);
|
||||
return &(*instances)[index-1];
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
pointer instances;
|
||||
std::size_t pos;
|
||||
instance_type *instances;
|
||||
index_type index;
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -557,8 +682,6 @@ public:
|
||||
using object_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
/*! @brief Entity dependent position type. */
|
||||
using pos_type = typename underlying_type::pos_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename underlying_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
@@ -643,7 +766,8 @@ public:
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
return const_iterator_type{instances.data(), instances.size()};
|
||||
const typename traits_type::difference_type pos = instances.size();
|
||||
return const_iterator_type{&instances, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -675,7 +799,8 @@ public:
|
||||
* @return An iterator to the first instance of the given type.
|
||||
*/
|
||||
iterator_type begin() ENTT_NOEXCEPT {
|
||||
return iterator_type{instances.data(), instances.size()};
|
||||
const typename traits_type::difference_type pos = instances.size();
|
||||
return iterator_type{&instances, pos};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,7 +818,7 @@ public:
|
||||
* given type.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
return const_iterator_type{instances.data(), {}};
|
||||
return const_iterator_type{&instances, {}};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -729,7 +854,25 @@ public:
|
||||
* given type.
|
||||
*/
|
||||
iterator_type end() ENTT_NOEXCEPT {
|
||||
return iterator_type{instances.data(), {}};
|
||||
return iterator_type{&instances, {}};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
inline const object_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return cbegin()[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
inline object_type & operator[](const size_type pos) ENTT_NOEXCEPT {
|
||||
return const_cast<object_type &>(const_cast<const SparseSet *>(this)->operator[](pos));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -876,19 +1019,21 @@ public:
|
||||
*
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param sort A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Compare, typename Sort = StdSort>
|
||||
void sort(Compare compare, Sort sort = Sort{}) {
|
||||
std::vector<pos_type> copy(instances.size());
|
||||
template<typename Compare, typename Sort = StdSort, typename... Args>
|
||||
void sort(Compare compare, Sort sort = Sort{}, Args &&... args) {
|
||||
std::vector<size_type> copy(instances.size());
|
||||
std::iota(copy.begin(), copy.end(), 0);
|
||||
|
||||
sort(copy.begin(), copy.end(), [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
return compare(const_cast<const object_type &>(instances[rhs]), const_cast<const object_type &>(instances[lhs]));
|
||||
});
|
||||
}, std::forward<Args>(args)...);
|
||||
|
||||
for(pos_type pos = 0, last = copy.size(); pos < last; ++pos) {
|
||||
for(size_type pos = 0, last = copy.size(); pos < last; ++pos) {
|
||||
auto curr = pos;
|
||||
auto next = copy[curr];
|
||||
|
||||
@@ -931,7 +1076,7 @@ public:
|
||||
auto from = other.cbegin();
|
||||
auto to = other.cend();
|
||||
|
||||
pos_type pos = underlying_type::size() - 1;
|
||||
size_type pos = underlying_type::size() - 1;
|
||||
const auto *local = underlying_type::data();
|
||||
|
||||
while(pos && from != to) {
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
#define ENTT_ENTITY_VIEW_HPP
|
||||
|
||||
|
||||
#include <iterator>
|
||||
#include <cassert>
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
@@ -60,6 +62,7 @@ class Registry;
|
||||
* @sa View
|
||||
* @sa View<Entity, Component>
|
||||
* @sa RawView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Types of components iterated by the view.
|
||||
@@ -82,14 +85,14 @@ class PersistentView final {
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename view_type::const_iterator_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename view_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename view_type::const_iterator_type;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given components.
|
||||
@@ -234,6 +237,15 @@ public:
|
||||
return view.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
const entity_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return view[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
@@ -445,6 +457,7 @@ private:
|
||||
* @sa View<Entity, Component>
|
||||
* @sa PersistentView
|
||||
* @sa RawView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Types of components iterated by the view.
|
||||
@@ -469,7 +482,25 @@ class View final {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
class Iterator {
|
||||
using size_type = typename view_type::size_type;
|
||||
friend class View<Entity, Component...>;
|
||||
|
||||
using extent_type = typename view_type::size_type;
|
||||
|
||||
Iterator(unchecked_type unchecked, underlying_iterator_type begin, underlying_iterator_type end) ENTT_NOEXCEPT
|
||||
: unchecked{unchecked},
|
||||
begin{begin},
|
||||
end{end},
|
||||
extent{min(std::make_index_sequence<unchecked.size()>{})}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t... Indexes>
|
||||
extent_type min(std::index_sequence<Indexes...>) const ENTT_NOEXCEPT {
|
||||
return std::min({ std::get<Indexes>(unchecked)->extent()... });
|
||||
}
|
||||
|
||||
bool valid() const ENTT_NOEXCEPT {
|
||||
const auto entity = *begin;
|
||||
@@ -485,18 +516,12 @@ class View final {
|
||||
using value_type = typename underlying_iterator_type::value_type;
|
||||
using pointer = typename underlying_iterator_type::pointer;
|
||||
using reference = typename underlying_iterator_type::reference;
|
||||
using iterator_category = typename underlying_iterator_type::iterator_category;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
Iterator(unchecked_type unchecked, size_type extent, underlying_iterator_type begin, underlying_iterator_type end) ENTT_NOEXCEPT
|
||||
: unchecked{unchecked},
|
||||
extent{extent},
|
||||
begin{begin},
|
||||
end{end}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
Iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator & operator++() ENTT_NOEXCEPT {
|
||||
return (++begin != end && !valid()) ? ++(*this) : *this;
|
||||
@@ -507,14 +532,6 @@ class View final {
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
Iterator & operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return ((begin += value) != end && !valid()) ? ++(*this) : *this;
|
||||
}
|
||||
|
||||
Iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return Iterator{unchecked, extent, begin+value, end};
|
||||
}
|
||||
|
||||
bool operator==(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.begin == begin;
|
||||
}
|
||||
@@ -523,15 +540,19 @@ class View final {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
value_type operator*() const ENTT_NOEXCEPT {
|
||||
return *begin;
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return begin.operator->();
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
const unchecked_type unchecked;
|
||||
const size_type extent;
|
||||
unchecked_type unchecked;
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
extent_type extent;
|
||||
};
|
||||
|
||||
View(pool_type<Component> &... pools) ENTT_NOEXCEPT
|
||||
@@ -543,11 +564,6 @@ class View final {
|
||||
return std::get<pool_type<Comp> &>(pools);
|
||||
}
|
||||
|
||||
template<typename Comp>
|
||||
inline pool_type<Comp> & pool() ENTT_NOEXCEPT {
|
||||
return const_cast<pool_type<Comp> &>(const_cast<const View *>(this)->pool<Comp>());
|
||||
}
|
||||
|
||||
const view_type * candidate() const ENTT_NOEXCEPT {
|
||||
return std::min({ static_cast<const view_type *>(&pool<Component>())... }, [](const auto *lhs, const auto *rhs) {
|
||||
return lhs->size() < rhs->size();
|
||||
@@ -563,10 +579,6 @@ class View final {
|
||||
return other;
|
||||
}
|
||||
|
||||
typename view_type::size_type extent() const ENTT_NOEXCEPT {
|
||||
return std::min({ pool<Component>().extent()... });
|
||||
}
|
||||
|
||||
template<typename Comp, typename Other>
|
||||
inline std::enable_if_t<std::is_same<Comp, Other>::value, const Other &>
|
||||
get(const component_iterator_type<Comp> &it, const Entity) const ENTT_NOEXCEPT { return *it; }
|
||||
@@ -578,29 +590,24 @@ class View final {
|
||||
template<typename Comp, typename Func, std::size_t... Indexes>
|
||||
void each(const pool_type<Comp> &cpool, Func func, std::index_sequence<Indexes...>) const {
|
||||
const auto other = unchecked(&cpool);
|
||||
std::array<underlying_iterator_type, sizeof...(Component)> data{{cpool.view_type::cbegin(), std::get<Indexes>(other)->cbegin()...}};
|
||||
std::array<underlying_iterator_type, sizeof...(Indexes)> data{{std::get<Indexes>(other)->cbegin()...}};
|
||||
const auto extent = std::min({ pool<Component>().extent()... });
|
||||
auto raw = std::make_tuple(pool<Component>().cbegin()...);
|
||||
const auto end = cpool.view_type::cend();
|
||||
std::size_t pos{};
|
||||
auto begin = cpool.view_type::cbegin();
|
||||
|
||||
// we can directly use the raw iterators if pools are ordered
|
||||
while(!pos && data[0] != end) {
|
||||
for(pos = data.size() - 1; pos && *(data[pos]++) == *data[pos-1]; --pos);
|
||||
|
||||
if(!pos) {
|
||||
func(*(data[0]++), *(std::get<component_iterator_type<Component>>(raw)++)...);
|
||||
}
|
||||
while(begin != end && std::min({ (*(std::get<Indexes>(data)++) == *begin)... })) {
|
||||
func(*(begin++), *(std::get<component_iterator_type<Component>>(raw)++)...);
|
||||
}
|
||||
|
||||
auto it = std::get<component_iterator_type<Comp>>(raw);
|
||||
const auto ext = extent();
|
||||
|
||||
// fallback to visit what remains using indirections
|
||||
for(; data[0] != end; ++data[0], ++it) {
|
||||
const auto entity = *data[0];
|
||||
while(begin != end) {
|
||||
const auto entity = *(begin++);
|
||||
const auto it = std::get<component_iterator_type<Comp>>(raw)++;
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
|
||||
if(sz < ext && std::all_of(other.cbegin(), other.cend(), [entity](const view_type *view) { return view->fast(entity); })) {
|
||||
if(sz < extent && std::all_of(other.cbegin(), other.cend(), [entity](const view_type *view) { return view->fast(entity); })) {
|
||||
// avoided at least the indirection due to the sparse set for the pivot type (see get for more details)
|
||||
func(entity, get<Comp, Component>(it, entity)...);
|
||||
}
|
||||
@@ -608,14 +615,14 @@ class View final {
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = Iterator;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = Iterator;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename view_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = Iterator;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = Iterator;
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities that have the given components.
|
||||
@@ -649,7 +656,7 @@ public:
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const auto *view = candidate();
|
||||
return iterator_type{ unchecked(view), extent(), view->cbegin(), view->cend() };
|
||||
return const_iterator_type{unchecked(view), view->cbegin(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -705,7 +712,7 @@ public:
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
const auto *view = candidate();
|
||||
return iterator_type{ unchecked(view), extent(), view->cend(), view->cend() };
|
||||
return const_iterator_type{unchecked(view), view->cend(), view->cend()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -753,7 +760,8 @@ public:
|
||||
*/
|
||||
bool contains(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
return sz < extent() && std::min({ (pool<Component>().has(entity) && (pool<Component>().data()[pool<Component>().view_type::get(entity)] == entity))... });
|
||||
const auto extent = std::min({ pool<Component>().extent()... });
|
||||
return sz < extent && std::min({ (pool<Component>().has(entity) && (pool<Component>().data()[pool<Component>().view_type::get(entity)] == entity))... });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,6 +942,7 @@ private:
|
||||
* @sa View
|
||||
* @sa PersistentView
|
||||
* @sa RawView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Type of component iterated by the view.
|
||||
@@ -951,16 +960,16 @@ class View<Entity, Component> final {
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename view_type::const_iterator_type;
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = typename pool_type::object_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename pool_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename pool_type::size_type;
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = typename pool_type::object_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename view_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename view_type::const_iterator_type;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that have the given component.
|
||||
@@ -1137,6 +1146,15 @@ public:
|
||||
return pool.view_type::end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
const entity_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return pool.view_type::operator[](pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
@@ -1268,6 +1286,7 @@ private:
|
||||
* @sa View
|
||||
* @sa View<Entity, Component>
|
||||
* @sa PersistentView
|
||||
* @sa RuntimeView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Type of component iterated by the view.
|
||||
@@ -1284,16 +1303,16 @@ class RawView final {
|
||||
{}
|
||||
|
||||
public:
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename pool_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename pool_type::const_iterator_type;
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = typename pool_type::object_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename pool_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename pool_type::size_type;
|
||||
/*! @brief Type of component iterated by the view. */
|
||||
using raw_type = typename pool_type::object_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = typename pool_type::iterator_type;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = typename pool_type::const_iterator_type;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of instances of the given type.
|
||||
@@ -1464,6 +1483,24 @@ public:
|
||||
return pool.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
const raw_type & operator[](const size_type pos) const ENTT_NOEXCEPT {
|
||||
return pool[pos];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the element at the given position.
|
||||
* @param pos Position of the element to return.
|
||||
* @return A reference to the requested element.
|
||||
*/
|
||||
inline raw_type & operator[](const size_type pos) ENTT_NOEXCEPT {
|
||||
return const_cast<raw_type &>(const_cast<const RawView *>(this)->operator[](pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates components and applies the given function object to them.
|
||||
*
|
||||
@@ -1486,12 +1523,12 @@ public:
|
||||
/**
|
||||
* @brief Iterates components and applies the given function object to them.
|
||||
*
|
||||
* The function object is provided with a const reference to each component
|
||||
* of the view.<br/>
|
||||
* The function object is provided with a reference to each component of the
|
||||
* view.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const Component &);
|
||||
* void(Component &);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
@@ -1507,6 +1544,345 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief Runtime view.
|
||||
*
|
||||
* Runtime views iterate over those entities that have at least all the given
|
||||
* components in their bags. During initialization, a runtime view looks at the
|
||||
* number of entities available for each component and picks up a reference to
|
||||
* the smallest set of candidate entities in order to get a performance boost
|
||||
* when iterate.<br/>
|
||||
* Order of elements during iterations are highly dependent on the order of the
|
||||
* underlying data structures. See SparseSet and its specializations for more
|
||||
* details.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @note
|
||||
* Views share references to the underlying data structures with the Registry
|
||||
* that generated them. Therefore any change to the entities and to the
|
||||
* components made by means of the registry are immediately reflected by views,
|
||||
* unless a pool wasn't missing when the view was built (in this case, the view
|
||||
* won't have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must overcome the one of the registry that generated it.
|
||||
* In any other case, attempting to use a view results in undefined behavior.
|
||||
*
|
||||
* @sa View
|
||||
* @sa View<Entity, Component>
|
||||
* @sa PersistentView
|
||||
* @sa RawView
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class RuntimeView {
|
||||
/*! @brief A registry is allowed to create views. */
|
||||
friend class Registry<Entity>;
|
||||
|
||||
using view_type = SparseSet<Entity>;
|
||||
using underlying_iterator_type = typename view_type::const_iterator_type;
|
||||
using pattern_type = std::vector<const view_type *>;
|
||||
using extent_type = typename view_type::size_type;
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
class Iterator {
|
||||
friend class RuntimeView<Entity>;
|
||||
|
||||
Iterator(underlying_iterator_type begin, underlying_iterator_type end, const view_type * const *first, const view_type * const *last, extent_type extent) ENTT_NOEXCEPT
|
||||
: begin{begin},
|
||||
end{end},
|
||||
first{first},
|
||||
last{last},
|
||||
extent{extent}
|
||||
{
|
||||
if(begin != end && !valid()) {
|
||||
++(*this);
|
||||
}
|
||||
}
|
||||
|
||||
bool valid() const ENTT_NOEXCEPT {
|
||||
const auto entity = *begin;
|
||||
const auto sz = size_type(entity & traits_type::entity_mask);
|
||||
|
||||
return sz < extent && std::all_of(first, last, [entity](const auto *view) {
|
||||
return view->fast(entity);
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
using difference_type = typename underlying_iterator_type::difference_type;
|
||||
using value_type = typename underlying_iterator_type::value_type;
|
||||
using pointer = typename underlying_iterator_type::pointer;
|
||||
using reference = typename underlying_iterator_type::reference;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
Iterator() ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
Iterator & operator=(const Iterator &) ENTT_NOEXCEPT = default;
|
||||
|
||||
Iterator & operator++() ENTT_NOEXCEPT {
|
||||
return (++begin != end && !valid()) ? ++(*this) : *this;
|
||||
}
|
||||
|
||||
Iterator operator++(int) ENTT_NOEXCEPT {
|
||||
Iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
bool operator==(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return other.begin == begin;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
pointer operator->() const ENTT_NOEXCEPT {
|
||||
return begin.operator->();
|
||||
}
|
||||
|
||||
inline reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
private:
|
||||
underlying_iterator_type begin;
|
||||
underlying_iterator_type end;
|
||||
const view_type * const *first;
|
||||
const view_type * const *last;
|
||||
extent_type extent;
|
||||
};
|
||||
|
||||
RuntimeView(pattern_type others) ENTT_NOEXCEPT
|
||||
: pools{std::move(others)}
|
||||
{
|
||||
const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) {
|
||||
return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size());
|
||||
});
|
||||
|
||||
// brings the best candidate (if any) on front of the vector
|
||||
std::rotate(pools.begin(), it, pools.end());
|
||||
}
|
||||
|
||||
extent_type min() const ENTT_NOEXCEPT {
|
||||
extent_type extent{};
|
||||
|
||||
if(valid()) {
|
||||
const auto it = std::min_element(pools.cbegin(), pools.cend(), [](const auto *lhs, const auto *rhs) {
|
||||
return lhs->extent() < rhs->extent();
|
||||
});
|
||||
|
||||
extent = (*it)->extent();
|
||||
}
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
inline bool valid() const ENTT_NOEXCEPT {
|
||||
return !pools.empty() && pools.front();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename view_type::entity_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename view_type::size_type;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator_type = Iterator;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator_type = Iterator;
|
||||
|
||||
/**
|
||||
* @brief Estimates the number of entities that have the given components.
|
||||
* @return Estimated number of entities that have the given components.
|
||||
*/
|
||||
size_type size() const ENTT_NOEXCEPT {
|
||||
return valid() ? pools.front()->size() : size_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the view is definitely empty.
|
||||
* @return True if the view is definitely empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
return !valid() || pools.front()->empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
const_iterator_type cbegin() const ENTT_NOEXCEPT {
|
||||
const_iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
const auto * const *data = pools.data();
|
||||
it = { pool.cbegin(), pool.cend(), data + 1, data + pools.size(), min() };
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline const_iterator_type begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* components.
|
||||
*
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
inline iterator_type begin() ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
const_iterator_type cend() const ENTT_NOEXCEPT {
|
||||
const_iterator_type it{};
|
||||
|
||||
if(valid()) {
|
||||
const auto &pool = *pools.front();
|
||||
it = { pool.cend(), pool.cend(), nullptr, nullptr, min() };
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline const_iterator_type end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @note
|
||||
* Input iterators stay true to the order imposed to the underlying data
|
||||
* structures.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given components.
|
||||
*/
|
||||
inline iterator_type end() ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a view contains an entity.
|
||||
* @param entity A valid entity identifier.
|
||||
* @return True if the view contains the given entity, false otherwise.
|
||||
*/
|
||||
bool contains(const entity_type entity) const ENTT_NOEXCEPT {
|
||||
return valid() && std::all_of(pools.cbegin(), pools.cend(), [entity](const auto *view) {
|
||||
return view->has(entity) && view->data()[view->get(entity)] == entity;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided only with
|
||||
* the entity itself. To get the components, users can use the registry with
|
||||
* which the view was built.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
std::for_each(cbegin(), cend(), func);
|
||||
}
|
||||
|
||||
private:
|
||||
pattern_type pools;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
#include "core/family.hpp"
|
||||
#include "core/hashed_string.hpp"
|
||||
#include "core/ident.hpp"
|
||||
#include "core/monostate.hpp"
|
||||
#include "entity/actor.hpp"
|
||||
#include "entity/entity.hpp"
|
||||
#include "entity/entt_traits.hpp"
|
||||
#include "entity/helper.hpp"
|
||||
#include "entity/prototype.hpp"
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
@@ -43,31 +42,27 @@ namespace entt {
|
||||
*/
|
||||
template<typename Delta>
|
||||
class Scheduler final {
|
||||
template<typename T>
|
||||
struct type_t { using type = T; };
|
||||
|
||||
struct ProcessHandler final {
|
||||
using instance_type = std::unique_ptr<void, void(*)(void *)>;
|
||||
using update_type = bool(*)(ProcessHandler &, Delta, void *);
|
||||
using abort_type = void(*)(ProcessHandler &, bool);
|
||||
using update_fn_type = bool(ProcessHandler &, Delta, void *);
|
||||
using abort_fn_type = void(ProcessHandler &, bool);
|
||||
using next_type = std::unique_ptr<ProcessHandler>;
|
||||
|
||||
instance_type instance;
|
||||
update_type update;
|
||||
abort_type abort;
|
||||
update_fn_type *update;
|
||||
abort_fn_type *abort;
|
||||
next_type next;
|
||||
};
|
||||
|
||||
template<typename Lambda>
|
||||
struct Then final: Lambda {
|
||||
Then(Lambda &&lambda, ProcessHandler *handler)
|
||||
: Lambda{std::forward<Lambda>(lambda)}, handler{handler}
|
||||
struct Then final {
|
||||
Then(ProcessHandler *handler)
|
||||
: handler{handler}
|
||||
{}
|
||||
|
||||
template<typename Proc, typename... Args>
|
||||
decltype(auto) then(Args &&... args) && {
|
||||
static_assert(std::is_base_of<Process<Proc, Delta>, Proc>::value, "!");
|
||||
handler = Lambda::operator()(handler, type_t<Proc>{}, std::forward<Args>(args)...);
|
||||
handler = Scheduler::then<Proc>(handler, std::forward<Args>(args)...);
|
||||
return std::move(*this);
|
||||
}
|
||||
|
||||
@@ -110,20 +105,15 @@ class Scheduler final {
|
||||
delete static_cast<Proc *>(proc);
|
||||
}
|
||||
|
||||
auto then(ProcessHandler *handler) {
|
||||
auto lambda = [](ProcessHandler *handler, auto next, auto... args) {
|
||||
using Proc = typename decltype(next)::type;
|
||||
template<typename Proc, typename... Args>
|
||||
static auto then(ProcessHandler *handler, Args &&... args) {
|
||||
if(handler) {
|
||||
auto proc = typename ProcessHandler::instance_type{new Proc{std::forward<Args>(args)...}, &Scheduler::deleter<Proc>};
|
||||
handler->next.reset(new ProcessHandler{std::move(proc), &Scheduler::update<Proc>, &Scheduler::abort<Proc>, nullptr});
|
||||
handler = handler->next.get();
|
||||
}
|
||||
|
||||
if(handler) {
|
||||
auto proc = typename ProcessHandler::instance_type{new Proc{std::forward<decltype(args)>(args)...}, &Scheduler::deleter<Proc>};
|
||||
handler->next.reset(new ProcessHandler{std::move(proc), &Scheduler::update<Proc>, &Scheduler::abort<Proc>, nullptr});
|
||||
handler = handler->next.get();
|
||||
}
|
||||
|
||||
return handler;
|
||||
};
|
||||
|
||||
return Then<decltype(lambda)>{std::move(lambda), handler};
|
||||
return handler;
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -202,7 +192,7 @@ public:
|
||||
ProcessHandler handler{std::move(proc), &Scheduler::update<Proc>, &Scheduler::abort<Proc>, nullptr};
|
||||
handlers.push_back(std::move(handler));
|
||||
|
||||
return then(&handlers.back());
|
||||
return Then{&handlers.back()};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
inline const Resource & operator *() const ENTT_NOEXCEPT { return get(); }
|
||||
|
||||
/**
|
||||
* @brief Gets a pointer to the managed resource from a handle .
|
||||
* @brief Gets a pointer to the managed resource from a handle.
|
||||
*
|
||||
* @warning
|
||||
* The behavior is undefined if the handle doesn't contain a resource.<br/>
|
||||
|
||||
@@ -34,10 +34,8 @@ class Delegate;
|
||||
*/
|
||||
template<typename Ret, typename... Args>
|
||||
class Delegate<Ret(Args...)> final {
|
||||
using proto_type = Ret(*)(void *, Args...);
|
||||
using stub_type = std::pair<void *, proto_type>;
|
||||
|
||||
static Ret fallback(void *, Args...) ENTT_NOEXCEPT { return {}; }
|
||||
using proto_fn_type = Ret(void *, Args...);
|
||||
using stub_type = std::pair<void *, proto_fn_type *>;
|
||||
|
||||
template<Ret(*Function)(Args...)>
|
||||
static Ret proto(void *, Args... args) {
|
||||
@@ -52,9 +50,18 @@ class Delegate<Ret(Args...)> final {
|
||||
public:
|
||||
/*! @brief Default constructor. */
|
||||
Delegate() ENTT_NOEXCEPT
|
||||
: stub{std::make_pair(nullptr, &fallback)}
|
||||
: stub{}
|
||||
{}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a delegate actually stores a listener.
|
||||
* @return True if the delegate is empty, false otherwise.
|
||||
*/
|
||||
bool empty() const ENTT_NOEXCEPT {
|
||||
// no need to test also stub.first
|
||||
return !stub.second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Binds a free function to a delegate.
|
||||
* @tparam Function A valid free function pointer.
|
||||
@@ -86,7 +93,7 @@ public:
|
||||
* After a reset, a delegate can be safely invoked with no effect.
|
||||
*/
|
||||
void reset() ENTT_NOEXCEPT {
|
||||
stub = std::make_pair(nullptr, &fallback);
|
||||
stub.second = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,12 +26,12 @@ struct Invoker;
|
||||
|
||||
template<typename Ret, typename... Args, typename Collector>
|
||||
struct Invoker<Ret(Args...), Collector> {
|
||||
using proto_type = Ret(*)(void *, Args...);
|
||||
using call_type = std::pair<void *, proto_type>;
|
||||
using proto_fn_type = Ret(void *, Args...);
|
||||
using call_type = std::pair<void *, proto_fn_type *>;
|
||||
|
||||
virtual ~Invoker() = default;
|
||||
|
||||
bool invoke(Collector &collector, proto_type proto, void *instance, Args... args) const {
|
||||
bool invoke(Collector &collector, proto_fn_type *proto, void *instance, Args... args) const {
|
||||
return collector(proto(instance, args...));
|
||||
}
|
||||
};
|
||||
@@ -39,12 +39,12 @@ struct Invoker<Ret(Args...), Collector> {
|
||||
|
||||
template<typename... Args, typename Collector>
|
||||
struct Invoker<void(Args...), Collector> {
|
||||
using proto_type = void(*)(void *, Args...);
|
||||
using call_type = std::pair<void *, proto_type>;
|
||||
using proto_fn_type = void(void *, Args...);
|
||||
using call_type = std::pair<void *, proto_fn_type *>;
|
||||
|
||||
virtual ~Invoker() = default;
|
||||
|
||||
bool invoke(Collector &, proto_type proto, void *instance, Args... args) const {
|
||||
bool invoke(Collector &, proto_fn_type *proto, void *instance, Args... args) const {
|
||||
return (proto(instance, args...), true);
|
||||
}
|
||||
};
|
||||
@@ -132,8 +132,8 @@ class Sink<Ret(Args...)> final {
|
||||
template<typename, typename>
|
||||
friend class SigH;
|
||||
|
||||
using proto_type = Ret(*)(void *, Args...);
|
||||
using call_type = std::pair<void *, proto_type>;
|
||||
using proto_fn_type = Ret(void *, Args...);
|
||||
using call_type = std::pair<void *, proto_fn_type *>;
|
||||
|
||||
template<Ret(*Function)(Args...)>
|
||||
static Ret proto(void *, Args... args) {
|
||||
@@ -145,7 +145,7 @@ class Sink<Ret(Args...)> final {
|
||||
return (static_cast<Class *>(instance)->*Member)(args...);
|
||||
}
|
||||
|
||||
Sink(std::vector<call_type> &calls)
|
||||
Sink(std::vector<call_type> &calls) ENTT_NOEXCEPT
|
||||
: calls{calls}
|
||||
{}
|
||||
|
||||
@@ -295,7 +295,7 @@ public:
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
sink_type sink() {
|
||||
sink_type sink() ENTT_NOEXCEPT {
|
||||
return { calls };
|
||||
}
|
||||
|
||||
|
||||
@@ -2,80 +2,93 @@
|
||||
# Tests configuration
|
||||
#
|
||||
|
||||
add_library(odr OBJECT odr.cpp)
|
||||
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
add_compile_options($<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_OPTIONS>)
|
||||
|
||||
macro(ADD_ENTT_TEST TEST_NAME TEST_SOURCE)
|
||||
add_library(odr OBJECT odr.cpp)
|
||||
set_target_properties(odr PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_compile_definitions(odr PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
|
||||
target_compile_features(odr PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
|
||||
target_compile_options(odr PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
|
||||
|
||||
macro(SETUP_AND_ADD_TEST TEST_NAME TEST_SOURCE)
|
||||
add_executable(${TEST_NAME} $<TARGET_OBJECTS:odr> ${TEST_SOURCE})
|
||||
target_link_libraries(${TEST_NAME} PRIVATE gtest_main Threads::Threads)
|
||||
set_target_properties(${TEST_NAME} PROPERTIES CXX_EXTENSIONS OFF)
|
||||
target_link_libraries(${TEST_NAME} PRIVATE EnTT GTest::Main Threads::Threads)
|
||||
target_compile_definitions(${TEST_NAME} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_DEFINITIONS>)
|
||||
target_compile_features(${TEST_NAME} PRIVATE $<TARGET_PROPERTY:EnTT,INTERFACE_COMPILE_FEATURES>)
|
||||
target_compile_options(${TEST_NAME} PRIVATE $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-pedantic -Wall>)
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
endmacro()
|
||||
|
||||
# Test benchmark
|
||||
|
||||
if(BUILD_BENCHMARK)
|
||||
ADD_ENTT_TEST(benchmark benchmark/benchmark.cpp)
|
||||
SETUP_AND_ADD_TEST(benchmark benchmark/benchmark.cpp)
|
||||
endif()
|
||||
|
||||
# Test mod
|
||||
|
||||
if(BUILD_MOD)
|
||||
set(DUKTAPE_DEPS_DIR ${entt_SOURCE_DIR}/deps/duktape)
|
||||
configure_file(${entt_SOURCE_DIR}/cmake/in/duktape.in ${DUKTAPE_DEPS_DIR}/CMakeLists.txt)
|
||||
set(DUKTAPE_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/duktape)
|
||||
configure_file(${EnTT_SOURCE_DIR}/cmake/in/duktape.in ${DUKTAPE_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${DUKTAPE_DEPS_DIR})
|
||||
set(DUKTAPE_SRC_DIR ${DUKTAPE_DEPS_DIR}/src/src)
|
||||
|
||||
set(MOD_TEST_SOURCE ${DUKTAPE_SRC_DIR}/duktape.c mod/mod.cpp)
|
||||
ADD_ENTT_TEST(mod ${MOD_TEST_SOURCE})
|
||||
SETUP_AND_ADD_TEST(mod "${MOD_TEST_SOURCE}")
|
||||
target_include_directories(mod PRIVATE ${DUKTAPE_SRC_DIR})
|
||||
endif()
|
||||
|
||||
# Test snapshot
|
||||
|
||||
if(BUILD_SNAPSHOT)
|
||||
set(CEREAL_DEPS_DIR ${entt_SOURCE_DIR}/deps/cereal)
|
||||
configure_file(${entt_SOURCE_DIR}/cmake/in/cereal.in ${CEREAL_DEPS_DIR}/CMakeLists.txt)
|
||||
set(CEREAL_DEPS_DIR ${EnTT_SOURCE_DIR}/deps/cereal)
|
||||
configure_file(${EnTT_SOURCE_DIR}/cmake/in/cereal.in ${CEREAL_DEPS_DIR}/CMakeLists.txt)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${CEREAL_DEPS_DIR})
|
||||
set(CEREAL_SRC_DIR ${CEREAL_DEPS_DIR}/src/include)
|
||||
|
||||
ADD_ENTT_TEST(cereal snapshot/snapshot.cpp)
|
||||
SETUP_AND_ADD_TEST(cereal snapshot/snapshot.cpp)
|
||||
target_include_directories(cereal PRIVATE ${CEREAL_SRC_DIR})
|
||||
endif()
|
||||
|
||||
# Test core
|
||||
|
||||
ADD_ENTT_TEST(algorithm entt/core/algorithm.cpp)
|
||||
ADD_ENTT_TEST(family entt/core/family.cpp)
|
||||
ADD_ENTT_TEST(hashed_string entt/core/hashed_string.cpp)
|
||||
ADD_ENTT_TEST(ident entt/core/ident.cpp)
|
||||
SETUP_AND_ADD_TEST(algorithm entt/core/algorithm.cpp)
|
||||
SETUP_AND_ADD_TEST(family entt/core/family.cpp)
|
||||
SETUP_AND_ADD_TEST(hashed_string entt/core/hashed_string.cpp)
|
||||
SETUP_AND_ADD_TEST(ident entt/core/ident.cpp)
|
||||
SETUP_AND_ADD_TEST(monostate entt/core/monostate.cpp)
|
||||
|
||||
# Test entity
|
||||
|
||||
ADD_ENTT_TEST(actor entt/entity/actor.cpp)
|
||||
ADD_ENTT_TEST(helper entt/entity/helper.cpp)
|
||||
ADD_ENTT_TEST(prototype entt/entity/prototype.cpp)
|
||||
ADD_ENTT_TEST(registry entt/entity/registry.cpp)
|
||||
ADD_ENTT_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
ADD_ENTT_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
ADD_ENTT_TEST(view entt/entity/view.cpp)
|
||||
SETUP_AND_ADD_TEST(actor entt/entity/actor.cpp)
|
||||
SETUP_AND_ADD_TEST(entity entt/entity/entity.cpp)
|
||||
SETUP_AND_ADD_TEST(helper entt/entity/helper.cpp)
|
||||
SETUP_AND_ADD_TEST(prototype entt/entity/prototype.cpp)
|
||||
SETUP_AND_ADD_TEST(registry entt/entity/registry.cpp)
|
||||
SETUP_AND_ADD_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
SETUP_AND_ADD_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
SETUP_AND_ADD_TEST(view entt/entity/view.cpp)
|
||||
|
||||
# Test locator
|
||||
|
||||
ADD_ENTT_TEST(locator entt/locator/locator.cpp)
|
||||
SETUP_AND_ADD_TEST(locator entt/locator/locator.cpp)
|
||||
|
||||
# Test process
|
||||
|
||||
ADD_ENTT_TEST(process entt/process/process.cpp)
|
||||
ADD_ENTT_TEST(scheduler entt/process/scheduler.cpp)
|
||||
SETUP_AND_ADD_TEST(process entt/process/process.cpp)
|
||||
SETUP_AND_ADD_TEST(scheduler entt/process/scheduler.cpp)
|
||||
|
||||
# Test resource
|
||||
|
||||
ADD_ENTT_TEST(resource entt/resource/resource.cpp)
|
||||
SETUP_AND_ADD_TEST(resource entt/resource/resource.cpp)
|
||||
|
||||
# Test signal
|
||||
|
||||
ADD_ENTT_TEST(delegate entt/signal/delegate.cpp)
|
||||
ADD_ENTT_TEST(dispatcher entt/signal/dispatcher.cpp)
|
||||
ADD_ENTT_TEST(emitter entt/signal/emitter.cpp)
|
||||
ADD_ENTT_TEST(sigh entt/signal/sigh.cpp)
|
||||
SETUP_AND_ADD_TEST(delegate entt/signal/delegate.cpp)
|
||||
SETUP_AND_ADD_TEST(dispatcher entt/signal/dispatcher.cpp)
|
||||
SETUP_AND_ADD_TEST(emitter entt/signal/emitter.cpp)
|
||||
SETUP_AND_ADD_TEST(sigh entt/signal/sigh.cpp)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <iterator>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
@@ -135,6 +136,31 @@ TEST(Benchmark, IterateSingleComponentRaw1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateSingleComponentRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, one component, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
@@ -242,6 +268,94 @@ TEST(Benchmark, IterateTwoComponentsPersistent1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
registry.prepare<Position, Velocity>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
registry.assign<Velocity>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, half of the entities have all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTwoComponentsRuntime1MOne) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, two components, only one entity has all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
|
||||
if(i == 5000000L) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = { registry.type<Position>(), registry.type<Velocity>() };
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
@@ -361,6 +475,130 @@ TEST(Benchmark, IterateFiveComponentsPersistent1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, half of the entities have all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateFiveComponentsRuntime1MOne) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, five components, only one entity has all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
|
||||
if(i == 5000000L) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponents1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
@@ -500,6 +738,175 @@ TEST(Benchmark, IterateTenComponentsPersistent1M) {
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponentsRuntime1M) {
|
||||
entt::DefaultRegistry registry;
|
||||
registry.prepare<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>();
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Position>(entity);
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
registry.assign<Comp<4>>(entity);
|
||||
registry.assign<Comp<5>>(entity);
|
||||
registry.assign<Comp<6>>(entity);
|
||||
registry.assign<Comp<7>>(entity);
|
||||
registry.assign<Comp<8>>(entity);
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>(),
|
||||
registry.type<Comp<4>>(),
|
||||
registry.type<Comp<5>>(),
|
||||
registry.type<Comp<6>>(),
|
||||
registry.type<Comp<7>>(),
|
||||
registry.type<Comp<8>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
registry.get<Comp<4>>(entity).x = {};
|
||||
registry.get<Comp<5>>(entity).x = {};
|
||||
registry.get<Comp<6>>(entity).x = {};
|
||||
registry.get<Comp<7>>(entity).x = {};
|
||||
registry.get<Comp<8>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponentsRuntime1MHalf) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, half of the entities have all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
registry.assign<Comp<4>>(entity);
|
||||
registry.assign<Comp<5>>(entity);
|
||||
registry.assign<Comp<6>>(entity);
|
||||
registry.assign<Comp<7>>(entity);
|
||||
registry.assign<Comp<8>>(entity);
|
||||
|
||||
if(i % 2) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>(),
|
||||
registry.type<Comp<4>>(),
|
||||
registry.type<Comp<5>>(),
|
||||
registry.type<Comp<6>>(),
|
||||
registry.type<Comp<7>>(),
|
||||
registry.type<Comp<8>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
registry.get<Comp<4>>(entity).x = {};
|
||||
registry.get<Comp<5>>(entity).x = {};
|
||||
registry.get<Comp<6>>(entity).x = {};
|
||||
registry.get<Comp<7>>(entity).x = {};
|
||||
registry.get<Comp<8>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, IterateTenComponentsRuntime1MOne) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
std::cout << "Iterating over 1000000 entities, ten components, only one entity has all the components, runtime view" << std::endl;
|
||||
|
||||
for(std::uint64_t i = 0; i < 1000000L; i++) {
|
||||
const auto entity = registry.create();
|
||||
registry.assign<Velocity>(entity);
|
||||
registry.assign<Comp<1>>(entity);
|
||||
registry.assign<Comp<2>>(entity);
|
||||
registry.assign<Comp<3>>(entity);
|
||||
registry.assign<Comp<4>>(entity);
|
||||
registry.assign<Comp<5>>(entity);
|
||||
registry.assign<Comp<6>>(entity);
|
||||
registry.assign<Comp<7>>(entity);
|
||||
registry.assign<Comp<8>>(entity);
|
||||
|
||||
if(i == 5000000L) {
|
||||
registry.assign<Position>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
auto test = [®istry](auto func) {
|
||||
using component_type = typename entt::DefaultRegistry::component_type;
|
||||
component_type types[] = {
|
||||
registry.type<Position>(),
|
||||
registry.type<Velocity>(),
|
||||
registry.type<Comp<1>>(),
|
||||
registry.type<Comp<2>>(),
|
||||
registry.type<Comp<3>>(),
|
||||
registry.type<Comp<4>>(),
|
||||
registry.type<Comp<5>>(),
|
||||
registry.type<Comp<6>>(),
|
||||
registry.type<Comp<7>>(),
|
||||
registry.type<Comp<8>>()
|
||||
};
|
||||
|
||||
Timer timer;
|
||||
registry.view(std::begin(types), std::end(types)).each(func);
|
||||
timer.elapsed();
|
||||
};
|
||||
|
||||
test([](auto) {});
|
||||
test([®istry](auto entity) {
|
||||
registry.get<Position>(entity).x = {};
|
||||
registry.get<Velocity>(entity).x = {};
|
||||
registry.get<Comp<1>>(entity).x = {};
|
||||
registry.get<Comp<2>>(entity).x = {};
|
||||
registry.get<Comp<3>>(entity).x = {};
|
||||
registry.get<Comp<4>>(entity).x = {};
|
||||
registry.get<Comp<5>>(entity).x = {};
|
||||
registry.get<Comp<6>>(entity).x = {};
|
||||
registry.get<Comp<7>>(entity).x = {};
|
||||
registry.get<Comp<8>>(entity).x = {};
|
||||
});
|
||||
}
|
||||
|
||||
TEST(Benchmark, SortSingle) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ TEST(Algorithm, StdSort) {
|
||||
|
||||
sort(arr.begin(), arr.end());
|
||||
|
||||
for(auto i = 0; i < 4; ++i) {
|
||||
for(typename decltype(arr)::size_type i = 0; i < (arr.size() - 1); ++i) {
|
||||
ASSERT_LT(arr[i], arr[i+1]);
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,21 @@ TEST(Algorithm, InsertionSort) {
|
||||
|
||||
sort(arr.begin(), arr.end());
|
||||
|
||||
for(auto i = 0; i < 4; ++i) {
|
||||
for(typename decltype(arr)::size_type i = 0; i < (arr.size() - 1); ++i) {
|
||||
ASSERT_LT(arr[i], arr[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Algorithm, OneShotBubbleSort) {
|
||||
std::array<int, 5> arr{{4, 1, 3, 2, 0}};
|
||||
entt::OneShotBubbleSort sort;
|
||||
|
||||
sort(arr.begin(), arr.end());
|
||||
sort(arr.begin(), arr.end());
|
||||
sort(arr.begin(), arr.end());
|
||||
sort(arr.begin(), arr.end());
|
||||
|
||||
for(typename decltype(arr)::size_type i = 0; i < (arr.size() - 1); ++i) {
|
||||
ASSERT_LT(arr[i], arr[i+1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,7 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
|
||||
static constexpr bool ptr(const char *str) {
|
||||
using hash_type = entt::HashedString::hash_type;
|
||||
|
||||
return (static_cast<hash_type>(entt::HashedString{str}) == entt::HashedString{str}
|
||||
&& static_cast<const char *>(entt::HashedString{str}) == str
|
||||
&& entt::HashedString{str} == entt::HashedString{str}
|
||||
&& !(entt::HashedString{str} != entt::HashedString{str}));
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
static constexpr bool ref(const char (&str)[N]) {
|
||||
using hash_type = entt::HashedString::hash_type;
|
||||
|
||||
return (static_cast<hash_type>(entt::HashedString{str}) == entt::HashedString{str}
|
||||
&& static_cast<const char *>(entt::HashedString{str}) == str
|
||||
&& entt::HashedString{str} == entt::HashedString{str}
|
||||
&& !(entt::HashedString{str} != entt::HashedString{str}));
|
||||
}
|
||||
|
||||
TEST(HashedString, Constexprness) {
|
||||
// how would you test a constexpr otherwise?
|
||||
static_assert(ptr("foo"), "!");
|
||||
static_assert(ref("bar"), "!");
|
||||
ASSERT_TRUE(true);
|
||||
}
|
||||
|
||||
TEST(HashedString, Functionalities) {
|
||||
using hash_type = entt::HashedString::hash_type;
|
||||
|
||||
@@ -40,10 +14,21 @@ TEST(HashedString, Functionalities) {
|
||||
ASSERT_EQ(static_cast<const char *>(fooHs), "foo");
|
||||
ASSERT_EQ(static_cast<const char *>(barHs), bar);
|
||||
|
||||
ASSERT_TRUE(fooHs == fooHs);
|
||||
ASSERT_TRUE(fooHs != barHs);
|
||||
ASSERT_EQ(fooHs, fooHs);
|
||||
ASSERT_NE(fooHs, barHs);
|
||||
|
||||
entt::HashedString hs{"foobar"};
|
||||
|
||||
ASSERT_EQ(static_cast<hash_type>(hs), 0x85944171f73967e8);
|
||||
|
||||
ASSERT_EQ(fooHs, "foo"_hs);
|
||||
ASSERT_NE(barHs, "foo"_hs);
|
||||
}
|
||||
|
||||
TEST(HashedString, Constexprness) {
|
||||
using hash_type = entt::HashedString::hash_type;
|
||||
// how would you test a constexpr otherwise?
|
||||
(void)std::integral_constant<hash_type, entt::HashedString{"quux"}>{};
|
||||
(void)std::integral_constant<hash_type, "quux"_hs>{};
|
||||
ASSERT_TRUE(true);
|
||||
}
|
||||
|
||||
@@ -6,28 +6,27 @@ struct AType {};
|
||||
struct AnotherType {};
|
||||
|
||||
TEST(Identifier, Uniqueness) {
|
||||
constexpr auto ID = entt::ident<AType, AnotherType>;
|
||||
using ID = entt::Identifier<AType, AnotherType>;
|
||||
constexpr AType anInstance;
|
||||
constexpr AnotherType anotherInstance;
|
||||
|
||||
ASSERT_NE(ID.get<AType>(), ID.get<AnotherType>());
|
||||
ASSERT_EQ(ID.get<AType>(), ID.get<decltype(anInstance)>());
|
||||
ASSERT_NE(ID.get<AType>(), ID.get<decltype(anotherInstance)>());
|
||||
ASSERT_EQ(ID.get<AType>(), ID.get<AType>());
|
||||
ASSERT_EQ(ID.get<AnotherType>(), ID.get<AnotherType>());
|
||||
ASSERT_NE(ID::get<AType>(), ID::get<AnotherType>());
|
||||
ASSERT_EQ(ID::get<AType>(), ID::get<decltype(anInstance)>());
|
||||
ASSERT_NE(ID::get<AType>(), ID::get<decltype(anotherInstance)>());
|
||||
ASSERT_EQ(ID::get<AType>(), ID::get<AType>());
|
||||
ASSERT_EQ(ID::get<AnotherType>(), ID::get<AnotherType>());
|
||||
|
||||
// test uses in constant expressions
|
||||
switch(ID.get<AnotherType>()) {
|
||||
case ID.get<AType>():
|
||||
switch(ID::get<AnotherType>()) {
|
||||
case ID::get<AType>():
|
||||
FAIL();
|
||||
break;
|
||||
case ID.get<AnotherType>():
|
||||
case ID::get<AnotherType>():
|
||||
SUCCEED();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Identifier, SingleType) {
|
||||
constexpr auto ID = entt::ident<AType>;
|
||||
std::integral_constant<decltype(ID)::identifier_type, ID.get()> ic;
|
||||
using ID = entt::Identifier<AType>;
|
||||
std::integral_constant<ID::identifier_type, ID::get<AType>()> ic;
|
||||
(void)ic;
|
||||
}
|
||||
|
||||
20
test/entt/core/monostate.cpp
Normal file
20
test/entt/core/monostate.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
#include <entt/core/monostate.hpp>
|
||||
|
||||
TEST(Monostate, Functionalities) {
|
||||
const bool bPre = entt::Monostate<entt::HashedString{"foobar"}>{};
|
||||
const int iPre = entt::Monostate<"foobar"_hs>{};
|
||||
|
||||
ASSERT_FALSE(bPre);
|
||||
ASSERT_EQ(iPre, int{});
|
||||
|
||||
entt::Monostate<"foobar"_hs>{} = true;
|
||||
entt::Monostate<"foobar"_hs>{} = 42;
|
||||
|
||||
const bool &bPost = entt::Monostate<"foobar"_hs>{};
|
||||
const int &iPost = entt::Monostate<entt::HashedString{"foobar"}>{};
|
||||
|
||||
ASSERT_TRUE(bPost);
|
||||
ASSERT_EQ(iPost, 42);
|
||||
}
|
||||
27
test/entt/entity/entity.cpp
Normal file
27
test/entt/entity/entity.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#include <functional>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/entity.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
template<bool>
|
||||
struct S {};
|
||||
|
||||
TEST(Traits, Null) {
|
||||
entt::DefaultRegistry registry{};
|
||||
|
||||
const auto entity = registry.create();
|
||||
registry.assign<int>(entity, 42);
|
||||
|
||||
ASSERT_TRUE(~typename entt::DefaultRegistry::entity_type{} == entt::null);
|
||||
|
||||
ASSERT_TRUE(entt::null == entt::null);
|
||||
ASSERT_FALSE(entt::null != entt::null);
|
||||
|
||||
ASSERT_FALSE(entity == entt::null);
|
||||
ASSERT_FALSE(entt::null == entity);
|
||||
|
||||
ASSERT_TRUE(entity != entt::null);
|
||||
ASSERT_TRUE(entt::null != entity);
|
||||
|
||||
ASSERT_FALSE(registry.valid(entt::null));
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/core/hashed_string.hpp>
|
||||
#include <entt/entity/helper.hpp>
|
||||
#include <entt/entity/registry.hpp>
|
||||
|
||||
TEST(Dependency, Functionalities) {
|
||||
TEST(Helper, Dependency) {
|
||||
entt::DefaultRegistry registry;
|
||||
const auto entity = registry.create();
|
||||
entt::dependency<double, float>(registry.construction<int>());
|
||||
@@ -47,3 +48,28 @@ TEST(Dependency, Functionalities) {
|
||||
ASSERT_FALSE(registry.has<double>(entity));
|
||||
ASSERT_FALSE(registry.has<float>(entity));
|
||||
}
|
||||
|
||||
TEST(Helper, Label) {
|
||||
entt::DefaultRegistry registry;
|
||||
const auto entity = registry.create();
|
||||
registry.assign<entt::label<"foobar"_hs>>(entity);
|
||||
registry.assign<int>(entity, 42);
|
||||
int counter{};
|
||||
|
||||
ASSERT_FALSE(registry.has<entt::label<"barfoo"_hs>>(entity));
|
||||
ASSERT_TRUE(registry.has<entt::label<"foobar"_hs>>(entity));
|
||||
|
||||
for(auto entity: registry.view<int, entt::label<"foobar"_hs>>()) {
|
||||
(void)entity;
|
||||
++counter;
|
||||
}
|
||||
|
||||
ASSERT_NE(counter, 0);
|
||||
|
||||
for(auto entity: registry.view<entt::label<"foobar"_hs>>()) {
|
||||
(void)entity;
|
||||
--counter;
|
||||
}
|
||||
|
||||
ASSERT_EQ(counter, 0);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ TEST(Prototype, SameRegistry) {
|
||||
ASSERT_EQ(std::get<0>(prototype.get<int, char>()), 3);
|
||||
ASSERT_EQ(std::get<1>(cprototype.get<int, char>()), 'c');
|
||||
|
||||
const auto e0 = prototype();
|
||||
const auto e0 = prototype.create();
|
||||
|
||||
ASSERT_TRUE((prototype.has<int, char>()));
|
||||
ASSERT_FALSE(registry.orphan(e0));
|
||||
@@ -79,7 +79,7 @@ TEST(Prototype, OtherRegistry) {
|
||||
ASSERT_EQ(std::get<0>(prototype.get<int, char>()), 3);
|
||||
ASSERT_EQ(std::get<1>(cprototype.get<int, char>()), 'c');
|
||||
|
||||
const auto e0 = prototype(registry);
|
||||
const auto e0 = prototype.create(registry);
|
||||
|
||||
ASSERT_TRUE((prototype.has<int, char>()));
|
||||
ASSERT_FALSE(registry.orphan(e0));
|
||||
@@ -133,3 +133,21 @@ TEST(Prototype, RAII) {
|
||||
|
||||
ASSERT_TRUE(registry.empty());
|
||||
}
|
||||
|
||||
TEST(Prototype, MoveConstructionAssignment) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
entt::DefaultPrototype prototype{registry};
|
||||
prototype.set<int>(0);
|
||||
auto other{std::move(prototype)};
|
||||
const auto e0 = other();
|
||||
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{2});
|
||||
ASSERT_TRUE(registry.has<int>(e0));
|
||||
|
||||
prototype = std::move(other);
|
||||
const auto e1 = prototype();
|
||||
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_TRUE(registry.has<int>(e1));
|
||||
}
|
||||
|
||||
@@ -59,12 +59,15 @@ TEST(DefaultRegistry, Functionalities) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_EQ(registry.alive(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_NO_THROW(registry.reserve(42));
|
||||
ASSERT_NO_THROW(registry.reserve<int>(8));
|
||||
ASSERT_NO_THROW(registry.reserve<char>(8));
|
||||
ASSERT_TRUE(registry.empty());
|
||||
|
||||
ASSERT_EQ(registry.capacity(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_EQ(registry.capacity(), entt::DefaultRegistry::size_type{42});
|
||||
ASSERT_EQ(registry.capacity<int>(), entt::DefaultRegistry::size_type{8});
|
||||
ASSERT_EQ(registry.capacity<char>(), entt::DefaultRegistry::size_type{8});
|
||||
ASSERT_EQ(registry.size<int>(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_EQ(registry.size<char>(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_TRUE(registry.empty<int>());
|
||||
@@ -79,7 +82,6 @@ TEST(DefaultRegistry, Functionalities) {
|
||||
ASSERT_TRUE(registry.has<>(e0));
|
||||
ASSERT_TRUE(registry.has<>(e1));
|
||||
|
||||
ASSERT_EQ(registry.capacity(), entt::DefaultRegistry::size_type{2});
|
||||
ASSERT_EQ(registry.size<int>(), entt::DefaultRegistry::size_type{1});
|
||||
ASSERT_EQ(registry.size<char>(), entt::DefaultRegistry::size_type{1});
|
||||
ASSERT_FALSE(registry.empty<int>());
|
||||
@@ -133,13 +135,12 @@ TEST(DefaultRegistry, Functionalities) {
|
||||
ASSERT_EQ(static_cast<const entt::DefaultRegistry &>(registry).get<int>(e1), 1);
|
||||
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_EQ(registry.alive(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_FALSE(registry.empty());
|
||||
|
||||
ASSERT_EQ(registry.version(e2), entt::DefaultRegistry::version_type{0});
|
||||
ASSERT_EQ(registry.current(e2), entt::DefaultRegistry::version_type{0});
|
||||
ASSERT_EQ(registry.capacity(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_NO_THROW(registry.destroy(e2));
|
||||
ASSERT_EQ(registry.capacity(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_EQ(registry.version(e2), entt::DefaultRegistry::version_type{0});
|
||||
ASSERT_EQ(registry.current(e2), entt::DefaultRegistry::version_type{1});
|
||||
|
||||
@@ -150,12 +151,14 @@ TEST(DefaultRegistry, Functionalities) {
|
||||
ASSERT_FALSE(registry.valid(e2));
|
||||
ASSERT_FALSE(registry.fast(e2));
|
||||
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{2});
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_EQ(registry.alive(), entt::DefaultRegistry::size_type{2});
|
||||
ASSERT_FALSE(registry.empty());
|
||||
|
||||
ASSERT_NO_THROW(registry.reset());
|
||||
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_EQ(registry.size(), entt::DefaultRegistry::size_type{3});
|
||||
ASSERT_EQ(registry.alive(), entt::DefaultRegistry::size_type{0});
|
||||
ASSERT_TRUE(registry.empty());
|
||||
|
||||
const auto e3 = registry.create();
|
||||
@@ -195,6 +198,22 @@ TEST(DefaultRegistry, Functionalities) {
|
||||
ASSERT_TRUE(registry.empty<int>());
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, Identifiers) {
|
||||
entt::DefaultRegistry registry;
|
||||
const auto pre = registry.create();
|
||||
|
||||
ASSERT_EQ(pre, registry.entity(pre));
|
||||
|
||||
registry.destroy(pre);
|
||||
const auto post = registry.create();
|
||||
|
||||
ASSERT_NE(pre, post);
|
||||
ASSERT_EQ(registry.entity(pre), registry.entity(post));
|
||||
ASSERT_NE(registry.version(pre), registry.version(post));
|
||||
ASSERT_NE(registry.version(pre), registry.current(pre));
|
||||
ASSERT_EQ(registry.version(post), registry.current(post));
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, RawData) {
|
||||
entt::DefaultRegistry registry;
|
||||
const entt::DefaultRegistry &cregistry = registry;
|
||||
@@ -741,3 +760,54 @@ TEST(DefaultRegistry, TagSignals) {
|
||||
ASSERT_EQ(listener.counter, 0);
|
||||
ASSERT_EQ(listener.last, e0);
|
||||
}
|
||||
|
||||
TEST(DefaultRegistry, DestroyByTagAndComponents) {
|
||||
entt::DefaultRegistry registry;
|
||||
|
||||
const auto e0 = registry.create();
|
||||
const auto e1 = registry.create();
|
||||
const auto e2 = registry.create();
|
||||
const auto e3 = registry.create();
|
||||
|
||||
registry.assign<int>(e0);
|
||||
registry.assign<char>(e0);
|
||||
registry.assign<double>(e0);
|
||||
|
||||
registry.assign<int>(e1);
|
||||
registry.assign<char>(e1);
|
||||
|
||||
registry.assign<int>(e2);
|
||||
|
||||
registry.assign<float>(entt::tag_t{}, e3);
|
||||
|
||||
ASSERT_TRUE(registry.valid(e0));
|
||||
ASSERT_TRUE(registry.valid(e1));
|
||||
ASSERT_TRUE(registry.valid(e2));
|
||||
ASSERT_TRUE(registry.valid(e3));
|
||||
|
||||
registry.destroy<int, char, double>(entt::persistent_t{});
|
||||
|
||||
ASSERT_FALSE(registry.valid(e0));
|
||||
ASSERT_TRUE(registry.valid(e1));
|
||||
ASSERT_TRUE(registry.valid(e2));
|
||||
ASSERT_TRUE(registry.valid(e3));
|
||||
|
||||
registry.destroy<int, char>();
|
||||
|
||||
ASSERT_FALSE(registry.valid(e0));
|
||||
ASSERT_FALSE(registry.valid(e1));
|
||||
ASSERT_TRUE(registry.valid(e2));
|
||||
ASSERT_TRUE(registry.valid(e3));
|
||||
|
||||
registry.destroy<int>();
|
||||
|
||||
ASSERT_FALSE(registry.valid(e0));
|
||||
ASSERT_FALSE(registry.valid(e1));
|
||||
ASSERT_FALSE(registry.valid(e2));
|
||||
ASSERT_TRUE(registry.valid(e3));
|
||||
|
||||
registry.destroy<int>(entt::tag_t{});
|
||||
registry.destroy<char>(entt::tag_t{});
|
||||
registry.destroy<double>(entt::tag_t{});
|
||||
registry.destroy<float>(entt::tag_t{});
|
||||
}
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
#include <entt/entity/sparse_set.hpp>
|
||||
|
||||
TEST(SparseSetNoType, Functionalities) {
|
||||
entt::SparseSet<unsigned int> set;
|
||||
entt::SparseSet<std::uint64_t> set;
|
||||
const auto &cset = set;
|
||||
|
||||
ASSERT_NO_THROW(set.reserve(42));
|
||||
set.reserve(42);
|
||||
|
||||
ASSERT_EQ(set.capacity(), 42);
|
||||
ASSERT_TRUE(set.empty());
|
||||
ASSERT_EQ(set.size(), 0u);
|
||||
ASSERT_EQ(cset.begin(), cset.end());
|
||||
@@ -49,13 +51,116 @@ TEST(SparseSetNoType, Functionalities) {
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
|
||||
(void)entt::SparseSet<unsigned int>{std::move(set)};
|
||||
entt::SparseSet<unsigned int> other;
|
||||
(void)entt::SparseSet<std::uint64_t>{std::move(set)};
|
||||
entt::SparseSet<std::uint64_t> other;
|
||||
other = std::move(set);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, DataBeginEnd) {
|
||||
entt::SparseSet<unsigned int> set;
|
||||
TEST(SparseSetNoType, ElementAccess) {
|
||||
entt::SparseSet<std::uint64_t> set;
|
||||
const auto &cset = set;
|
||||
|
||||
set.construct(42);
|
||||
set.construct(3);
|
||||
|
||||
for(typename entt::SparseSet<std::uint64_t>::size_type i{}; i < set.size(); ++i) {
|
||||
ASSERT_EQ(set[i], i ? 42 : 3);
|
||||
ASSERT_EQ(cset[i], i ? 42 : 3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, Iterator) {
|
||||
using iterator_type = typename entt::SparseSet<std::uint64_t>::iterator_type;
|
||||
|
||||
entt::SparseSet<std::uint64_t> set;
|
||||
set.construct(3);
|
||||
|
||||
iterator_type end{set.begin()};
|
||||
iterator_type begin{};
|
||||
begin = set.end();
|
||||
std::swap(begin, end);
|
||||
|
||||
ASSERT_EQ(begin, set.begin());
|
||||
ASSERT_EQ(end, set.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(begin++, set.begin());
|
||||
ASSERT_EQ(begin--, set.end());
|
||||
|
||||
ASSERT_EQ(begin+1, set.end());
|
||||
ASSERT_EQ(end-1, set.begin());
|
||||
|
||||
ASSERT_EQ(++begin, set.end());
|
||||
ASSERT_EQ(--begin, set.begin());
|
||||
|
||||
ASSERT_EQ(begin += 1, set.end());
|
||||
ASSERT_EQ(begin -= 1, set.begin());
|
||||
|
||||
ASSERT_EQ(begin + (end - begin), set.end());
|
||||
ASSERT_EQ(begin - (begin - end), set.end());
|
||||
|
||||
ASSERT_EQ(end - (end - begin), set.begin());
|
||||
ASSERT_EQ(end + (begin - end), set.begin());
|
||||
|
||||
ASSERT_EQ(begin[0], *set.begin());
|
||||
|
||||
ASSERT_LT(begin, end);
|
||||
ASSERT_LE(begin, set.begin());
|
||||
|
||||
ASSERT_GT(end, begin);
|
||||
ASSERT_GE(end, set.end());
|
||||
|
||||
ASSERT_EQ(*begin, 3);
|
||||
ASSERT_EQ(*begin.operator->(), 3);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, ConstIterator) {
|
||||
using iterator_type = typename entt::SparseSet<std::uint64_t>::const_iterator_type;
|
||||
|
||||
entt::SparseSet<std::uint64_t> set;
|
||||
set.construct(3);
|
||||
|
||||
iterator_type cend{set.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = set.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, set.cbegin());
|
||||
ASSERT_EQ(cend, set.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin++, set.cbegin());
|
||||
ASSERT_EQ(cbegin--, set.cend());
|
||||
|
||||
ASSERT_EQ(cbegin+1, set.cend());
|
||||
ASSERT_EQ(cend-1, set.cbegin());
|
||||
|
||||
ASSERT_EQ(++cbegin, set.cend());
|
||||
ASSERT_EQ(--cbegin, set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin += 1, set.cend());
|
||||
ASSERT_EQ(cbegin -= 1, set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin + (cend - cbegin), set.cend());
|
||||
ASSERT_EQ(cbegin - (cbegin - cend), set.cend());
|
||||
|
||||
ASSERT_EQ(cend - (cend - cbegin), set.cbegin());
|
||||
ASSERT_EQ(cend + (cbegin - cend), set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin[0], *set.cbegin());
|
||||
|
||||
ASSERT_LT(cbegin, cend);
|
||||
ASSERT_LE(cbegin, set.cbegin());
|
||||
|
||||
ASSERT_GT(cend, cbegin);
|
||||
ASSERT_GE(cend, set.cend());
|
||||
|
||||
ASSERT_EQ(*cbegin, 3);
|
||||
ASSERT_EQ(*cbegin.operator->(), 3);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, Data) {
|
||||
entt::SparseSet<std::uint64_t> set;
|
||||
|
||||
set.construct(3);
|
||||
set.construct(12);
|
||||
@@ -68,36 +173,11 @@ TEST(SparseSetNoType, DataBeginEnd) {
|
||||
ASSERT_EQ(*(set.data() + 0u), 3u);
|
||||
ASSERT_EQ(*(set.data() + 1u), 12u);
|
||||
ASSERT_EQ(*(set.data() + 2u), 42u);
|
||||
|
||||
auto it = set.begin();
|
||||
|
||||
ASSERT_EQ(*it, 42u);
|
||||
ASSERT_EQ(*(it+1), 12u);
|
||||
ASSERT_EQ(*(it+2), 3u);
|
||||
ASSERT_EQ(it += 3, set.end());
|
||||
|
||||
auto begin = set.begin();
|
||||
auto end = set.end();
|
||||
|
||||
ASSERT_EQ(*(begin++), 42u);
|
||||
ASSERT_EQ(*(begin++), 12u);
|
||||
ASSERT_EQ(*(begin++), 3u);
|
||||
|
||||
ASSERT_EQ(begin, end);
|
||||
|
||||
auto cbegin = set.cbegin();
|
||||
auto cend = set.cend();
|
||||
|
||||
ASSERT_NE(cbegin, cend);
|
||||
ASSERT_EQ(cbegin+3, cend);
|
||||
ASSERT_NE(cbegin, cend);
|
||||
ASSERT_EQ(cbegin += 3, cend);
|
||||
ASSERT_EQ(cbegin, cend);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, RespectDisjoint) {
|
||||
entt::SparseSet<unsigned int> lhs;
|
||||
entt::SparseSet<unsigned int> rhs;
|
||||
entt::SparseSet<std::uint64_t> lhs;
|
||||
entt::SparseSet<std::uint64_t> rhs;
|
||||
const auto &clhs = lhs;
|
||||
|
||||
lhs.construct(3);
|
||||
@@ -116,8 +196,8 @@ TEST(SparseSetNoType, RespectDisjoint) {
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, RespectOverlap) {
|
||||
entt::SparseSet<unsigned int> lhs;
|
||||
entt::SparseSet<unsigned int> rhs;
|
||||
entt::SparseSet<std::uint64_t> lhs;
|
||||
entt::SparseSet<std::uint64_t> rhs;
|
||||
const auto &clhs = lhs;
|
||||
|
||||
lhs.construct(3);
|
||||
@@ -138,8 +218,8 @@ TEST(SparseSetNoType, RespectOverlap) {
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, RespectOrdered) {
|
||||
entt::SparseSet<unsigned int> lhs;
|
||||
entt::SparseSet<unsigned int> rhs;
|
||||
entt::SparseSet<std::uint64_t> lhs;
|
||||
entt::SparseSet<std::uint64_t> rhs;
|
||||
|
||||
lhs.construct(1);
|
||||
lhs.construct(2);
|
||||
@@ -178,8 +258,8 @@ TEST(SparseSetNoType, RespectOrdered) {
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, RespectReverse) {
|
||||
entt::SparseSet<unsigned int> lhs;
|
||||
entt::SparseSet<unsigned int> rhs;
|
||||
entt::SparseSet<std::uint64_t> lhs;
|
||||
entt::SparseSet<std::uint64_t> rhs;
|
||||
|
||||
lhs.construct(1);
|
||||
lhs.construct(2);
|
||||
@@ -218,8 +298,8 @@ TEST(SparseSetNoType, RespectReverse) {
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, RespectUnordered) {
|
||||
entt::SparseSet<unsigned int> lhs;
|
||||
entt::SparseSet<unsigned int> rhs;
|
||||
entt::SparseSet<std::uint64_t> lhs;
|
||||
entt::SparseSet<std::uint64_t> rhs;
|
||||
|
||||
lhs.construct(1);
|
||||
lhs.construct(2);
|
||||
@@ -257,11 +337,29 @@ TEST(SparseSetNoType, RespectUnordered) {
|
||||
ASSERT_EQ(rhs.get(5), 5u);
|
||||
}
|
||||
|
||||
TEST(SparseSetNoType, CanModifyDuringIteration) {
|
||||
entt::SparseSet<std::uint64_t> set;
|
||||
set.construct(0);
|
||||
|
||||
ASSERT_EQ(set.capacity(), entt::SparseSet<std::uint64_t>::size_type{1});
|
||||
|
||||
const auto it = set.cbegin();
|
||||
set.reserve(entt::SparseSet<std::uint64_t>::size_type{2});
|
||||
|
||||
ASSERT_EQ(set.capacity(), entt::SparseSet<std::uint64_t>::size_type{2});
|
||||
|
||||
// this should crash with asan enabled if we break the constraint
|
||||
const auto entity = *it;
|
||||
(void)entity;
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, Functionalities) {
|
||||
entt::SparseSet<unsigned int, int> set;
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
const auto &cset = set;
|
||||
|
||||
ASSERT_NO_THROW(set.reserve(42));
|
||||
set.reserve(42);
|
||||
|
||||
ASSERT_EQ(set.capacity(), 42);
|
||||
ASSERT_TRUE(set.empty());
|
||||
ASSERT_EQ(set.size(), 0u);
|
||||
ASSERT_EQ(cset.begin(), cset.end());
|
||||
@@ -302,26 +400,127 @@ TEST(SparseSetWithType, Functionalities) {
|
||||
ASSERT_FALSE(set.has(0));
|
||||
ASSERT_FALSE(set.has(42));
|
||||
|
||||
(void)entt::SparseSet<unsigned int>{std::move(set)};
|
||||
entt::SparseSet<unsigned int> other;
|
||||
(void)entt::SparseSet<std::uint64_t>{std::move(set)};
|
||||
entt::SparseSet<std::uint64_t> other;
|
||||
other = std::move(set);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ElementAccess) {
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
const auto &cset = set;
|
||||
|
||||
set.construct(42, 1);
|
||||
set.construct(3, 0);
|
||||
|
||||
for(typename entt::SparseSet<std::uint64_t, int>::size_type i{}; i < set.size(); ++i) {
|
||||
ASSERT_EQ(set[i], i);
|
||||
ASSERT_EQ(cset[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, AggregatesMustWork) {
|
||||
struct AggregateType { int value; };
|
||||
// the goal of this test is to enforce the requirements for aggregate types
|
||||
entt::SparseSet<unsigned int, AggregateType>{}.construct(0, 42);
|
||||
entt::SparseSet<std::uint64_t, AggregateType>{}.construct(0, 42);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, TypesFromStandardTemplateLibraryMustWork) {
|
||||
// see #37 - this test shouldn't crash, that's all
|
||||
entt::SparseSet<unsigned int, std::unordered_set<int>> set;
|
||||
entt::SparseSet<std::uint64_t, std::unordered_set<int>> set;
|
||||
set.construct(0).insert(42);
|
||||
set.destroy(0);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RawBeginEnd) {
|
||||
entt::SparseSet<unsigned int, int> set;
|
||||
TEST(SparseSetWithType, Iterator) {
|
||||
struct InternalType { int value; };
|
||||
|
||||
using iterator_type = typename entt::SparseSet<std::uint64_t, InternalType>::iterator_type;
|
||||
|
||||
entt::SparseSet<std::uint64_t, InternalType> set;
|
||||
set.construct(3, 42);
|
||||
|
||||
iterator_type end{set.begin()};
|
||||
iterator_type begin{};
|
||||
begin = set.end();
|
||||
std::swap(begin, end);
|
||||
|
||||
ASSERT_EQ(begin, set.begin());
|
||||
ASSERT_EQ(end, set.end());
|
||||
ASSERT_NE(begin, end);
|
||||
|
||||
ASSERT_EQ(begin++, set.begin());
|
||||
ASSERT_EQ(begin--, set.end());
|
||||
|
||||
ASSERT_EQ(begin+1, set.end());
|
||||
ASSERT_EQ(end-1, set.begin());
|
||||
|
||||
ASSERT_EQ(++begin, set.end());
|
||||
ASSERT_EQ(--begin, set.begin());
|
||||
|
||||
ASSERT_EQ(begin += 1, set.end());
|
||||
ASSERT_EQ(begin -= 1, set.begin());
|
||||
|
||||
ASSERT_EQ(begin + (end - begin), set.end());
|
||||
ASSERT_EQ(begin - (begin - end), set.end());
|
||||
|
||||
ASSERT_EQ(end - (end - begin), set.begin());
|
||||
ASSERT_EQ(end + (begin - end), set.begin());
|
||||
|
||||
ASSERT_EQ(begin[0].value, set.begin()->value);
|
||||
|
||||
ASSERT_LT(begin, end);
|
||||
ASSERT_LE(begin, set.begin());
|
||||
|
||||
ASSERT_GT(end, begin);
|
||||
ASSERT_GE(end, set.end());
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ConstIterator) {
|
||||
struct InternalType { int value; };
|
||||
|
||||
using iterator_type = typename entt::SparseSet<std::uint64_t, InternalType>::const_iterator_type;
|
||||
|
||||
entt::SparseSet<std::uint64_t, InternalType> set;
|
||||
set.construct(3, 42);
|
||||
|
||||
iterator_type cend{set.cbegin()};
|
||||
iterator_type cbegin{};
|
||||
cbegin = set.cend();
|
||||
std::swap(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin, set.cbegin());
|
||||
ASSERT_EQ(cend, set.cend());
|
||||
ASSERT_NE(cbegin, cend);
|
||||
|
||||
ASSERT_EQ(cbegin++, set.cbegin());
|
||||
ASSERT_EQ(cbegin--, set.cend());
|
||||
|
||||
ASSERT_EQ(cbegin+1, set.cend());
|
||||
ASSERT_EQ(cend-1, set.cbegin());
|
||||
|
||||
ASSERT_EQ(++cbegin, set.cend());
|
||||
ASSERT_EQ(--cbegin, set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin += 1, set.cend());
|
||||
ASSERT_EQ(cbegin -= 1, set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin + (cend - cbegin), set.cend());
|
||||
ASSERT_EQ(cbegin - (cbegin - cend), set.cend());
|
||||
|
||||
ASSERT_EQ(cend - (cend - cbegin), set.cbegin());
|
||||
ASSERT_EQ(cend + (cbegin - cend), set.cbegin());
|
||||
|
||||
ASSERT_EQ(cbegin[0].value, set.cbegin()->value);
|
||||
|
||||
ASSERT_LT(cbegin, cend);
|
||||
ASSERT_LE(cbegin, set.cbegin());
|
||||
|
||||
ASSERT_GT(cend, cbegin);
|
||||
ASSERT_GE(cend, set.cend());
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, Raw) {
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
|
||||
set.construct(3, 3);
|
||||
set.construct(12, 6);
|
||||
@@ -334,27 +533,10 @@ TEST(SparseSetWithType, RawBeginEnd) {
|
||||
ASSERT_EQ(*(set.raw() + 0u), 3);
|
||||
ASSERT_EQ(*(set.raw() + 1u), 6);
|
||||
ASSERT_EQ(*(set.raw() + 2u), 9);
|
||||
|
||||
auto begin = set.begin();
|
||||
auto end = set.end();
|
||||
|
||||
ASSERT_EQ(*(begin++), 9);
|
||||
ASSERT_EQ(*(begin++), 6);
|
||||
ASSERT_EQ(*(begin++), 3);
|
||||
ASSERT_EQ(begin, end);
|
||||
|
||||
auto cbegin = set.cbegin();
|
||||
auto cend = set.cend();
|
||||
|
||||
ASSERT_NE(cbegin, cend);
|
||||
ASSERT_EQ(cbegin+3, cend);
|
||||
ASSERT_NE(cbegin, cend);
|
||||
ASSERT_EQ(cbegin += 3, cend);
|
||||
ASSERT_EQ(cbegin, cend);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, SortOrdered) {
|
||||
entt::SparseSet<unsigned int, int> set;
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
|
||||
set.construct(12, 12);
|
||||
set.construct(42, 9);
|
||||
@@ -390,7 +572,7 @@ TEST(SparseSetWithType, SortOrdered) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, SortReverse) {
|
||||
entt::SparseSet<unsigned int, int> set;
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
|
||||
set.construct(12, 1);
|
||||
set.construct(42, 3);
|
||||
@@ -426,7 +608,7 @@ TEST(SparseSetWithType, SortReverse) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, SortUnordered) {
|
||||
entt::SparseSet<unsigned int, int> set;
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
|
||||
set.construct(12, 6);
|
||||
set.construct(42, 3);
|
||||
@@ -462,8 +644,8 @@ TEST(SparseSetWithType, SortUnordered) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectDisjoint) {
|
||||
entt::SparseSet<unsigned int, int> lhs;
|
||||
entt::SparseSet<unsigned int, int> rhs;
|
||||
entt::SparseSet<std::uint64_t, int> lhs;
|
||||
entt::SparseSet<std::uint64_t, int> rhs;
|
||||
const auto &clhs = lhs;
|
||||
|
||||
lhs.construct(3, 3);
|
||||
@@ -490,8 +672,8 @@ TEST(SparseSetWithType, RespectDisjoint) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectOverlap) {
|
||||
entt::SparseSet<unsigned int, int> lhs;
|
||||
entt::SparseSet<unsigned int, int> rhs;
|
||||
entt::SparseSet<std::uint64_t, int> lhs;
|
||||
entt::SparseSet<std::uint64_t, int> rhs;
|
||||
const auto &clhs = lhs;
|
||||
|
||||
lhs.construct(3, 3);
|
||||
@@ -520,8 +702,8 @@ TEST(SparseSetWithType, RespectOverlap) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectOrdered) {
|
||||
entt::SparseSet<unsigned int, int> lhs;
|
||||
entt::SparseSet<unsigned int, int> rhs;
|
||||
entt::SparseSet<std::uint64_t, int> lhs;
|
||||
entt::SparseSet<std::uint64_t, int> rhs;
|
||||
|
||||
lhs.construct(1, 0);
|
||||
lhs.construct(2, 0);
|
||||
@@ -566,8 +748,8 @@ TEST(SparseSetWithType, RespectOrdered) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectReverse) {
|
||||
entt::SparseSet<unsigned int, int> lhs;
|
||||
entt::SparseSet<unsigned int, int> rhs;
|
||||
entt::SparseSet<std::uint64_t, int> lhs;
|
||||
entt::SparseSet<std::uint64_t, int> rhs;
|
||||
|
||||
lhs.construct(1, 0);
|
||||
lhs.construct(2, 0);
|
||||
@@ -612,8 +794,8 @@ TEST(SparseSetWithType, RespectReverse) {
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, RespectUnordered) {
|
||||
entt::SparseSet<unsigned int, int> lhs;
|
||||
entt::SparseSet<unsigned int, int> rhs;
|
||||
entt::SparseSet<std::uint64_t, int> lhs;
|
||||
entt::SparseSet<std::uint64_t, int> rhs;
|
||||
|
||||
lhs.construct(1, 0);
|
||||
lhs.construct(2, 0);
|
||||
@@ -657,10 +839,26 @@ TEST(SparseSetWithType, RespectUnordered) {
|
||||
ASSERT_EQ(*(rhs.data() + 5u), 5u);
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, CanModifyDuringIteration) {
|
||||
entt::SparseSet<std::uint64_t, int> set;
|
||||
set.construct(0, 42);
|
||||
|
||||
ASSERT_EQ(set.capacity(), entt::SparseSet<std::uint64_t>::size_type{1});
|
||||
|
||||
const auto it = set.cbegin();
|
||||
set.reserve(entt::SparseSet<std::uint64_t>::size_type{2});
|
||||
|
||||
ASSERT_EQ(set.capacity(), entt::SparseSet<std::uint64_t>::size_type{2});
|
||||
|
||||
// this should crash with asan enabled if we break the constraint
|
||||
const auto entity = *it;
|
||||
(void)entity;
|
||||
}
|
||||
|
||||
TEST(SparseSetWithType, ReferencesGuaranteed) {
|
||||
struct InternalType { int value; };
|
||||
|
||||
entt::SparseSet<unsigned int, InternalType> set;
|
||||
entt::SparseSet<std::uint64_t, InternalType> set;
|
||||
|
||||
set.construct(0, 0);
|
||||
set.construct(1, 1);
|
||||
@@ -698,6 +896,6 @@ TEST(SparseSetWithType, MoveOnlyComponent) {
|
||||
};
|
||||
|
||||
// it's purpose is to ensure that move only components are always accepted
|
||||
entt::SparseSet<unsigned int, MoveOnlyComponent> set;
|
||||
entt::SparseSet<std::uint64_t, MoveOnlyComponent> set;
|
||||
(void)set;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,7 @@
|
||||
struct AService {};
|
||||
|
||||
struct AnotherService {
|
||||
virtual ~AnotherService() = default;
|
||||
virtual void f(bool) = 0;
|
||||
bool check{false};
|
||||
};
|
||||
|
||||
@@ -16,20 +16,23 @@ TEST(Delegate, Functionalities) {
|
||||
entt::Delegate<int(int)> mfdel;
|
||||
DelegateFunctor functor;
|
||||
|
||||
ASSERT_EQ(ffdel(42), int{});
|
||||
ASSERT_EQ(mfdel(42), int{});
|
||||
ASSERT_TRUE(ffdel.empty());
|
||||
ASSERT_TRUE(mfdel.empty());
|
||||
|
||||
ffdel.connect<&delegateFunction>();
|
||||
mfdel.connect<DelegateFunctor, &DelegateFunctor::operator()>(&functor);
|
||||
|
||||
ASSERT_FALSE(ffdel.empty());
|
||||
ASSERT_FALSE(mfdel.empty());
|
||||
|
||||
ASSERT_EQ(ffdel(3), 9);
|
||||
ASSERT_EQ(mfdel(3), 6);
|
||||
|
||||
ffdel.reset();
|
||||
mfdel.reset();
|
||||
|
||||
ASSERT_EQ(ffdel(42), int{});
|
||||
ASSERT_EQ(mfdel(42), int{});
|
||||
ASSERT_TRUE(ffdel.empty());
|
||||
ASSERT_TRUE(mfdel.empty());
|
||||
}
|
||||
|
||||
TEST(Delegate, Comparison) {
|
||||
|
||||
@@ -142,7 +142,7 @@ TEST(SigH, Functions) {
|
||||
sigh.publish(v);
|
||||
|
||||
ASSERT_FALSE(sigh.empty());
|
||||
ASSERT_EQ((entt::SigH<bool(int)>::size_type)1, sigh.size());
|
||||
ASSERT_EQ(static_cast<entt::SigH<bool(int)>::size_type>(1), sigh.size());
|
||||
ASSERT_EQ(42, v);
|
||||
|
||||
v = 0;
|
||||
@@ -150,7 +150,7 @@ TEST(SigH, Functions) {
|
||||
sigh.publish(v);
|
||||
|
||||
ASSERT_TRUE(sigh.empty());
|
||||
ASSERT_EQ((entt::SigH<bool(int)>::size_type)0, sigh.size());
|
||||
ASSERT_EQ(static_cast<entt::SigH<bool(int)>::size_type>(0), sigh.size());
|
||||
ASSERT_EQ(0, v);
|
||||
|
||||
sigh.sink().connect<&SigHListener::f>();
|
||||
@@ -166,25 +166,25 @@ TEST(SigH, Members) {
|
||||
|
||||
ASSERT_TRUE(s.k);
|
||||
ASSERT_FALSE(sigh.empty());
|
||||
ASSERT_EQ((entt::SigH<bool(int)>::size_type)1, sigh.size());
|
||||
ASSERT_EQ(static_cast<entt::SigH<bool(int)>::size_type>(1), sigh.size());
|
||||
|
||||
sigh.sink().disconnect<SigHListener, &SigHListener::g>(ptr);
|
||||
sigh.publish(42);
|
||||
|
||||
ASSERT_TRUE(s.k);
|
||||
ASSERT_TRUE(sigh.empty());
|
||||
ASSERT_EQ((entt::SigH<bool(int)>::size_type)0, sigh.size());
|
||||
ASSERT_EQ(static_cast<entt::SigH<bool(int)>::size_type>(0), sigh.size());
|
||||
|
||||
sigh.sink().connect<SigHListener, &SigHListener::g>(ptr);
|
||||
sigh.sink().connect<SigHListener, &SigHListener::h>(ptr);
|
||||
|
||||
ASSERT_FALSE(sigh.empty());
|
||||
ASSERT_EQ((entt::SigH<bool(int)>::size_type)2, sigh.size());
|
||||
ASSERT_EQ(static_cast<entt::SigH<bool(int)>::size_type>(2), sigh.size());
|
||||
|
||||
sigh.sink().disconnect(ptr);
|
||||
|
||||
ASSERT_TRUE(sigh.empty());
|
||||
ASSERT_EQ((entt::SigH<bool(int)>::size_type)0, sigh.size());
|
||||
ASSERT_EQ(static_cast<entt::SigH<bool(int)>::size_type>(0), sigh.size());
|
||||
}
|
||||
|
||||
TEST(SigH, Collector) {
|
||||
@@ -205,7 +205,7 @@ TEST(SigH, Collector) {
|
||||
|
||||
ASSERT_FALSE(sigh_all.empty());
|
||||
ASSERT_FALSE(collector_all.vec.empty());
|
||||
ASSERT_EQ((std::vector<int>::size_type)2, collector_all.vec.size());
|
||||
ASSERT_EQ(static_cast<std::vector<int>::size_type>(2), collector_all.vec.size());
|
||||
ASSERT_EQ(42, collector_all.vec[0]);
|
||||
ASSERT_EQ(42, collector_all.vec[1]);
|
||||
|
||||
@@ -217,6 +217,6 @@ TEST(SigH, Collector) {
|
||||
|
||||
ASSERT_FALSE(sigh_first.empty());
|
||||
ASSERT_FALSE(collector_first.vec.empty());
|
||||
ASSERT_EQ((std::vector<int>::size_type)1, collector_first.vec.size());
|
||||
ASSERT_EQ(static_cast<std::vector<int>::size_type>(1), collector_first.vec.size());
|
||||
ASSERT_EQ(42, collector_first.vec[0]);
|
||||
}
|
||||
|
||||
@@ -235,34 +235,41 @@ public:
|
||||
|
||||
duk_push_array(ctx);
|
||||
|
||||
dreg.registry.each([ctx, nargs, &pos, &dreg](auto entity) {
|
||||
auto ®istry = dreg.registry;
|
||||
auto &func = dreg.func;
|
||||
bool match = true;
|
||||
std::vector<typename entt::DefaultRegistry::component_type> components;
|
||||
std::vector<typename entt::DefaultRegistry::component_type> runtime;
|
||||
|
||||
for (duk_idx_t arg = 0; match && arg < nargs; arg++) {
|
||||
auto type = duk_require_uint(ctx, arg);
|
||||
for(duk_idx_t arg = 0; arg < nargs; arg++) {
|
||||
auto type = duk_require_uint(ctx, arg);
|
||||
|
||||
if(type < udef) {
|
||||
assert(func.find(type) != func.cend());
|
||||
match = (registry.*func[type].test)(entity);
|
||||
} else {
|
||||
const auto ctype = registry.type<DuktapeRuntime>();
|
||||
assert(func.find(ctype) != func.cend());
|
||||
match = (registry.*func[ctype].test)(entity);
|
||||
|
||||
if(match) {
|
||||
auto &components = registry.get<DuktapeRuntime>(entity).components;
|
||||
match = (components.find(type) != components.cend());
|
||||
}
|
||||
if(type < udef) {
|
||||
components.push_back(type);
|
||||
} else {
|
||||
if(runtime.empty()) {
|
||||
components.push_back(dreg.registry.type<DuktapeRuntime>());
|
||||
}
|
||||
}
|
||||
|
||||
if(match) {
|
||||
runtime.push_back(type);
|
||||
}
|
||||
}
|
||||
|
||||
auto view = dreg.registry.view(components.cbegin(), components.cend());
|
||||
|
||||
for(const auto entity: view) {
|
||||
if(runtime.empty()) {
|
||||
duk_push_uint(ctx, entity);
|
||||
duk_put_prop_index(ctx, -2, pos++);
|
||||
} else {
|
||||
const auto &components = dreg.registry.get<DuktapeRuntime>(entity).components;
|
||||
const auto match = std::all_of(runtime.cbegin(), runtime.cend(), [&components](const auto type) {
|
||||
return components.find(type) != components.cend();
|
||||
});
|
||||
|
||||
if(match) {
|
||||
duk_push_uint(ctx, entity);
|
||||
duk_put_prop_index(ctx, -2, pos++);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user