initial
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
build/
|
||||||
|
.git/
|
||||||
|
.zed/
|
||||||
|
.clangd/
|
||||||
|
compile_commands.json
|
||||||
|
*.md
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
build/
|
||||||
|
Debug/
|
||||||
|
Release/
|
||||||
|
Testing/
|
||||||
|
.cache
|
||||||
|
imgui.ini
|
||||||
|
|
||||||
|
# editors
|
||||||
|
.idea/
|
||||||
|
cmake-build-debug/
|
||||||
|
.opencode/
|
||||||
|
compile_commands.json
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
[submodule "external/tracy"]
|
||||||
|
path = external/tracy
|
||||||
|
url = https://github.com/wolfpld/tracy
|
||||||
|
[submodule "external/entt"]
|
||||||
|
path = external/entt
|
||||||
|
url = https://github.com/skypjack/entt
|
||||||
|
[submodule "external/jolt"]
|
||||||
|
path = external/jolt
|
||||||
|
url = https://github.com/jrouwe/JoltPhysics
|
||||||
|
[submodule "external/glm"]
|
||||||
|
path = external/glm
|
||||||
|
url = https://github.com/g-truc/glm
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"label": "Server",
|
||||||
|
"request": "launch",
|
||||||
|
// The program to debug. This field supports path resolution with ~ or . symbols.
|
||||||
|
"program": "./Debug/modules/server/tw_server",
|
||||||
|
// cwd: defaults to the current working directory of your project ($ZED_WORKTREE_ROOT)
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"label": "Client",
|
||||||
|
"request": "launch",
|
||||||
|
// The program to debug. This field supports path resolution with ~ or . symbols.
|
||||||
|
"program": "./Debug/modules/client/mmo_client",
|
||||||
|
"args": ["8101"],
|
||||||
|
// cwd: defaults to the current working directory of your project ($ZED_WORKTREE_ROOT)
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"label": "MockClient",
|
||||||
|
"request": "launch",
|
||||||
|
// The program to debug. This field supports path resolution with ~ or . symbols.
|
||||||
|
"program": "./Debug/modules/mock_client/mmo_mock_client",
|
||||||
|
"args": ["8101"],
|
||||||
|
// cwd: defaults to the current working directory of your project ($ZED_WORKTREE_ROOT)
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"label": "ZoneManagerTests",
|
||||||
|
"request": "launch",
|
||||||
|
// The program to debug. This field supports path resolution with ~ or . symbols.
|
||||||
|
"program": "./target/Debug/modules/server/tests/tw_zone_manager_tests",
|
||||||
|
// cwd: defaults to the current working directory of your project ($ZED_WORKTREE_ROOT)
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT",
|
||||||
|
"console": "integratedTerminal"
|
||||||
|
}, {
|
||||||
|
"adapter": "CodeLLDB",
|
||||||
|
"label": "QuicrTests2",
|
||||||
|
"request": "launch",
|
||||||
|
// The program to debug. This field supports path resolution with ~ or . symbols.
|
||||||
|
"program": "/home/martin/projects/mmo/Debug/modules/network/tests/tw_network_tests",
|
||||||
|
"args": ["[quicr2]"],
|
||||||
|
// cwd: defaults to the current working directory of your project ($ZED_WORKTREE_ROOT)
|
||||||
|
"cwd": "$ZED_WORKTREE_ROOT",
|
||||||
|
"build": {
|
||||||
|
"command": "cmake --build ./Debug -j16",
|
||||||
|
"directory": "$ZED_WORKTREE_ROOT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.26)
|
||||||
|
project(towards)
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
include(cmake/CompilerOptions.cmake)
|
||||||
|
include(cmake/Dependencies.cmake)
|
||||||
|
|
||||||
|
file(GLOB FILES
|
||||||
|
src/runtime/*.cpp
|
||||||
|
src/world/*.cpp
|
||||||
|
src/draw/*.cpp
|
||||||
|
src/draw/RenderPasses/*.cpp
|
||||||
|
src/debug/tools/*.cpp
|
||||||
|
src/io/*.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
file(GLOB HEADERS
|
||||||
|
src/runtime/*.hpp
|
||||||
|
src/world/*.hpp
|
||||||
|
src/draw/*.hpp
|
||||||
|
src/debug/tools/*.hpp
|
||||||
|
src/io/*.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} OBJECT ${FILES})
|
||||||
|
add_dependencies(${PROJECT_NAME} imgui imgui::plot tw_shaders)
|
||||||
|
target_sources(${PROJECT_NAME}
|
||||||
|
PUBLIC FILE_SET HEADERS
|
||||||
|
BASE_DIRS src
|
||||||
|
FILES ${HEADERS}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME}
|
||||||
|
PUBLIC
|
||||||
|
${PROJECT_SOURCE_DIR}/src/
|
||||||
|
${CMAKE_SOURCE_DIR}/external/implot/
|
||||||
|
${JoltPhysics_SOURCE_DIR}/..
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
PUBLIC
|
||||||
|
tw::network
|
||||||
|
tw::protocol
|
||||||
|
loft::common
|
||||||
|
loft::base
|
||||||
|
loft::render_graph
|
||||||
|
glm::glm
|
||||||
|
EnTT::EnTT
|
||||||
|
Jolt
|
||||||
|
tl::expected
|
||||||
|
Tracy::TracyClient
|
||||||
|
pqxx
|
||||||
|
pq
|
||||||
|
)
|
||||||
|
|
||||||
|
include(cmake/Modules.cmake)
|
||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
# ── build stage ──────────────────────────────────────────────────────────────
|
||||||
|
FROM ubuntu:24.04 AS builder
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
build-essential \
|
||||||
|
gcc-13 g++-13 \
|
||||||
|
cmake \
|
||||||
|
ninja-build \
|
||||||
|
git \
|
||||||
|
mold \
|
||||||
|
libvulkan-dev \
|
||||||
|
libsdl2-dev \
|
||||||
|
libprotobuf-dev protobuf-compiler \
|
||||||
|
libpq-dev \
|
||||||
|
libpqxx-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN cmake -B build -G Ninja \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DCMAKE_C_COMPILER=gcc-13 \
|
||||||
|
-DCMAKE_CXX_COMPILER=g++-13 \
|
||||||
|
-DCMAKE_PREFIX_PATH=/usr/lib/x86_64-linux-gnu \
|
||||||
|
&& cmake --build build --target tw_server -j$(nproc)
|
||||||
|
|
||||||
|
# ── runtime stage ─────────────────────────────────────────────────────────────
|
||||||
|
FROM ubuntu:24.04
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
libvulkan1 \
|
||||||
|
libsdl2-2.0-0 \
|
||||||
|
libprotobuf32t64 \
|
||||||
|
libpq5 \
|
||||||
|
libpqxx-dev \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
COPY --from=builder /src/build/modules/server/tw_server /usr/local/bin/tw_server
|
||||||
|
|
||||||
|
EXPOSE 8101/udp 8102/udp
|
||||||
|
|
||||||
|
ENTRYPOINT ["tw_server"]
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
# mmo
|
||||||
|
|
||||||
|
Game engine demo with built-in networking for multiplayer.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Dependencies:
|
||||||
|
- SDL2
|
||||||
|
- Vulkan SDK
|
||||||
|
|
||||||
|
Both need to be downloaded before building. Do not forget to set PATH on Windows.
|
||||||
|
|
||||||
|
|
||||||
|
This command will initialize the build directory: `build/`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cmake -S . -B ./build/
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build the entire project.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ cmake --build ./build
|
||||||
|
```
|
||||||
|
|
||||||
|
There are now two executables:
|
||||||
|
|
||||||
|
Server is at `build/modules/client/mmo_server`
|
||||||
|
|
||||||
|
Client is at `build/modules/client/mmo_client`
|
||||||
|
|
||||||
|
First run the server, so that client can connect. The server will try to use port 8080, but if it is already occupied, it will try use the next free higher one.
|
||||||
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
if(NOT DEFINED CMAKE_CXX_STANDARD)
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_compile_options(-fno-omit-frame-pointer)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Debug" OR NOT CMAKE_BUILD_TYPE)
|
||||||
|
add_compile_options(-g)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(USE_MOLD_LINKER "Use mold as the linker" ON)
|
||||||
|
if(USE_MOLD_LINKER)
|
||||||
|
add_link_options(-fuse-ld=mold)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(TRACY_ENABLE "Enable Tracy profiler" ON)
|
||||||
|
|
||||||
|
add_compile_definitions(
|
||||||
|
IMGUI_IMPL_VULKAN_USE_VOLK
|
||||||
|
SPDLOG_COMPILED_LIB
|
||||||
|
GLM_ENABLE_EXPERIMENTAL
|
||||||
|
TRACY_ENABLE
|
||||||
|
TRACY_CALLSTACK=1
|
||||||
|
)
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
set(EXPECTED_BUILD_TESTS OFF CACHE BOOL "Disable tl::expected tests" FORCE)
|
||||||
|
set(BUILD_EXAMPLES OFF CACHE BOOL "Disable dependency examples" FORCE)
|
||||||
|
set(LFT_ENABLE_EXAMPLES OFF CACHE BOOL "Disable loft examples" FORCE)
|
||||||
|
set(FASTNOISE2_NOISETOOL OFF CACHE BOOL "Disable FastNoise2 graph tool" FORCE)
|
||||||
|
|
||||||
|
find_package(Vulkan REQUIRED)
|
||||||
|
find_package(SDL2 REQUIRED)
|
||||||
|
find_package(Protobuf CONFIG REQUIRED)
|
||||||
|
|
||||||
|
include(FetchContent)
|
||||||
|
set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Distribution")
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
glm
|
||||||
|
GIT_REPOSITORY https://github.com/g-truc/glm
|
||||||
|
GIT_TAG 1.0.1
|
||||||
|
SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/glm/
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
entt
|
||||||
|
GIT_REPOSITORY https://github.com/skypjack/entt
|
||||||
|
GIT_TAG v3.15.0
|
||||||
|
SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/entt
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
Catch2
|
||||||
|
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||||
|
GIT_TAG v3.10.0
|
||||||
|
FIND_PACKAGE_ARGS
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
JoltPhysics
|
||||||
|
GIT_REPOSITORY https://github.com/jrouwe/JoltPhysics
|
||||||
|
GIT_TAG v5.4.0
|
||||||
|
SOURCE_DIR ${CMAKE_SOURCE_DIR}/external/jolt/
|
||||||
|
SOURCE_SUBDIR Build
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
spdlog
|
||||||
|
GIT_REPOSITORY https://github.com/gabime/spdlog
|
||||||
|
GIT_TAG v1.16.0
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
expected
|
||||||
|
GIT_REPOSITORY https://github.com/TartanLlama/expected
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
tracy
|
||||||
|
GIT_REPOSITORY https://github.com/wolfpld/tracy.git
|
||||||
|
GIT_TAG master
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
# FetchContent_Declare(
|
||||||
|
# Quadtree
|
||||||
|
# GIT_REPOSITORY https://github.com/pvigier/Quadtree.git
|
||||||
|
# GIT_SHALLOW TRUE
|
||||||
|
# )
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(glm entt Catch2 JoltPhysics spdlog expected tracy)
|
||||||
|
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/external/loft)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/external/imgui)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/external/implot)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/network/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/protocol/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/messaging/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/chat_service/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/server/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/client/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/mock_client/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/gui/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/server_gui/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/serialization/)
|
||||||
|
add_subdirectory(${CMAKE_SOURCE_DIR}/modules/peer_to_peer/)
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
services:
|
||||||
|
timescaledb:
|
||||||
|
image: timescale/timescaledb:latest-pg16
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: mmo
|
||||||
|
POSTGRES_PASSWORD: mmo
|
||||||
|
POSTGRES_DB: mmo
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
volumes:
|
||||||
|
- timescaledb_data:/var/lib/postgresql/data
|
||||||
|
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
GF_SECURITY_ADMIN_USER: admin
|
||||||
|
GF_SECURITY_ADMIN_PASSWORD: admin
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- grafana_data:/var/lib/grafana
|
||||||
|
depends_on:
|
||||||
|
- timescaledb
|
||||||
|
|
||||||
|
zone-server:
|
||||||
|
build: .
|
||||||
|
ports:
|
||||||
|
- "8101:8101/udp"
|
||||||
|
- "8102:8102/udp"
|
||||||
|
depends_on:
|
||||||
|
- timescaledb
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
timescaledb_data:
|
||||||
|
grafana_data:
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
test
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
USE_BAZEL_VERSION=6.x
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
common --enable_bzlmod
|
||||||
|
build --enable_platform_specific_config
|
||||||
|
build --incompatible_enable_cc_toolchain_resolution
|
||||||
|
build --enable_runfiles
|
||||||
|
build --incompatible_strict_action_env
|
||||||
|
|
||||||
|
# required for googletest
|
||||||
|
build:linux --cxxopt=-std=c++17
|
||||||
|
build:macos --cxxopt=-std=c++17
|
||||||
|
|
||||||
|
common:ci --announce_rc
|
||||||
|
common:ci --verbose_failures
|
||||||
|
common:ci --keep_going
|
||||||
|
test:ci --test_output=errors
|
||||||
|
|
||||||
|
try-import %workspace%/user.bazelrc
|
||||||
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
BasedOnStyle: llvm
|
||||||
|
---
|
||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignEscapedNewlines: DontAlign
|
||||||
|
AllowShortBlocksOnASingleLine: Always
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: Empty
|
||||||
|
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
ColumnLimit: 0
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '<[[:alnum:]_]+>'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '<(gtest|gmock)/'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '<[[:alnum:]_./]+>'
|
||||||
|
Priority: 3
|
||||||
|
- Regex: '<entt/'
|
||||||
|
Priority: 4
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 5
|
||||||
|
IncludeIsMainRegex: "^$"
|
||||||
|
IndentPPDirectives: AfterHash
|
||||||
|
IndentWidth: 4
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
Language: Cpp
|
||||||
|
PointerAlignment: Right
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceAroundPointerQualifiers: After
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCtorInitializerColon: false
|
||||||
|
SpaceBeforeInheritanceColon: false
|
||||||
|
SpaceBeforeParens: Never
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: false
|
||||||
|
Standard: Latest
|
||||||
|
TabWidth: 4
|
||||||
|
UseTab: Never
|
||||||
Vendored
+56
@@ -0,0 +1,56 @@
|
|||||||
|
Checks: >
|
||||||
|
bugprone-*,
|
||||||
|
clang-analyzer-*,
|
||||||
|
-clang-analyzer-optin.core.EnumCastOutOfRange,
|
||||||
|
concurrency-*,
|
||||||
|
cppcoreguidelines-*,
|
||||||
|
-cppcoreguidelines-owning-memory,
|
||||||
|
-cppcoreguidelines-pro-bounds-constant-array-index,
|
||||||
|
-cppcoreguidelines-pro-type-const-cast,
|
||||||
|
-cppcoreguidelines-pro-type-member-init,
|
||||||
|
-cppcoreguidelines-pro-type-reinterpret-cast,
|
||||||
|
-cppcoreguidelines-pro-type-union-access,
|
||||||
|
misc-*,
|
||||||
|
-misc-include-cleaner,
|
||||||
|
-misc-no-recursion,
|
||||||
|
modernize-*,
|
||||||
|
-modernize-use-trailing-return-type,
|
||||||
|
performance-*,
|
||||||
|
portability-*,
|
||||||
|
readability-*,
|
||||||
|
-readability-function-cognitive-complexity,
|
||||||
|
-readability-named-parameter,
|
||||||
|
-readability-uppercase-literal-suffix,
|
||||||
|
CheckOptions:
|
||||||
|
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
|
||||||
|
value: true
|
||||||
|
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
|
||||||
|
value: true
|
||||||
|
- key: cppcoreguidelines-rvalue-reference-param-not-moved.AllowPartialMove
|
||||||
|
value: true
|
||||||
|
- key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams
|
||||||
|
value: true
|
||||||
|
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
|
||||||
|
value: true
|
||||||
|
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
|
||||||
|
value: true
|
||||||
|
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||||
|
value: true
|
||||||
|
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
|
||||||
|
value: true
|
||||||
|
- key: misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables
|
||||||
|
value: true
|
||||||
|
- key: modernize-avoid-c-arrays.AllowStringArrays
|
||||||
|
value: true
|
||||||
|
- key: performance-enum-size.EnumIgnoreList
|
||||||
|
value: meta_traits
|
||||||
|
- key: readability-function-cognitive-complexity.IgnoreMacros
|
||||||
|
value: true
|
||||||
|
- key: readability-identifier-length.MinimumParameterNameLength
|
||||||
|
value: 2
|
||||||
|
- key: readability-identifier-length.MinimumVariableNameLength
|
||||||
|
value: 2
|
||||||
|
- key: readability-magic-numbers.IgnoreAllFloatingPointValues
|
||||||
|
value: true
|
||||||
|
- key: readability-magic-numbers.IgnorePowersOf2IntegerValues
|
||||||
|
value: true
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
|
||||||
|
github: skypjack
|
||||||
|
custom: https://www.paypal.me/skypjack
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
name: Bazel Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# A release archive is required for bzlmod
|
||||||
|
# See: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
|
||||||
|
bazel-release-archive:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
- run: go install github.com/bazelbuild/buildtools/buildozer@latest
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: ./scripts/sync_bzlmod_version.sh
|
||||||
|
- run: git archive $GITHUB_REF -o "entt-${GITHUB_REF:10}.tar.gz"
|
||||||
|
- run: gh release upload ${GITHUB_REF:10} "entt-${GITHUB_REF:10}.tar.gz"
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ github.token }}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
name: bazel
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
test:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- windows-latest
|
||||||
|
- macos-latest
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: bazelisk test --config=ci ...
|
||||||
|
working-directory: test
|
||||||
|
env:
|
||||||
|
USE_BAZEL_VERSION: 6.x
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
linux:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
compiler:
|
||||||
|
- { pkg: g++, exe: 'g++', version: 12 }
|
||||||
|
- { pkg: g++, exe: 'g++', version: 13 }
|
||||||
|
- { pkg: g++, exe: 'g++', version: 14 }
|
||||||
|
- { pkg: clang, exe: 'clang++', version: 16 }
|
||||||
|
- { pkg: clang, exe: 'clang++', version: 17 }
|
||||||
|
- { pkg: clang, exe: 'clang++', version: 18 }
|
||||||
|
|
||||||
|
timeout-minutes: 15
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install compiler
|
||||||
|
run: |
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||||
|
make -j4
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
run: ctest -C Debug -j4
|
||||||
|
|
||||||
|
windows:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
toolset: [default, v142, clang-cl]
|
||||||
|
include:
|
||||||
|
- toolset: v142
|
||||||
|
toolset_option: -T"v142"
|
||||||
|
- toolset: clang-cl
|
||||||
|
toolset_option: -T"ClangCl"
|
||||||
|
|
||||||
|
timeout-minutes: 15
|
||||||
|
runs-on: windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ${{ matrix.toolset_option }} ..
|
||||||
|
cmake --build . -j 4
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
run: ctest -C Debug -j4
|
||||||
|
|
||||||
|
macos:
|
||||||
|
timeout-minutes: 15
|
||||||
|
runs-on: macOS-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||||
|
make -j4
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
run: ctest -C Debug -j4
|
||||||
|
|
||||||
|
extra:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-latest, macOS-latest, ubuntu-latest]
|
||||||
|
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||||
|
cxx_std: [cxx_std_17, cxx_std_20]
|
||||||
|
|
||||||
|
timeout-minutes: 15
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
|
||||||
|
cmake --build . -j 4
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
run: ctest -C Debug -j4
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
name: coverage
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
codecov:
|
||||||
|
timeout-minutes: 15
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CXXFLAGS: "--coverage -fno-elide-constructors -fno-inline -fno-default-inline"
|
||||||
|
CXX: g++
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||||
|
make -j4
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
run: ctest -C Debug -j4
|
||||||
|
- name: Collect data
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
sudo apt install lcov
|
||||||
|
lcov -c -d . -o coverage.info --ignore-errors gcov,gcov,mismatch,mismatch
|
||||||
|
lcov -l coverage.info
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
files: build/coverage.info
|
||||||
|
name: EnTT
|
||||||
|
fail_ci_if_error: true
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
name: deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: published
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
homebrew-entt:
|
||||||
|
timeout-minutes: 5
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
env:
|
||||||
|
GH_REPO: homebrew-entt
|
||||||
|
FORMULA: entt.rb
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Clone repository
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
|
||||||
|
run: git clone https://$GITHUB_ACTOR:$PERSONAL_ACCESS_TOKEN@github.com/$GITHUB_ACTOR/$GH_REPO.git
|
||||||
|
- name: Prepare formula
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
cd $GH_REPO
|
||||||
|
curl "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" --location --fail --silent --show-error --output archive.tar.gz
|
||||||
|
sed -i -e '/url/s/".*"/"'$(echo "https://github.com/${{ github.repository }}/archive/${{ github.ref }}.tar.gz" | sed -e 's/[\/&]/\\&/g')'"/' $FORMULA
|
||||||
|
sed -i -e '/sha256/s/".*"/"'$(openssl sha256 archive.tar.gz | cut -d " " -f 2)'"/' $FORMULA
|
||||||
|
- name: Update remote
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
cd $GH_REPO
|
||||||
|
git config --local user.email "action@github.com"
|
||||||
|
git config --local user.name "$GITHUB_ACTOR"
|
||||||
|
git add $FORMULA
|
||||||
|
git commit -m "Update to ${{ github.ref }}"
|
||||||
|
git push origin master
|
||||||
+31
@@ -0,0 +1,31 @@
|
|||||||
|
name: sanitizer
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
clang:
|
||||||
|
timeout-minutes: 15
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
compiler: [clang++]
|
||||||
|
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||||
|
cxx_std: [cxx_std_17, cxx_std_20]
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CXX: ${{ matrix.compiler }}
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_USE_SANITIZER=ON -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} ..
|
||||||
|
make -j4
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CTEST_OUTPUT_ON_FAILURE: 1
|
||||||
|
run: ctest -C Debug -j4
|
||||||
+84
@@ -0,0 +1,84 @@
|
|||||||
|
name: tools
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- tools
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
iwyu:
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
env:
|
||||||
|
IWYU: "0.23"
|
||||||
|
LLVM: "19"
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install llvm/clang
|
||||||
|
# see: https://apt.llvm.org/
|
||||||
|
run: |
|
||||||
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
sudo add-apt-repository "deb http://apt.llvm.org/noble/ llvm-toolchain-noble-$LLVM main"
|
||||||
|
sudo apt update
|
||||||
|
sudo apt remove -y "llvm*"
|
||||||
|
sudo apt remove -y "libclang*"
|
||||||
|
sudo apt remove -y "clang*"
|
||||||
|
sudo apt install -y llvm-$LLVM-dev
|
||||||
|
sudo apt install -y libclang-$LLVM-dev
|
||||||
|
sudo apt install -y clang-$LLVM
|
||||||
|
- name: Compile iwyu
|
||||||
|
# see: https://github.com/include-what-you-use/include-what-you-use
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1
|
||||||
|
mkdir include-what-you-use/build
|
||||||
|
cd include-what-you-use/build
|
||||||
|
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
|
||||||
|
-DCMAKE_INSTALL_PREFIX=./ \
|
||||||
|
..
|
||||||
|
make -j4
|
||||||
|
bin/include-what-you-use --version
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
run: |
|
||||||
|
export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin
|
||||||
|
cmake -DCMAKE_C_COMPILER=clang-$LLVM \
|
||||||
|
-DCMAKE_CXX_COMPILER=clang++-$LLVM \
|
||||||
|
-DENTT_BUILD_TESTING=ON \
|
||||||
|
-DENTT_BUILD_BENCHMARK=ON \
|
||||||
|
-DENTT_BUILD_EXAMPLE=ON \
|
||||||
|
-DENTT_BUILD_LIB=ON \
|
||||||
|
-DENTT_BUILD_SNAPSHOT=ON \
|
||||||
|
-DENTT_BUILD_TOOLS=ON \
|
||||||
|
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
|
||||||
|
..
|
||||||
|
make -j4
|
||||||
|
|
||||||
|
clang-tidy:
|
||||||
|
timeout-minutes: 60
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
continue-on-error: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Compile tests
|
||||||
|
working-directory: build
|
||||||
|
env:
|
||||||
|
CXX: clang++
|
||||||
|
run: |
|
||||||
|
cmake -DENTT_BUILD_TESTING=ON \
|
||||||
|
-DENTT_BUILD_BENCHMARK=ON \
|
||||||
|
-DENTT_BUILD_EXAMPLE=ON \
|
||||||
|
-DENTT_BUILD_LIB=ON \
|
||||||
|
-DENTT_BUILD_SNAPSHOT=ON \
|
||||||
|
-DENTT_BUILD_TOOLS=ON \
|
||||||
|
-DENTT_USE_CLANG_TIDY=ON \
|
||||||
|
..
|
||||||
|
make -j4
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Conan
|
||||||
|
conan/test_package/build
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
*.user
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
.vs
|
||||||
|
CMakeSettings.json
|
||||||
|
cpp.hint
|
||||||
|
|
||||||
|
# Bazel
|
||||||
|
/bazel-*
|
||||||
|
/test/bazel-*
|
||||||
|
/user.bazelrc
|
||||||
|
*.bazel.lock
|
||||||
Vendored
+56
@@ -0,0 +1,56 @@
|
|||||||
|
# Author
|
||||||
|
|
||||||
|
skypjack
|
||||||
|
|
||||||
|
# Contributors
|
||||||
|
|
||||||
|
alexames
|
||||||
|
BenediktConze
|
||||||
|
bjadamson
|
||||||
|
ceeac
|
||||||
|
ColinH
|
||||||
|
corystegel
|
||||||
|
Croydon
|
||||||
|
cschreib
|
||||||
|
cugone
|
||||||
|
dbacchet
|
||||||
|
dBagrat
|
||||||
|
djarek
|
||||||
|
DNKpp
|
||||||
|
DonKult
|
||||||
|
drglove
|
||||||
|
eliasdaler
|
||||||
|
erez-o
|
||||||
|
eugeneko
|
||||||
|
gale83
|
||||||
|
ghost
|
||||||
|
grdowns
|
||||||
|
Green-Sky
|
||||||
|
Innokentiy-Alaytsev
|
||||||
|
Kerndog73
|
||||||
|
Koward
|
||||||
|
Lawrencemm
|
||||||
|
markand
|
||||||
|
mhammerc
|
||||||
|
Milerius
|
||||||
|
Minimonium
|
||||||
|
morbo84
|
||||||
|
m-waka
|
||||||
|
netpoetica
|
||||||
|
NixAJ
|
||||||
|
Oortonaut
|
||||||
|
Paolo-Oliverio
|
||||||
|
pgruenbacher
|
||||||
|
prowolf
|
||||||
|
Qix-
|
||||||
|
stefanofiorentino
|
||||||
|
suVrik
|
||||||
|
szunhammer
|
||||||
|
The5-1
|
||||||
|
vblanco20-1
|
||||||
|
willtunnels
|
||||||
|
WizardIke
|
||||||
|
WoLfulus
|
||||||
|
w1th0utnam3
|
||||||
|
xissburg
|
||||||
|
zaucy
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
alias(
|
||||||
|
name = "entt",
|
||||||
|
actual = "//src:entt",
|
||||||
|
)
|
||||||
Vendored
+330
@@ -0,0 +1,330 @@
|
|||||||
|
# EnTT
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.15.7)
|
||||||
|
|
||||||
|
# Read project version
|
||||||
|
|
||||||
|
set(ENTT_VERSION_REGEX "#define ENTT_VERSION_.*[ \t]+(.+)")
|
||||||
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/entt/config/version.h" ENTT_VERSION REGEX ${ENTT_VERSION_REGEX})
|
||||||
|
list(TRANSFORM ENTT_VERSION REPLACE ${ENTT_VERSION_REGEX} "\\1")
|
||||||
|
string(JOIN "." ENTT_VERSION ${ENTT_VERSION})
|
||||||
|
|
||||||
|
# Project configuration
|
||||||
|
|
||||||
|
project(
|
||||||
|
EnTT
|
||||||
|
VERSION ${ENTT_VERSION}
|
||||||
|
DESCRIPTION "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more"
|
||||||
|
HOMEPAGE_URL "https://github.com/skypjack/entt"
|
||||||
|
LANGUAGES C CXX
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(VERBOSE "*")
|
||||||
|
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||||
|
message(VERBOSE "* Copyright (c) 2017-2025 Michele Caini <michele.caini@gmail.com>")
|
||||||
|
message(VERBOSE "*")
|
||||||
|
|
||||||
|
# CMake stuff
|
||||||
|
|
||||||
|
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||||
|
|
||||||
|
# Compiler stuff
|
||||||
|
|
||||||
|
option(ENTT_USE_LIBCPP "Use libc++ by adding -stdlib=libc++ flag if available." OFF)
|
||||||
|
option(ENTT_USE_SANITIZER "Enable sanitizers by adding -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined flags if available." OFF)
|
||||||
|
option(ENTT_USE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF)
|
||||||
|
|
||||||
|
if(ENTT_USE_LIBCPP)
|
||||||
|
if(NOT WIN32)
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
include(CMakePushCheckState)
|
||||||
|
|
||||||
|
cmake_push_check_state()
|
||||||
|
|
||||||
|
set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -stdlib=libc++")
|
||||||
|
|
||||||
|
check_cxx_source_compiles("
|
||||||
|
#include<type_traits>
|
||||||
|
int main() { return std::is_same_v<int, char>; }
|
||||||
|
" ENTT_HAS_LIBCPP)
|
||||||
|
|
||||||
|
cmake_pop_check_state()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ENTT_HAS_LIBCPP)
|
||||||
|
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENTT_USE_SANITIZER)
|
||||||
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
|
||||||
|
set(ENTT_HAS_SANITIZER TRUE CACHE BOOL "" FORCE)
|
||||||
|
mark_as_advanced(ENTT_HAS_SANITIZER)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ENTT_HAS_SANITIZER)
|
||||||
|
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENTT_USE_CLANG_TIDY)
|
||||||
|
find_program(ENTT_CLANG_TIDY_EXECUTABLE "clang-tidy")
|
||||||
|
|
||||||
|
if(NOT ENTT_CLANG_TIDY_EXECUTABLE)
|
||||||
|
message(VERBOSE "The option ENTT_USE_CLANG_TIDY is set but clang-tidy executable is not available.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add EnTT target
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
add_library(EnTT INTERFACE)
|
||||||
|
add_library(EnTT::EnTT ALIAS EnTT)
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
EnTT
|
||||||
|
INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src>
|
||||||
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_features(EnTT INTERFACE cxx_std_17)
|
||||||
|
|
||||||
|
if(ENTT_HAS_LIBCPP)
|
||||||
|
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENTT_HAS_SANITIZER)
|
||||||
|
target_compile_options(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||||
|
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENTT_CLANG_TIDY_EXECUTABLE)
|
||||||
|
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add EnTT goodies
|
||||||
|
|
||||||
|
option(ENTT_INCLUDE_HEADERS "Add all EnTT headers to the EnTT target." OFF)
|
||||||
|
option(ENTT_INCLUDE_NATVIS "Add EnTT natvis files to the EnTT target." OFF)
|
||||||
|
|
||||||
|
if(ENTT_INCLUDE_NATVIS)
|
||||||
|
if(MSVC)
|
||||||
|
set(ENTT_HAS_NATVIS TRUE CACHE BOOL "" FORCE)
|
||||||
|
mark_as_advanced(ENTT_HAS_NATVIS)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ENTT_HAS_NATVIS)
|
||||||
|
message(VERBOSE "The option ENTT_INCLUDE_NATVIS is set but natvis files are not supported. They will not be added to the target.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENTT_INCLUDE_HEADERS)
|
||||||
|
target_sources(
|
||||||
|
EnTT
|
||||||
|
INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/config.h>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/macro.h>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/version.h>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_map.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_set.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/table.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/bit.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/hashed_string.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ident.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/iterator.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/memory.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/monostate.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ranges.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/tuple.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_info.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_traits.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/utility.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/component.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/entity.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/ranges.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/flow.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/locator/locator.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/adl_pointer.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/container.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/context.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/factory.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/meta.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/node.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/pointer.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/policy.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/range.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/resolve.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/process.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/scheduler.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/cache.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/loader.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/resource/resource.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/delegate.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/dispatcher.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/emitter.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/fwd.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/signal/sigh.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entt.hpp>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/fwd.hpp>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ENTT_HAS_NATVIS)
|
||||||
|
target_sources(
|
||||||
|
EnTT
|
||||||
|
INTERFACE
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/config.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/container.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/core.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/entity.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/graph.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
|
||||||
|
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/signal.natvis>
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Install EnTT and all related files
|
||||||
|
|
||||||
|
option(ENTT_INSTALL "Install EnTT and all related files." OFF)
|
||||||
|
|
||||||
|
if(ENTT_INSTALL)
|
||||||
|
# Install pkg-config file
|
||||||
|
|
||||||
|
include(JoinPaths)
|
||||||
|
|
||||||
|
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
|
||||||
|
|
||||||
|
join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
|
||||||
|
${EnTT_PKGCONFIG}
|
||||||
|
@ONLY
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES ${EnTT_PKGCONFIG}
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||||
|
)
|
||||||
|
|
||||||
|
# Install EnTT
|
||||||
|
|
||||||
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS EnTT
|
||||||
|
EXPORT EnTTTargets
|
||||||
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
write_basic_package_version_file(
|
||||||
|
EnTTConfigVersion.cmake
|
||||||
|
VERSION ${PROJECT_VERSION}
|
||||||
|
COMPATIBILITY AnyNewerVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_package_config_file(
|
||||||
|
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
|
||||||
|
EnTTConfig.cmake
|
||||||
|
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||||
|
)
|
||||||
|
|
||||||
|
export(
|
||||||
|
EXPORT EnTTTargets
|
||||||
|
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
|
||||||
|
NAMESPACE EnTT::
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
EXPORT EnTTTargets
|
||||||
|
FILE EnTTTargets.cmake
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||||
|
NAMESPACE EnTT::
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
FILES
|
||||||
|
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
|
||||||
|
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||||
|
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||||
|
)
|
||||||
|
|
||||||
|
install(
|
||||||
|
DIRECTORY src/
|
||||||
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||||
|
FILES_MATCHING
|
||||||
|
PATTERN "*.h"
|
||||||
|
PATTERN "*.hpp"
|
||||||
|
)
|
||||||
|
|
||||||
|
export(PACKAGE EnTT)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||||
|
|
||||||
|
if(ENTT_BUILD_TESTING)
|
||||||
|
option(ENTT_FIND_GTEST_PACKAGE "Enable finding gtest package." OFF)
|
||||||
|
|
||||||
|
option(ENTT_BUILD_BENCHMARK "Build benchmark." OFF)
|
||||||
|
option(ENTT_BUILD_EXAMPLE "Build examples." OFF)
|
||||||
|
option(ENTT_BUILD_LIB "Build lib tests." OFF)
|
||||||
|
option(ENTT_BUILD_SNAPSHOT "Build snapshot test with Cereal." OFF)
|
||||||
|
|
||||||
|
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for the tests")
|
||||||
|
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for the tests")
|
||||||
|
|
||||||
|
include(CTest)
|
||||||
|
enable_testing()
|
||||||
|
add_subdirectory(test)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||||
|
|
||||||
|
if(ENTT_BUILD_DOCS)
|
||||||
|
add_subdirectory(docs)
|
||||||
|
endif()
|
||||||
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
# Contributing
|
||||||
|
|
||||||
|
First of all, thank you very much for taking the time to contribute to the
|
||||||
|
`EnTT` library.<br/>
|
||||||
|
How to do it mostly depends on the type of contribution:
|
||||||
|
|
||||||
|
* If you have a question, **please** ensure there isn't already an answer for
|
||||||
|
you by searching on GitHub under
|
||||||
|
[issues](https://github.com/skypjack/entt/issues). Do not forget to search
|
||||||
|
also through the closed ones. If you are unable to find a proper answer, feel
|
||||||
|
free to [open a new issue](https://github.com/skypjack/entt/issues/new).
|
||||||
|
Usually, questions are marked as such and closed in a few days.
|
||||||
|
|
||||||
|
* If you want to fix a typo in the inline documentation or in the README file,
|
||||||
|
if you want to add some new sections or if you want to help me with the
|
||||||
|
language by reviewing what I wrote so far (I'm not a native speaker after
|
||||||
|
all), **please** open a new
|
||||||
|
[pull request](https://github.com/skypjack/entt/pulls) with your changes.
|
||||||
|
|
||||||
|
* If you found a bug, **please** ensure there isn't already an answer for you by
|
||||||
|
searching on GitHub under [issues](https://github.com/skypjack/entt/issues).
|
||||||
|
If you are unable to find an open issue addressing the problem, feel free to
|
||||||
|
[open a new one](https://github.com/skypjack/entt/issues/new). **Please**, do
|
||||||
|
not forget to carefully describe how to reproduce the problem, then add all
|
||||||
|
the information about the system on which you are experiencing it and point
|
||||||
|
out the version of `EnTT` you are using (tag or commit).
|
||||||
|
|
||||||
|
* If you found a bug and you wrote a patch to fix it, open a new
|
||||||
|
[pull request](https://github.com/skypjack/entt/pulls) with your code.
|
||||||
|
**Please**, add some tests to avoid regressions in future if possible, it
|
||||||
|
would be really appreciated. Note that the `EnTT` library has a
|
||||||
|
[coverage at 100%](https://coveralls.io/github/skypjack/entt?branch=master)
|
||||||
|
(at least it was at 100% at the time I wrote this file) and this is the reason
|
||||||
|
for which you can be confident with using it in a production environment.
|
||||||
|
|
||||||
|
* If you want to propose a new feature and you know how to code it, **please**
|
||||||
|
do not issue directly a pull request. Before to do it,
|
||||||
|
[create a new issue](https://github.com/skypjack/entt/issues/new) to discuss
|
||||||
|
your proposal. Other users could be interested in your idea and the discussion
|
||||||
|
that will follow can refine it and therefore give us a better solution.
|
||||||
|
|
||||||
|
* If you want to request a new feature, I'm available for hiring. Take a look at
|
||||||
|
[my profile](https://github.com/skypjack) and feel free to write me.
|
||||||
Vendored
+21
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2017-2025 Michele Caini, author of EnTT
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copy of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copy or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
Vendored
+4
@@ -0,0 +1,4 @@
|
|||||||
|
module(name = "entt")
|
||||||
|
|
||||||
|
bazel_dep(name = "rules_cc", version = "0.0.8")
|
||||||
|
bazel_dep(name = "bazel_skylib", version = "1.4.2")
|
||||||
Vendored
+414
@@ -0,0 +1,414 @@
|
|||||||
|

|
||||||
|
|
||||||
|
[](https://github.com/skypjack/entt/actions)
|
||||||
|
[](https://codecov.io/gh/skypjack/entt)
|
||||||
|
[](https://godbolt.org/z/zxW73f)
|
||||||
|
[](https://skypjack.github.io/entt/)
|
||||||
|
[](https://vcpkg.link/ports/entt)
|
||||||
|
[](https://conan.io/center/recipes/entt)
|
||||||
|
[](https://gitter.im/skypjack/entt)
|
||||||
|
[](https://discord.gg/5BjPWBd)
|
||||||
|
|
||||||
|
> `EnTT` has been a dream so far, we haven't found a single bug to date and it's
|
||||||
|
> super easy to work with
|
||||||
|
>
|
||||||
|
> -- Every EnTT User Ever
|
||||||
|
|
||||||
|
`EnTT` is a header-only, tiny and easy to use library for game programming and
|
||||||
|
much more written in **modern C++**.<br/>
|
||||||
|
[Among others](https://github.com/skypjack/entt/wiki/EnTT-in-Action), it's used
|
||||||
|
in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the
|
||||||
|
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri
|
||||||
|
and the amazing [**Ragdoll**](https://ragdolldynamics.com/).<br/>
|
||||||
|
If you don't see your project in the list, please open an issue, submit a PR or
|
||||||
|
add the [\#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Do you want to **keep up with changes** or do you have a **question** that
|
||||||
|
doesn't require you to open an issue?<br/>
|
||||||
|
Join the [gitter channel](https://gitter.im/skypjack/entt) and the
|
||||||
|
[discord server](https://discord.gg/5BjPWBd), meet other users like you. The
|
||||||
|
more we are, the better for everyone.<br/>
|
||||||
|
Don't forget to check the
|
||||||
|
[FAQs](https://github.com/skypjack/entt/wiki/Frequently-Asked-Questions) and the
|
||||||
|
[wiki](https://github.com/skypjack/entt/wiki) too. Your answers may already be
|
||||||
|
there.
|
||||||
|
|
||||||
|
Do you want to support `EnTT`? Consider becoming a
|
||||||
|
[**sponsor**](https://github.com/users/skypjack/sponsorship) or making a
|
||||||
|
donation via [**PayPal**](https://www.paypal.me/skypjack).<br/>
|
||||||
|
Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||||
|
**special** thanks to:
|
||||||
|
|
||||||
|
[](https://mojang.com)
|
||||||
|
[](https://img.ly/)
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Code Example](#code-example)
|
||||||
|
* [Motivation](#motivation)
|
||||||
|
* [Benchmark](#benchmark)
|
||||||
|
* [Integration](#integration)
|
||||||
|
* [Requirements](#requirements)
|
||||||
|
* [CMake](#cmake)
|
||||||
|
* [Natvis support](#natvis-support)
|
||||||
|
* [Packaging Tools](#packaging-tools)
|
||||||
|
* [pkg-config](#pkg-config)
|
||||||
|
* [Documentation](#documentation)
|
||||||
|
* [Tests](#tests)
|
||||||
|
* [EnTT in Action](#entt-in-action)
|
||||||
|
* [Contributors](#contributors)
|
||||||
|
* [License](#license)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
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/)
|
||||||
|
* [ECS on Wikipedia](https://en.wikipedia.org/wiki/Entity%E2%80%93component%E2%80%93system)
|
||||||
|
|
||||||
|
This project started off as a pure entity-component system. Over time the
|
||||||
|
codebase has grown as more and more classes and functionalities were added.<br/>
|
||||||
|
Here is a brief, yet incomplete list of what it offers today:
|
||||||
|
|
||||||
|
* Built-in **RTTI system** mostly similar to the standard one.
|
||||||
|
* A `constexpr` utility for human-readable **resource names**.
|
||||||
|
* Minimal **configuration system** built using the monostate pattern.
|
||||||
|
* Incredibly fast **entity-component system** with its own _pay for what you
|
||||||
|
use_ policy, unconstrained component types with optional pointer stability and
|
||||||
|
hooks for storage customization.
|
||||||
|
* Views and groups to iterate entities and components and allow different access
|
||||||
|
patterns, from **perfect SoA** to fully random.
|
||||||
|
* A lot of **facilities** built on top of the entity-component system to help
|
||||||
|
the users and avoid reinventing the wheel.
|
||||||
|
* General purpose **execution graph builder** for optimal scheduling.
|
||||||
|
* The smallest and most basic implementation of a **service locator** ever seen.
|
||||||
|
* A built-in, non-intrusive and macro-free runtime **reflection system**.
|
||||||
|
* **Static polymorphism** made simple and within everyone's reach.
|
||||||
|
* A few homemade containers, like a sparse set based **hash map**.
|
||||||
|
* A **cooperative scheduler** for processes of any type.
|
||||||
|
* All that is needed for **resource management** (cache, loaders, handles).
|
||||||
|
* Delegates, **signal handlers** and a tiny event dispatcher.
|
||||||
|
* A general purpose **event emitter** as a CRTP idiom based class template.
|
||||||
|
* And **much more**! Check out the
|
||||||
|
[**wiki**](https://github.com/skypjack/entt/wiki).
|
||||||
|
|
||||||
|
Consider this list a work in progress as well as the project. The whole API is
|
||||||
|
fully documented in-code for those who are brave enough to read it.<br/>
|
||||||
|
Please, do note that all tools are also DLL-friendly now and run smoothly across
|
||||||
|
boundaries.
|
||||||
|
|
||||||
|
One thing known to most is that `EnTT` is also used in **Minecraft**.<br/>
|
||||||
|
Given that the game is available literally everywhere, I can confidently say
|
||||||
|
that the library has been sufficiently tested on every platform that can come to
|
||||||
|
mind.
|
||||||
|
|
||||||
|
## Code Example
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <entt/entt.hpp>
|
||||||
|
|
||||||
|
struct position {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct velocity {
|
||||||
|
float dx;
|
||||||
|
float dy;
|
||||||
|
};
|
||||||
|
|
||||||
|
void update(entt::registry ®istry) {
|
||||||
|
auto view = registry.view<const position, velocity>();
|
||||||
|
|
||||||
|
// use a callback
|
||||||
|
view.each([](const auto &pos, auto &vel) { /* ... */ });
|
||||||
|
|
||||||
|
// use an extended callback
|
||||||
|
view.each([](const auto entity, const auto &pos, auto &vel) { /* ... */ });
|
||||||
|
|
||||||
|
// use a range-for
|
||||||
|
for(auto [entity, pos, vel]: view.each()) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
// use forward iterators and get only the components of interest
|
||||||
|
for(auto entity: view) {
|
||||||
|
auto &vel = view.get<velocity>(entity);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
entt::registry registry;
|
||||||
|
|
||||||
|
for(auto i = 0u; i < 10u; ++i) {
|
||||||
|
const auto entity = registry.create();
|
||||||
|
registry.emplace<position>(entity, i * 1.f, i * 1.f);
|
||||||
|
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
|
||||||
|
}
|
||||||
|
|
||||||
|
update(registry);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
I started developing `EnTT` for the _wrong_ reason: my goal was to design an
|
||||||
|
entity-component system to beat another well known open source library both in
|
||||||
|
terms of performance and possibly memory usage.<br/>
|
||||||
|
In the end, I did it, but it wasn't very satisfying. Actually it wasn't
|
||||||
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Benchmark
|
||||||
|
|
||||||
|
For what it's worth, you'll **never** see me trying to make other projects look
|
||||||
|
bad or offer dubious comparisons just to make this library seem cooler.<br/>
|
||||||
|
I leave this activity to others, if they enjoy it (and it seems that some people
|
||||||
|
actually like it). I prefer to make better use of my time.
|
||||||
|
|
||||||
|
If you are interested, you can compile the `benchmark` test in release mode (to
|
||||||
|
enable compiler optimizations, otherwise it would make little sense) by setting
|
||||||
|
the `ENTT_BUILD_BENCHMARK` option of `CMake` to `ON`, then evaluate yourself
|
||||||
|
whether you're satisfied with the results or not.
|
||||||
|
|
||||||
|
There are also a lot of projects out there that use `EnTT` as a basis for
|
||||||
|
comparison (this should already tell you a lot). Many of these benchmarks are
|
||||||
|
completely wrong, many others are simply incomplete, good at omitting some
|
||||||
|
information and using the wrong function to compare a given feature. Certainly
|
||||||
|
there are also good ones but they age quickly if nobody updates them, especially
|
||||||
|
when the library they are dealing with is actively developed.<br/>
|
||||||
|
Out of all of them, [this](https://github.com/abeimler/ecs_benchmark) seems like
|
||||||
|
the most up-to-date project and also covers a certain number of libraries. I
|
||||||
|
can't say exactly whether `EnTT` is used correctly or not. However, even if used
|
||||||
|
poorly, it should still give the reader an idea of where it's going to operate.
|
||||||
|
|
||||||
|
# Integration
|
||||||
|
|
||||||
|
`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:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <entt/entt.hpp>
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the line below to include only the entity-component system instead:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then pass the proper `-I` argument to the compiler to add the `src` directory to
|
||||||
|
the include paths.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
To be able to use `EnTT`, users must provide a full-featured compiler that
|
||||||
|
supports at least C++17.<br/>
|
||||||
|
The requirements below are mandatory to compile the tests and to extract the
|
||||||
|
documentation:
|
||||||
|
|
||||||
|
* `CMake` version 3.7 or later.
|
||||||
|
* `Doxygen` version 1.8 or later.
|
||||||
|
|
||||||
|
Alternatively, [Bazel](https://bazel.build) is also supported as a build system
|
||||||
|
(credits to [zaucy](https://github.com/zaucy) who offered to maintain it).<br/>
|
||||||
|
In the documentation below I'll still refer to `CMake`, this being the official
|
||||||
|
build system of the library.
|
||||||
|
|
||||||
|
## CMake
|
||||||
|
|
||||||
|
To use `EnTT` from a `CMake` project, just link an existing target to the
|
||||||
|
`EnTT::EnTT` alias.<br/>
|
||||||
|
The library offers everything you need for locating (as in `find_package`),
|
||||||
|
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
|
||||||
|
it in many of the ways that you can think of and that involve `CMake`.<br/>
|
||||||
|
Covering all possible cases would require a treatise and not a simple README
|
||||||
|
file, but I'm confident that anyone reading this section also knows what it's
|
||||||
|
about and can use `EnTT` from a `CMake` project without problems.
|
||||||
|
|
||||||
|
## Natvis support
|
||||||
|
|
||||||
|
When using `CMake`, just enable the option `ENTT_INCLUDE_NATVIS` and enjoy
|
||||||
|
it.<br/>
|
||||||
|
Otherwise, most of the tools are covered via Natvis and all files can be found
|
||||||
|
in the `natvis` directory, divided by module.<br/>
|
||||||
|
If you spot errors or have suggestions, any contribution is welcome!
|
||||||
|
|
||||||
|
## Packaging Tools
|
||||||
|
|
||||||
|
`EnTT` is available for some of the most known packaging tools. In particular:
|
||||||
|
|
||||||
|
* [`Conan`](https://github.com/conan-io/conan-center-index), the C/C++ Package
|
||||||
|
Manager for Developers.
|
||||||
|
|
||||||
|
* [`vcpkg`](https://github.com/Microsoft/vcpkg), Microsoft VC++ Packaging
|
||||||
|
Tool.<br/>
|
||||||
|
You can download and install `EnTT` in just a few simple steps:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git clone https://github.com/Microsoft/vcpkg.git
|
||||||
|
$ cd vcpkg
|
||||||
|
$ ./bootstrap-vcpkg.sh
|
||||||
|
$ ./vcpkg integrate install
|
||||||
|
$ vcpkg install entt
|
||||||
|
```
|
||||||
|
|
||||||
|
Or you can use the `experimental` feature to test the latest changes:
|
||||||
|
|
||||||
|
```
|
||||||
|
vcpkg install entt[experimental] --head
|
||||||
|
```
|
||||||
|
|
||||||
|
The `EnTT` port in `vcpkg` is kept up to date by Microsoft team members and
|
||||||
|
community contributors.<br/>
|
||||||
|
If the version is out of date, please
|
||||||
|
[create an issue or pull request](https://github.com/Microsoft/vcpkg) on the
|
||||||
|
`vcpkg` repository.
|
||||||
|
|
||||||
|
* [`Homebrew`](https://github.com/skypjack/homebrew-entt), the missing package
|
||||||
|
manager for macOS.<br/>
|
||||||
|
Available as a homebrew formula. Just type the following to install it:
|
||||||
|
|
||||||
|
```
|
||||||
|
brew install skypjack/entt/entt
|
||||||
|
```
|
||||||
|
|
||||||
|
* [`build2`](https://build2.org), build toolchain for developing and packaging C
|
||||||
|
and C++ code.<br/>
|
||||||
|
In order to use the [`entt`](https://cppget.org/entt) package in a `build2`
|
||||||
|
project, add the following line or a similar one to the `manifest` file:
|
||||||
|
|
||||||
|
```
|
||||||
|
depends: entt ^3.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
Also check that the configuration refers to a valid repository, so that the
|
||||||
|
package can be found by `build2`:
|
||||||
|
|
||||||
|
* [`cppget.org`](https://cppget.org), the open-source community central
|
||||||
|
repository, accessible as `https://pkg.cppget.org/1/stable`.
|
||||||
|
|
||||||
|
* [Package source repository](https://github.com/build2-packaging/entt):
|
||||||
|
accessible as either `https://github.com/build2-packaging/entt.git` or
|
||||||
|
`ssh://git@github.com/build2-packaging/entt.git`.
|
||||||
|
Feel free to [report issues](https://github.com/build2-packaging/entt) with
|
||||||
|
this package.
|
||||||
|
|
||||||
|
Both can be used with `bpkg add-repo` or added in a project
|
||||||
|
`repositories.manifest`. See the official
|
||||||
|
[documentation](https://build2.org/build2-toolchain/doc/build2-toolchain-intro.xhtml#guide-repositories)
|
||||||
|
for more details.
|
||||||
|
|
||||||
|
* [`bzlmod`](https://bazel.build/external/overview#bzlmod), Bazel's external
|
||||||
|
dependency management system.<br/>
|
||||||
|
To use the [`entt`](https://registry.bazel.build/modules/entt) module in a
|
||||||
|
`bazel` project, add the following to your `MODULE.bazel` file:
|
||||||
|
|
||||||
|
```starlark
|
||||||
|
bazel_dep(name = "entt", version = "3.12.2")
|
||||||
|
```
|
||||||
|
|
||||||
|
EnTT will now be available as `@entt` (short for `@entt//:entt`) to be used
|
||||||
|
in your `cc_*` rule `deps`.
|
||||||
|
|
||||||
|
Consider this list a work in progress and help me to make it longer if you like.
|
||||||
|
|
||||||
|
## pkg-config
|
||||||
|
|
||||||
|
`EnTT` also supports `pkg-config` (for some definition of _supports_ at least).
|
||||||
|
A suitable file called `entt.pc` is generated and installed in a proper
|
||||||
|
directory when running `CMake`.<br/>
|
||||||
|
This should also make it easier to use with tools such as `Meson` or similar.
|
||||||
|
|
||||||
|
# Documentation
|
||||||
|
|
||||||
|
The documentation is based on [doxygen](http://www.doxygen.nl/). To build it:
|
||||||
|
|
||||||
|
$ cd build
|
||||||
|
$ cmake .. -DENTT_BUILD_DOCS=ON
|
||||||
|
$ make
|
||||||
|
|
||||||
|
The API reference is created in HTML format in the `build/docs/html` directory.
|
||||||
|
To navigate it with your favorite browser:
|
||||||
|
|
||||||
|
$ cd build
|
||||||
|
$ your_favorite_browser docs/html/index.html
|
||||||
|
|
||||||
|
The same version is also available [online](https://skypjack.github.io/entt/)
|
||||||
|
for the latest release, that is the last stable tag.<br/>
|
||||||
|
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
|
||||||
|
to the project where users can find all related documentation pages.
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
To compile and run the tests, `EnTT` requires *googletest*.<br/>
|
||||||
|
`cmake` downloads and compiles the library before compiling anything else. In
|
||||||
|
order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to `ON`.
|
||||||
|
|
||||||
|
To build the most basic set of tests:
|
||||||
|
|
||||||
|
* `$ cd build`
|
||||||
|
* `$ cmake -DENTT_BUILD_TESTING=ON ..`
|
||||||
|
* `$ make`
|
||||||
|
* `$ make test`
|
||||||
|
|
||||||
|
Note that benchmarks are not part of this set.
|
||||||
|
|
||||||
|
# EnTT in Action
|
||||||
|
|
||||||
|
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||||
|
mention most of them because of some signatures I put on some documents time
|
||||||
|
ago. Fortunately, there are also people who took the time to implement open
|
||||||
|
source projects based on `EnTT` and did not hold back when it came to
|
||||||
|
documenting them.
|
||||||
|
|
||||||
|
[Here](https://github.com/skypjack/entt/wiki/EnTT-in-Action) you can find an
|
||||||
|
incomplete list of games, applications and articles that can be used as a
|
||||||
|
reference.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
# Contributors
|
||||||
|
|
||||||
|
Requests for features, PRs, suggestions and feedback are highly appreciated.
|
||||||
|
|
||||||
|
If you find you can help and want to contribute to the project with your
|
||||||
|
experience or you do want to get part of the project for some other reason, 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 as soon as possible.
|
||||||
|
|
||||||
|
If you decide to participate, please see the guidelines for
|
||||||
|
[contributing](https://github.com/skypjack/entt/blob/master/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.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
Code and documentation Copyright (c) 2017-2025 Michele Caini.<br/>
|
||||||
|
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||||
|
|
||||||
|
Code released under
|
||||||
|
[the MIT license](https://github.com/skypjack/entt/blob/master/LICENSE).<br/>
|
||||||
|
Documentation released under
|
||||||
|
[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/).<br/>
|
||||||
|
All logos released under
|
||||||
|
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/).
|
||||||
Vendored
+37
@@ -0,0 +1,37 @@
|
|||||||
|
EXAMPLES
|
||||||
|
* filter on runtime values/variables (not only types)
|
||||||
|
* support to polymorphic types (see #859)
|
||||||
|
|
||||||
|
DOC:
|
||||||
|
* custom storage/view
|
||||||
|
* update entity doc when the storage based model is in place
|
||||||
|
* in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
|
||||||
|
* view: single vs multi type views are no longer a thing actually
|
||||||
|
* bump entities, reserved bits on identifiers
|
||||||
|
|
||||||
|
TODO:
|
||||||
|
* review all NOLINT
|
||||||
|
* bring nested groups back in place (see bd34e7f)
|
||||||
|
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||||
|
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
|
||||||
|
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
|
||||||
|
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
|
||||||
|
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
|
||||||
|
* self contained entity traits to avoid explicit specializations (ie enum constants)
|
||||||
|
* auto type info data from types if present
|
||||||
|
* test: push sharing types further
|
||||||
|
* storage entity: fast range-push from above
|
||||||
|
* table: pop back to support swap and pop, single column access, empty type optimization
|
||||||
|
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
|
||||||
|
* suppress -Wself-move on CI with g++13
|
||||||
|
* view specializations for multi, single and filtered elements
|
||||||
|
* don't pass reactive storage by default to callback
|
||||||
|
* runtime types support for meta for types that aren't backed by C++ types
|
||||||
|
* built-in no-pagination storage - no_pagination page size as limits::max
|
||||||
|
* any cdynamic to support const ownership construction
|
||||||
|
* allow passing arguments to meta setter/getter (we can fallback on meta invoke probably)
|
||||||
|
* FetchContent_Populate -> FetchContent_MakeAvailable warnings
|
||||||
|
* doc: IMPLICIT_DIR_DOCS for dir docs or \dir
|
||||||
|
* meta non-const allow_cast overloads: (const int &) to (int &) is not allowed, but (const int &) to (double &) is allowed (support only for convertibles)
|
||||||
|
* improve non-const allow cast with in-place switch
|
||||||
|
* meta fixed_size could return the size directly if present
|
||||||
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
# SEE MODULE.bazel
|
||||||
Vendored
Vendored
+13
@@ -0,0 +1,13 @@
|
|||||||
|
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||||
|
|
||||||
|
COPTS = selects.with_or({
|
||||||
|
("//conditions:default", "@rules_cc//cc/compiler:clang", "@rules_cc//cc/compiler:gcc", "@rules_cc//cc/compiler:mingw-gcc"): [
|
||||||
|
"-std=c++17",
|
||||||
|
"-w",
|
||||||
|
],
|
||||||
|
("@rules_cc//cc/compiler:msvc-cl", "@rules_cc//cc/compiler:clang-cl"): [
|
||||||
|
"/std:c++17",
|
||||||
|
"/permissive-",
|
||||||
|
"/w",
|
||||||
|
],
|
||||||
|
})
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
@PACKAGE_INIT@
|
||||||
|
|
||||||
|
set(EnTT_VERSION "@PROJECT_VERSION@")
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/EnTTTargets.cmake")
|
||||||
|
check_required_components("@PROJECT_NAME@")
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
|
includedir=@EnTT_PKGCONFIG_INCLUDEDIR@
|
||||||
|
|
||||||
|
Name: EnTT
|
||||||
|
Description: Gaming meets modern C++
|
||||||
|
Url: https://github.com/skypjack/entt
|
||||||
|
Version: @ENTT_VERSION@
|
||||||
|
Cflags: -I${includedir}
|
||||||
+23
@@ -0,0 +1,23 @@
|
|||||||
|
# This module provides function for joining paths
|
||||||
|
# known from most languages
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (MIT OR CC0-1.0)
|
||||||
|
# Copyright 2020 Jan Tojnar
|
||||||
|
# https://github.com/jtojnar/cmake-snips
|
||||||
|
#
|
||||||
|
# Modelled after Python’s os.path.join
|
||||||
|
# https://docs.python.org/3.7/library/os.path.html#os.path.join
|
||||||
|
# Windows not supported
|
||||||
|
function(join_paths joined_path first_path_segment)
|
||||||
|
set(temp_path "${first_path_segment}")
|
||||||
|
foreach(current_segment IN LISTS ARGN)
|
||||||
|
if(NOT ("${current_segment}" STREQUAL ""))
|
||||||
|
if(IS_ABSOLUTE "${current_segment}")
|
||||||
|
set(temp_path "${current_segment}")
|
||||||
|
else()
|
||||||
|
set(temp_path "${temp_path}/${current_segment}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(${joined_path} "${temp_path}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
Vendored
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from cpt.packager import ConanMultiPackager
|
||||||
|
import os
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
username = os.getenv("GITHUB_ACTOR")
|
||||||
|
tag_version = os.getenv("GITHUB_REF")
|
||||||
|
tag_package = os.getenv("GITHUB_REPOSITORY")
|
||||||
|
login_username = os.getenv("CONAN_LOGIN_USERNAME")
|
||||||
|
package_version = tag_version.replace("refs/tags/v", "")
|
||||||
|
package_name = tag_package.replace("skypjack/", "")
|
||||||
|
reference = "{}/{}".format(package_name, package_version)
|
||||||
|
channel = os.getenv("CONAN_CHANNEL", "stable")
|
||||||
|
upload = os.getenv("CONAN_UPLOAD")
|
||||||
|
stable_branch_pattern = os.getenv("CONAN_STABLE_BRANCH_PATTERN", r"v\d+\.\d+\.\d+.*")
|
||||||
|
test_folder = os.getenv("CPT_TEST_FOLDER", os.path.join("conan", "test_package"))
|
||||||
|
upload_only_when_stable = os.getenv("CONAN_UPLOAD_ONLY_WHEN_STABLE", True)
|
||||||
|
disable_shared = os.getenv("CONAN_DISABLE_SHARED_BUILD", "False")
|
||||||
|
|
||||||
|
builder = ConanMultiPackager(username=username,
|
||||||
|
reference=reference,
|
||||||
|
channel=channel,
|
||||||
|
login_username=login_username,
|
||||||
|
upload=upload,
|
||||||
|
stable_branch_pattern=stable_branch_pattern,
|
||||||
|
upload_only_when_stable=upload_only_when_stable,
|
||||||
|
test_folder=test_folder)
|
||||||
|
builder.add()
|
||||||
|
|
||||||
|
filtered_builds = []
|
||||||
|
for settings, options, env_vars, build_requires, reference in builder.items:
|
||||||
|
if disable_shared == "False" or not options["{}:shared".format(package_name)]:
|
||||||
|
filtered_builds.append([settings, options, env_vars, build_requires])
|
||||||
|
builder.builds = filtered_builds
|
||||||
|
|
||||||
|
builder.run()
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
conan user
|
||||||
|
python conan/build.py
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
set -x
|
||||||
|
|
||||||
|
pip install -U conan_package_tools conan
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.7.2)
|
||||||
|
project(test_package)
|
||||||
|
|
||||||
|
set(CMAKE_VERBOSE_MAKEFILE TRUE)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
|
||||||
|
conan_basic_setup()
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} test_package.cpp)
|
||||||
|
target_link_libraries(${PROJECT_NAME} ${CONAN_LIBS})
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from conans import ConanFile, CMake
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
class TestPackageConan(ConanFile):
|
||||||
|
settings = "os", "compiler", "build_type", "arch"
|
||||||
|
generators = "cmake"
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
cmake = CMake(self)
|
||||||
|
cmake.configure()
|
||||||
|
cmake.build()
|
||||||
|
|
||||||
|
def test(self):
|
||||||
|
bin_path = os.path.join("bin", "test_package")
|
||||||
|
self.run(bin_path, run_environment=True)
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
#include <entt/entt.hpp>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct position {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct velocity {
|
||||||
|
float dx;
|
||||||
|
float dy;
|
||||||
|
};
|
||||||
|
|
||||||
|
void update(entt::registry ®istry) {
|
||||||
|
auto view = registry.view<position, velocity>();
|
||||||
|
|
||||||
|
for(auto entity: view) {
|
||||||
|
// gets only the elements that are going to be used ...
|
||||||
|
|
||||||
|
auto &vel = view.get<velocity>(entity);
|
||||||
|
|
||||||
|
vel.dx = 0.;
|
||||||
|
vel.dy = 0.;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(std::uint64_t dt, entt::registry ®istry) {
|
||||||
|
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
|
||||||
|
// gets all the elements of the view at once ...
|
||||||
|
|
||||||
|
pos.x += vel.dx * dt;
|
||||||
|
pos.y += vel.dy * dt;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
entt::registry registry;
|
||||||
|
std::uint64_t dt = 16;
|
||||||
|
|
||||||
|
for(auto i = 0; i < 10; ++i) {
|
||||||
|
auto entity = registry.create();
|
||||||
|
registry.emplace<position>(entity, i * 1.f, i * 1.f);
|
||||||
|
if(i % 2 == 0) { registry.emplace<velocity>(entity, i * .1f, i * .1f); }
|
||||||
|
}
|
||||||
|
|
||||||
|
update(dt, registry);
|
||||||
|
update(registry);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
Vendored
+27
@@ -0,0 +1,27 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from conans import ConanFile
|
||||||
|
|
||||||
|
|
||||||
|
class EnttConan(ConanFile):
|
||||||
|
name = "entt"
|
||||||
|
description = "Gaming meets modern C++ - a fast and reliable entity-component system (ECS) and much more "
|
||||||
|
topics = ("conan," "entt", "gaming", "entity", "ecs")
|
||||||
|
url = "https://github.com/skypjack/entt"
|
||||||
|
homepage = url
|
||||||
|
author = "Michele Caini <michele.caini@gmail.com>"
|
||||||
|
license = "MIT"
|
||||||
|
exports = ["LICENSE"]
|
||||||
|
exports_sources = ["src/*"]
|
||||||
|
no_copy_source = True
|
||||||
|
|
||||||
|
def package(self):
|
||||||
|
self.copy(pattern="LICENSE", dst="licenses")
|
||||||
|
self.copy(pattern="*", dst="include", src="src", keep_path=True)
|
||||||
|
|
||||||
|
def package_info(self):
|
||||||
|
if not self.in_local_cache:
|
||||||
|
self.cpp_info.includedirs = ["src"]
|
||||||
|
|
||||||
|
def package_id(self):
|
||||||
|
self.info.header_only()
|
||||||
Vendored
+59
@@ -0,0 +1,59 @@
|
|||||||
|
# Doxygen configuration (documentation)
|
||||||
|
|
||||||
|
find_package(Doxygen 1.13)
|
||||||
|
|
||||||
|
if(DOXYGEN_FOUND)
|
||||||
|
include(FetchContent)
|
||||||
|
|
||||||
|
FetchContent_Declare(
|
||||||
|
doxygen-awesome-css
|
||||||
|
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
|
||||||
|
GIT_TAG main
|
||||||
|
GIT_SHALLOW 1
|
||||||
|
)
|
||||||
|
|
||||||
|
FetchContent_GetProperties(doxygen-awesome-css)
|
||||||
|
|
||||||
|
if(NOT doxygen-awesome-css_POPULATED)
|
||||||
|
FetchContent_Populate(doxygen-awesome-css)
|
||||||
|
set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
|
||||||
|
set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR})
|
||||||
|
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(
|
||||||
|
docs ALL
|
||||||
|
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
|
||||||
|
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
|
||||||
|
VERBATIM
|
||||||
|
SOURCES
|
||||||
|
md/config.md
|
||||||
|
md/container.md
|
||||||
|
md/core.md
|
||||||
|
md/entity.md
|
||||||
|
md/faq.md
|
||||||
|
md/lib.md
|
||||||
|
md/links.md
|
||||||
|
md/locator.md
|
||||||
|
md/meta.md
|
||||||
|
md/poly.md
|
||||||
|
md/process.md
|
||||||
|
md/reference.md
|
||||||
|
md/resource.md
|
||||||
|
md/signal.md
|
||||||
|
md/unreal.md
|
||||||
|
doxy.in
|
||||||
|
)
|
||||||
|
|
||||||
|
if(ENTT_INSTALL)
|
||||||
|
install(
|
||||||
|
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
|
||||||
|
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
Vendored
+2914
File diff suppressed because it is too large
Load Diff
Vendored
+115
@@ -0,0 +1,115 @@
|
|||||||
|
# Crash Course: configuration
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Definitions](#definitions)
|
||||||
|
* [ENTT_NOEXCEPTION](#entt_noexception)
|
||||||
|
* [ENTT_USE_ATOMIC](#entt_use_atomic)
|
||||||
|
* [ENTT_ID_TYPE](#entt_id_type)
|
||||||
|
* [ENTT_SPARSE_PAGE](#entt_sparse_page)
|
||||||
|
* [ENTT_PACKED_PAGE](#entt_packed_page)
|
||||||
|
* [ENTT_ASSERT](#entt_assert)
|
||||||
|
* [ENTT_ASSERT_CONSTEXPR](#entt_assert_constexpr)
|
||||||
|
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
|
||||||
|
* [ENTT_NO_ETO](#entt_no_eto)
|
||||||
|
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
`EnTT` has become almost completely customizable over time, in many
|
||||||
|
respects. These variables are just one of the many ways to customize how it
|
||||||
|
works.<br/>
|
||||||
|
In the vast majority of cases, users will have no interest in changing the
|
||||||
|
default parameters. For all other cases, the list of possible configurations
|
||||||
|
with which it is possible to adjust the behavior of the library at runtime can
|
||||||
|
be found below.
|
||||||
|
|
||||||
|
# Definitions
|
||||||
|
|
||||||
|
All options are intended as parameters to the compiler (or user-defined macros
|
||||||
|
within the compilation units, if preferred).<br/>
|
||||||
|
Each parameter can result in internal library definitions. It is not recommended
|
||||||
|
to try to also modify these definitions, since there is no guarantee that they
|
||||||
|
will remain stable over time unlike the options below.
|
||||||
|
|
||||||
|
## ENTT_NOEXCEPTION
|
||||||
|
|
||||||
|
Define this variable without assigning any value to it to turn off exception
|
||||||
|
handling in `EnTT`.<br/>
|
||||||
|
This is roughly equivalent to setting the compiler flag `-fno-exceptions` but is
|
||||||
|
also limited to this library only.
|
||||||
|
|
||||||
|
## ENTT_USE_ATOMIC
|
||||||
|
|
||||||
|
In general, `EnTT` does not offer primitives to support multi-threading. Many of
|
||||||
|
the features can be split over multiple threads without any explicit control and
|
||||||
|
the user is the one who knows if a synchronization point is required.<br/>
|
||||||
|
However, some features are not easily accessible to users and are made
|
||||||
|
thread-safe by means of this definition.
|
||||||
|
|
||||||
|
## ENTT_ID_TYPE
|
||||||
|
|
||||||
|
`entt::id_type` is directly controlled by this definition and widely used within
|
||||||
|
the library.<br/>
|
||||||
|
By default, its type is `std::uint32_t`. However, users can define a different
|
||||||
|
default type if necessary.
|
||||||
|
|
||||||
|
## ENTT_SPARSE_PAGE
|
||||||
|
|
||||||
|
It is known that the ECS module of `EnTT` is based on _sparse sets_. What is
|
||||||
|
less known perhaps is that the sparse arrays are paged to reduce memory
|
||||||
|
usage.<br/>
|
||||||
|
Default size of pages (that is, the number of elements they contain) is 4096 but
|
||||||
|
users can adjust it if appropriate. In all cases, the chosen value **must** be a
|
||||||
|
power of 2.
|
||||||
|
|
||||||
|
## ENTT_PACKED_PAGE
|
||||||
|
|
||||||
|
As it happens with sparse arrays, packed arrays are also paginated. However, in
|
||||||
|
this case the aim is not to reduce memory usage but to have pointer stability
|
||||||
|
upon component creation.<br/>
|
||||||
|
Default size of pages (that is, the number of elements they contain) is 1024 but
|
||||||
|
users can adjust it if appropriate. In all cases, the chosen value **must** be a
|
||||||
|
power of 2.
|
||||||
|
|
||||||
|
## ENTT_ASSERT
|
||||||
|
|
||||||
|
For performance reasons, `EnTT` does not use exceptions or any other control
|
||||||
|
structures. In fact, it offers many features that result in undefined behavior
|
||||||
|
if not used correctly.<br/>
|
||||||
|
To get around this, the library relies on a lot of asserts for the purpose of
|
||||||
|
detecting errors in debug builds. By default, it uses `assert` internally. Users
|
||||||
|
are allowed to overwrite its behavior by setting this variable.
|
||||||
|
|
||||||
|
### ENTT_ASSERT_CONSTEXPR
|
||||||
|
|
||||||
|
Usually, an assert within a `constexpr` function is not a big deal. However, in
|
||||||
|
case of extreme customizations, it might be useful to differentiate.<br/>
|
||||||
|
For this purpose, `EnTT` introduces an admittedly badly named variable to make
|
||||||
|
the job easier in this regard. By default, this variable forwards its arguments
|
||||||
|
to `ENTT_ASSERT`.
|
||||||
|
|
||||||
|
### ENTT_DISABLE_ASSERT
|
||||||
|
|
||||||
|
Assertions may in turn affect performance to an extent when enabled. Whether
|
||||||
|
`ENTT_ASSERT` and `ENTT_ASSERT_CONSTEXPR` are redefined or not, all asserts can
|
||||||
|
be disabled at once by means of this definition.<br/>
|
||||||
|
Note that `ENTT_DISABLE_ASSERT` takes precedence over the redefinition of the
|
||||||
|
other variables and is therefore meant to disable all controls no matter what.
|
||||||
|
|
||||||
|
## ENTT_NO_ETO
|
||||||
|
|
||||||
|
In order to reduce memory consumption and increase performance, empty types are
|
||||||
|
never instantiated nor stored by the ECS module of `EnTT`.<br/>
|
||||||
|
Use this variable to treat these types like all others and therefore to create a
|
||||||
|
dedicated storage for them.
|
||||||
|
|
||||||
|
## ENTT_STANDARD_CPP
|
||||||
|
|
||||||
|
`EnTT` mixes non-standard language features with others that are perfectly
|
||||||
|
compliant to offer some of its functionalities.<br/>
|
||||||
|
This definition prevents the library from using non-standard techniques, that
|
||||||
|
is, functionalities that are not fully compliant with the standard C++.<br/>
|
||||||
|
While there are no known portability issues at the time of this writing, this
|
||||||
|
should make the library fully portable anyway if needed.
|
||||||
Vendored
+85
@@ -0,0 +1,85 @@
|
|||||||
|
# Crash Course: containers
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Containers](#containers)
|
||||||
|
* [Dense map](#dense-map)
|
||||||
|
* [Dense set](#dense-set)
|
||||||
|
* [Adaptors](#adaptors)
|
||||||
|
* [Table](#table)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
The standard C++ library offers a wide range of containers and adaptors already.
|
||||||
|
It is really difficult to do better (although it is very easy to do worse, as
|
||||||
|
many examples available online demonstrate).<br/>
|
||||||
|
`EnTT` does not try in any way to replace what is offered by the standard. Quite
|
||||||
|
the opposite, given the widespread use that is made of standard containers.<br/>
|
||||||
|
However, the library also tries to fill a gap in features and functionalities by
|
||||||
|
making available some containers and adaptors initially developed for internal
|
||||||
|
use.
|
||||||
|
|
||||||
|
This section of the library is likely to grow larger over time. However, for the
|
||||||
|
moment it is quite small and mainly aimed at satisfying some internal
|
||||||
|
needs.<br/>
|
||||||
|
For all containers and adaptors made available, full test coverage and stability
|
||||||
|
over time is guaranteed as usual.
|
||||||
|
|
||||||
|
# Containers
|
||||||
|
|
||||||
|
## Dense map
|
||||||
|
|
||||||
|
The dense map made available in `EnTT` is a hash map that aims to return a
|
||||||
|
packed array of elements, so as to reduce the number of jumps in memory during
|
||||||
|
iterations.<br/>
|
||||||
|
The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||||
|
implicit list within the packed array itself.
|
||||||
|
|
||||||
|
The interface is very close to its counterpart in the standard library, that is,
|
||||||
|
the `std::unordered_map` class.<br/>
|
||||||
|
However, both local and non-local iterators returned by a dense map belong to
|
||||||
|
the input iterator category although they respectively model the concepts of a
|
||||||
|
_forward iterator_ type and a _random access iterator_ type.<br/>
|
||||||
|
This is because they return a pair of references rather than a reference to a
|
||||||
|
pair. In other words, dense maps return a so called _proxy iterator_ the value
|
||||||
|
type of which is:
|
||||||
|
|
||||||
|
* `std::pair<const Key &, Type &>` for non-const iterator types.
|
||||||
|
* `std::pair<const Key &, const Type &>` for const iterator types.
|
||||||
|
|
||||||
|
This is quite different from what any standard library map returns and should be
|
||||||
|
taken into account when looking for a drop-in replacement.
|
||||||
|
|
||||||
|
## Dense set
|
||||||
|
|
||||||
|
The dense set made available in `EnTT` is a hash set that aims to return a
|
||||||
|
packed array of elements, so as to reduce the number of jumps in memory during
|
||||||
|
iterations.<br/>
|
||||||
|
The implementation is based on _sparse sets_ and each bucket is identified by an
|
||||||
|
implicit list within the packed array itself.
|
||||||
|
|
||||||
|
The interface is in all respects similar to its counterpart in the standard
|
||||||
|
library, that is, the `std::unordered_set` class.<br/>
|
||||||
|
However, this type of set also supports reverse iteration and therefore offers
|
||||||
|
all the functions necessary for the purpose (such as `rbegin` and `rend`).
|
||||||
|
|
||||||
|
# Adaptors
|
||||||
|
|
||||||
|
## Table
|
||||||
|
|
||||||
|
The `basic_table` class is a container adaptor which manages multiple sequential
|
||||||
|
containers together, treating them as different columns of the same table.<br/>
|
||||||
|
The `table` alias allows users to provide only the types to handle, using
|
||||||
|
`std::vector` as the default sequential container.
|
||||||
|
|
||||||
|
Only a small set of functions is provided, although very close to what the API
|
||||||
|
of the `std::vector` class offers.<br/>
|
||||||
|
The internal implementation is purposely supported by a tuple of containers
|
||||||
|
rather than a container of tuples. The purpose is to allow efficient access to
|
||||||
|
single columns and not just access to the entire data set of the table.
|
||||||
|
|
||||||
|
When a row is accessed, all data is returned in the form of a tuple containing
|
||||||
|
(possibly const) references to the elements of the row itself.<br/>
|
||||||
|
Similarly, when a table is iterated, tuples of references to table elements are
|
||||||
|
returned for each row.
|
||||||
Vendored
+986
@@ -0,0 +1,986 @@
|
|||||||
|
# Crash Course: core functionalities
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Any as in any type](#any-as-in-any-type)
|
||||||
|
* [Small buffer optimization](#small-buffer-optimization)
|
||||||
|
* [Alignment requirement](#alignment-requirement)
|
||||||
|
* [Bit](#bit)
|
||||||
|
* [Compressed pair](#compressed-pair)
|
||||||
|
* [Enum as bitmask](#enum-as-bitmask)
|
||||||
|
* [Hashed strings](#hashed-strings)
|
||||||
|
* [Wide characters](#wide-characters)
|
||||||
|
* [Conflicts](#conflicts)
|
||||||
|
* [Iterators](#iterators)
|
||||||
|
* [Input iterator pointer](#input-iterator-pointer)
|
||||||
|
* [Iota iterator](#iota-iterator)
|
||||||
|
* [Iterable adaptor](#iterable-adaptor)
|
||||||
|
* [Memory](#memory)
|
||||||
|
* [Allocator aware unique pointers](#allocator-aware-unique-pointers)
|
||||||
|
* [Monostate](#monostate)
|
||||||
|
* [Type support](#type-support)
|
||||||
|
* [Built-in RTTI support](#built-in-rtti-support)
|
||||||
|
* [Type info](#type-info)
|
||||||
|
* [Almost unique identifiers](#almost-unique-identifiers)
|
||||||
|
* [Type traits](#type-traits)
|
||||||
|
* [Size of](#size-of)
|
||||||
|
* [Is applicable](#is-applicable)
|
||||||
|
* [Constness as](#constness-as)
|
||||||
|
* [Member class type](#member-class-type)
|
||||||
|
* [N-th argument](#n-th-argument)
|
||||||
|
* [Integral constant](#integral-constant)
|
||||||
|
* [Tag](#tag)
|
||||||
|
* [Type list and value list](#type-list-and-value-list)
|
||||||
|
* [Unique sequential identifiers](#unique-sequential-identifiers)
|
||||||
|
* [Compile-time generator](#compile-time-generator)
|
||||||
|
* [Runtime generator](#runtime-generator)
|
||||||
|
* [Utilities](#utilities)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
`EnTT` comes with a bunch of core functionalities mostly used by the other parts
|
||||||
|
of the library.<br/>
|
||||||
|
Many of these tools are also useful in everyday work. Therefore, it is worth
|
||||||
|
describing them so as not to reinvent the wheel in case of need.
|
||||||
|
|
||||||
|
# Any as in any type
|
||||||
|
|
||||||
|
`EnTT` offers its own `any` type. It may seem redundant considering that C++17
|
||||||
|
introduced `std::any`, but it is not (hopefully).<br/>
|
||||||
|
First of all, the _type_ returned by an `std::any` is a const reference to an
|
||||||
|
`std::type_info`, an implementation defined class that is not something everyone
|
||||||
|
wants to see in a software. Furthermore, there is no way to bind it to the type
|
||||||
|
system of the library and therefore with its integrated RTTI support.
|
||||||
|
|
||||||
|
The `any` API is very similar to that of its most famous counterpart, mainly
|
||||||
|
because this class serves the same purpose of being an opaque container for any
|
||||||
|
type of value.<br/>
|
||||||
|
Instances also minimize the number of allocations by relying on a well known
|
||||||
|
technique called _small buffer optimization_ and a fake vtable.
|
||||||
|
|
||||||
|
Creating an object of the `any` type, whether empty or not, is trivial:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// an empty container
|
||||||
|
entt::any empty{};
|
||||||
|
|
||||||
|
// a container for an int
|
||||||
|
entt::any any{0};
|
||||||
|
|
||||||
|
// in place type construction
|
||||||
|
entt::any in_place_type{std::in_place_type<int>, 42};
|
||||||
|
|
||||||
|
// take ownership of already existing, dynamically allocated objects
|
||||||
|
entt::any in_place{std::in_place, std::make_unique<int>(42).release()};
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, the `make_any` function serves the same purpose. It requires to
|
||||||
|
always be explicit about the type and does not support taking ownership:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::any any = entt::make_any<int>(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
In all cases, the `any` class takes the burden of destroying the contained
|
||||||
|
element when required, regardless of the storage strategy used for the specific
|
||||||
|
object.<br/>
|
||||||
|
Furthermore, an instance of `any` is not tied to an actual type. Therefore, the
|
||||||
|
wrapper is reconfigured when it is assigned a new object of a type other than
|
||||||
|
the one it contains.
|
||||||
|
|
||||||
|
There is also a way to directly assign a value to the variable contained by an
|
||||||
|
`entt::any`, without necessarily replacing it. This is especially useful when
|
||||||
|
the object is used in _aliasing mode_, as described below:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::any any{42};
|
||||||
|
entt::any value{3};
|
||||||
|
|
||||||
|
// assigns by copy
|
||||||
|
any.assign(value);
|
||||||
|
|
||||||
|
// assigns by move
|
||||||
|
any.assign(std::move(value));
|
||||||
|
```
|
||||||
|
|
||||||
|
The `any` class performs a check on the type information and whether or not the
|
||||||
|
original type was copy or move assignable, as appropriate.<br/>
|
||||||
|
In all cases, the `assign` function returns a boolean value that is true in case
|
||||||
|
of success and false otherwise.
|
||||||
|
|
||||||
|
When in doubt about the type of object contained, the `type` member function
|
||||||
|
returns a const reference to the `type_info` associated with its element, or
|
||||||
|
`type_id<void>()` if the container is empty.<br/>
|
||||||
|
The type is also used internally when comparing two `any` objects:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if(any == empty) { /* ... */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, before proceeding with a comparison, it is verified that the
|
||||||
|
_type_ of the two objects is actually the same.<br/>
|
||||||
|
Refer to the `EnTT` type system documentation for more details about how
|
||||||
|
`type_info` works and the possible risks of a comparison.
|
||||||
|
|
||||||
|
A particularly interesting feature of this class is that it can also be used as
|
||||||
|
an opaque container for const and non-const references:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int value = 42;
|
||||||
|
|
||||||
|
entt::any any{std::in_place_type<int &>(value)};
|
||||||
|
entt::any cany = entt::make_any<const int &>(value);
|
||||||
|
entt::any fwd = entt::forward_as_any(value);
|
||||||
|
|
||||||
|
any.emplace<const int &>(value);
|
||||||
|
```
|
||||||
|
|
||||||
|
In other words, whenever `any` is explicitly told to construct an _alias_, it
|
||||||
|
acts as a pointer to the original instance rather than making a copy of it or
|
||||||
|
moving it internally. The contained object is never destroyed, and users must
|
||||||
|
ensure that its lifetime exceeds that of the container.<br/>
|
||||||
|
Similarly, it is possible to create non-owning copies of `any` from an existing
|
||||||
|
object:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// aliasing constructor
|
||||||
|
entt::any ref = other.as_ref();
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, it does not matter if the original container actually holds an
|
||||||
|
object or is as a reference for unmanaged elements already. The new instance
|
||||||
|
thus created does not create copies and only serves as a reference for the
|
||||||
|
original item.
|
||||||
|
|
||||||
|
It is worth mentioning that, while everything works transparently when it comes
|
||||||
|
to non-const references, there are some exceptions when it comes to const
|
||||||
|
references.<br/>
|
||||||
|
In particular, the `data` member function invoked on a non-const instance of
|
||||||
|
`any` that wraps a const reference returns a null pointer in all cases.
|
||||||
|
|
||||||
|
To cast an instance of `any` to a type, the library offers a set of `any_cast`
|
||||||
|
functions in all respects similar to their most famous counterparts.<br/>
|
||||||
|
The only difference is that, in the case of `EnTT`, they will not raise
|
||||||
|
exceptions but will only trigger an assert in debug mode, otherwise resulting in
|
||||||
|
undefined behavior in case of misuse in release mode.
|
||||||
|
|
||||||
|
## Small buffer optimization
|
||||||
|
|
||||||
|
The `any` class uses a technique called _small buffer optimization_ to reduce
|
||||||
|
the number of allocations where possible.<br/>
|
||||||
|
The default reserved size for an instance of `any` is `sizeof(double[2])`.
|
||||||
|
However, this is also configurable if needed. In fact, `any` is defined as an
|
||||||
|
alias for `basic_any<Len>`, where `Len` is the size above.<br/>
|
||||||
|
Users can easily set a custom size or define their own aliases:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using my_any = entt::basic_any<sizeof(double[4])>;
|
||||||
|
```
|
||||||
|
|
||||||
|
This feature, in addition to allowing the choice of a size that best suits the
|
||||||
|
needs of an application, also offers the possibility of forcing dynamic creation
|
||||||
|
of objects during construction.<br/>
|
||||||
|
In other terms, if the size is 0, `any` suppresses the small buffer optimization
|
||||||
|
and always dynamically allocates objects (except for aliasing cases).
|
||||||
|
|
||||||
|
## Alignment requirement
|
||||||
|
|
||||||
|
The alignment requirement is optional and by default the most stringent (the
|
||||||
|
largest) for any object whose size is at most equal to the one provided.<br/>
|
||||||
|
It is provided as an optional second parameter following the desired size for
|
||||||
|
the internal storage:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>;
|
||||||
|
```
|
||||||
|
|
||||||
|
The `basic_any` class template inspects the alignment requirements in each case,
|
||||||
|
even when not provided and may decide not to use the small buffer optimization
|
||||||
|
in order to meet them.
|
||||||
|
|
||||||
|
# Bit
|
||||||
|
|
||||||
|
Finding out the population count of an unsigned integral value (`popcount`),
|
||||||
|
whether a number is a power of two or not (`has_single_bit`) as well as the next
|
||||||
|
power of two given a random value (`next_power_of_two`) can be useful.<br/>
|
||||||
|
For example, it helps to allocate memory in pages having a size suitable for the
|
||||||
|
fast modulus:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const std::size_t result = entt::fast_mod(value, modulus);
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
|
||||||
|
this type of operation is far superior in terms of performance to the basic
|
||||||
|
modulus and for this reason preferred in many areas.
|
||||||
|
|
||||||
|
# Compressed pair
|
||||||
|
|
||||||
|
Primarily designed for internal use and far from being feature complete, the
|
||||||
|
`compressed_pair` class does exactly what it promises: it tries to reduce the
|
||||||
|
size of a pair by exploiting _Empty Base Class Optimization_ (or _EBCO_).<br/>
|
||||||
|
This class **is not** a drop-in replacement for `std::pair`. However, it offers
|
||||||
|
enough functionalities to be a good alternative for when reducing memory usage
|
||||||
|
is more important than having some cool and probably useless feature.
|
||||||
|
|
||||||
|
Although the API is very close to that of `std::pair` (apart from the fact that
|
||||||
|
the template parameters are inferred from the constructor and therefore there is
|
||||||
|
no `entt::make_compressed_pair`), the major difference is that `first` and
|
||||||
|
`second` are functions for implementation requirements:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::compressed_pair pair{0, 3.};
|
||||||
|
pair.first() = 42;
|
||||||
|
```
|
||||||
|
|
||||||
|
There is not much to describe then. It is recommended to rely on documentation
|
||||||
|
and intuition. At the end of the day, it is just a pair and nothing more.
|
||||||
|
|
||||||
|
# Enum as bitmask
|
||||||
|
|
||||||
|
Sometimes it is useful to be able to use enums as bitmasks. However, enum
|
||||||
|
classes are not really suitable for the purpose. Main problem is that they do
|
||||||
|
not convert implicitly to their underlying type.<br/>
|
||||||
|
The choice is then between using old-fashioned enums (with all their problems
|
||||||
|
that I do not want to discuss here) or writing _ugly_ code.
|
||||||
|
|
||||||
|
Fortunately, there is also a third way: adding enough operators in the global
|
||||||
|
scope to treat enum classes as bitmasks transparently.<br/>
|
||||||
|
The ultimate goal is to write code like the following (or maybe something more
|
||||||
|
meaningful, but this should give a grasp and remain simple at the same time):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class my_flag {
|
||||||
|
unknown = 0x01,
|
||||||
|
enabled = 0x02,
|
||||||
|
disabled = 0x04
|
||||||
|
};
|
||||||
|
|
||||||
|
const my_flag flags = my_flag::enabled;
|
||||||
|
const bool is_enabled = !!(flags & my_flag::enabled);
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem with adding all operators to the global scope is that these come
|
||||||
|
into play even when not required, with the risk of introducing errors that are
|
||||||
|
difficult to deal with.<br/>
|
||||||
|
However, C++ offers enough tools to get around this problem. In particular, the
|
||||||
|
library requires users to register the enum classes for which bitmask support
|
||||||
|
should be enabled:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<>
|
||||||
|
struct entt::enum_as_bitmask<my_flag>
|
||||||
|
: std::true_type
|
||||||
|
{};
|
||||||
|
```
|
||||||
|
|
||||||
|
This is handy when dealing with enum classes defined by third party libraries
|
||||||
|
and over which the user has no control. However, it is also verbose and can be
|
||||||
|
avoided by adding a specific value to the enum class itself:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class my_flag {
|
||||||
|
unknown = 0x01,
|
||||||
|
enabled = 0x02,
|
||||||
|
disabled = 0x04,
|
||||||
|
_entt_enum_as_bitmask
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, there is no need to specialize the `enum_as_bitmask` traits, since
|
||||||
|
`EnTT` automatically detects the flag and enables the bitmask support.<br/>
|
||||||
|
Once the enum class is registered (in one way or the other), the most common
|
||||||
|
operators such as `&`, `|` but also `&=` and `|=` are available for use.
|
||||||
|
|
||||||
|
Refer to the official documentation for the full list of operators.
|
||||||
|
|
||||||
|
# Hashed strings
|
||||||
|
|
||||||
|
Hashed strings are human-readable identifiers in the codebase that turn into
|
||||||
|
numeric values at runtime, thus without affecting performance.<br/>
|
||||||
|
The class has an implicit `constexpr` constructor that chews a bunch of
|
||||||
|
characters. Once created, one can get the original string by means of the `data`
|
||||||
|
member function or convert the instance into a number.<br/>
|
||||||
|
A hashed string is well suited wherever a constant expression is required. No
|
||||||
|
_string-to-number_ conversion will take place at runtime if used carefully.
|
||||||
|
|
||||||
|
Example of use:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto load(entt::hashed_string::hash_type resource) {
|
||||||
|
// uses the numeric representation of the resource to load and return it
|
||||||
|
}
|
||||||
|
|
||||||
|
auto resource = load(entt::hashed_string{"gui/background"});
|
||||||
|
```
|
||||||
|
|
||||||
|
There is also a _user defined literal_ dedicated to hashed strings to make them
|
||||||
|
more _user-friendly_:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using namespace entt::literals;
|
||||||
|
constexpr auto str = "text"_hs;
|
||||||
|
```
|
||||||
|
|
||||||
|
User defined literals in `EnTT` are enclosed in the `entt::literals` namespace.
|
||||||
|
Therefore, the entire namespace or selectively the literal of interest must be
|
||||||
|
explicitly included before each use, a bit like `std::literals`.<br/>
|
||||||
|
The class also offers the necessary functionalities to create hashed strings at
|
||||||
|
runtime:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::string orig{"text"};
|
||||||
|
|
||||||
|
// create a full-featured hashed string...
|
||||||
|
entt::hashed_string str{orig.c_str()};
|
||||||
|
|
||||||
|
// ... or compute only the unique identifier
|
||||||
|
const auto hash = entt::hashed_string::value(orig.c_str());
|
||||||
|
```
|
||||||
|
|
||||||
|
This possibility should not be exploited in tight loops, since the computation
|
||||||
|
takes place at runtime and no longer at compile-time. It could therefore affect
|
||||||
|
performance to some degrees.
|
||||||
|
|
||||||
|
## Wide characters
|
||||||
|
|
||||||
|
The `hashed_string` class is an alias for `basic_hashed_string<char>`. To use
|
||||||
|
the C++ type for wide character representations, there exists also the alias
|
||||||
|
`hashed_wstring` for `basic_hashed_string<wchar_t>`.<br/>
|
||||||
|
In this case, the user defined literal to use to create hashed strings on the
|
||||||
|
fly is `_hws`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
constexpr auto str = L"text"_hws;
|
||||||
|
```
|
||||||
|
|
||||||
|
The hash type of `hashed_wstring` is the same as its counterpart.
|
||||||
|
|
||||||
|
## Conflicts
|
||||||
|
|
||||||
|
The hashed string class uses FNV-1a internally to hash strings. Because of the
|
||||||
|
_pigeonhole principle_, conflicts are possible. This is a fact.<br/>
|
||||||
|
There is no silver bullet to solve the problem of conflicts when dealing with
|
||||||
|
hashing functions. In this case, the best solution is likely to give up. That is
|
||||||
|
all.<br/>
|
||||||
|
After all, human-readable unique identifiers are not something strictly defined
|
||||||
|
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.
|
||||||
|
|
||||||
|
# Iterators
|
||||||
|
|
||||||
|
Writing and working with iterators is not always easy. More often than not it
|
||||||
|
also leads to duplicated code.<br/>
|
||||||
|
`EnTT` tries to overcome this problem by offering some utilities designed to
|
||||||
|
make this hard work easier.
|
||||||
|
|
||||||
|
## Input iterator pointer
|
||||||
|
|
||||||
|
When writing an input iterator that returns in-place constructed values if
|
||||||
|
dereferenced, it is not always straightforward to figure out what `value_type`
|
||||||
|
is and how to make it behave like a full-fledged pointer.<br/>
|
||||||
|
Conversely, it would be very useful to have an `operator->` available on the
|
||||||
|
iterator itself that always works without too much complexity.
|
||||||
|
|
||||||
|
The input iterator pointer is meant for this. It is a small class that wraps the
|
||||||
|
in-place constructed value and adds some functions on top of it to make it
|
||||||
|
suitable for use with input iterators:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct iterator_type {
|
||||||
|
using value_type = std::pair<first_type, second_type>;
|
||||||
|
using pointer = input_iterator_pointer<value_type>;
|
||||||
|
using reference = value_type;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The library makes extensive use of this class internally. In many cases, the
|
||||||
|
`value_type` of the returned iterators is just an input iterator pointer.
|
||||||
|
|
||||||
|
## Iota iterator
|
||||||
|
|
||||||
|
Waiting for C++20, this iterator accepts an integral value and returns all
|
||||||
|
elements in a certain range:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::iota_iterator first{0};
|
||||||
|
entt::iota_iterator last{100};
|
||||||
|
|
||||||
|
for(; first != last; ++first) {
|
||||||
|
int value = *first;
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the future, views will replace this class. Meanwhile, the library makes some
|
||||||
|
interesting uses of it when a range of integral values is to be returned to the
|
||||||
|
user.
|
||||||
|
|
||||||
|
## Iterable adaptor
|
||||||
|
|
||||||
|
Typically, a container class provides `begin` and `end` member functions (with
|
||||||
|
their const counterparts) for iteration.<br/>
|
||||||
|
However, it can happen that a class offers multiple iteration methods or allows
|
||||||
|
users to iterate different sets of _elements_.
|
||||||
|
|
||||||
|
The iterable adaptor is a utility class that makes it easier to use and access
|
||||||
|
data in this case.<br/>
|
||||||
|
It accepts a couple of iterators (or an iterator and a sentinel) and offers an
|
||||||
|
_iterable_ object with all the expected methods like `begin`, `end` and whatnot.
|
||||||
|
|
||||||
|
The library uses this class extensively.<br/>
|
||||||
|
Think for example of views, which can be iterated to access entities but also
|
||||||
|
offer a method for obtaining an iterable object that returns tuples of entities
|
||||||
|
and components at once.<br/>
|
||||||
|
Another example is the registry class which allows users to iterate its storage
|
||||||
|
by returning an iterable object for the purpose.
|
||||||
|
|
||||||
|
# Memory
|
||||||
|
|
||||||
|
There are a handful of tools within `EnTT` to interact with memory in one way or
|
||||||
|
another.<br/>
|
||||||
|
Some are geared towards simplifying the implementation of (internal or external)
|
||||||
|
allocator aware containers. Others are designed to help the developer with
|
||||||
|
everyday problems.
|
||||||
|
|
||||||
|
The former are very specific and for niche problems. These are tools designed to
|
||||||
|
unwrap fancy or plain pointers (`to_address`) or to help forget the meaning of
|
||||||
|
acronyms like _POCCA_, _POCMA_ or _POCS_.<br/>
|
||||||
|
I will not describe them here in detail. Instead, I recommend reading the inline
|
||||||
|
documentation to those interested in the subject.
|
||||||
|
|
||||||
|
## Allocator aware unique pointers
|
||||||
|
|
||||||
|
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
|
||||||
|
support allocators while unique pointers do not.<br/>
|
||||||
|
There is a proposal at the moment that also shows (among the other things) how
|
||||||
|
this can be implemented without any compiler support.
|
||||||
|
|
||||||
|
The `allocate_unique` function follows this proposal, making a virtue out of
|
||||||
|
necessity:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::unique_ptr<my_type, entt::allocation_deleter<my_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
|
||||||
|
```
|
||||||
|
|
||||||
|
Although the internal implementation is slightly different from what is proposed
|
||||||
|
for the standard, this function offers an API that is a drop-in replacement for
|
||||||
|
the same feature.
|
||||||
|
|
||||||
|
# Monostate
|
||||||
|
|
||||||
|
The monostate pattern is often presented as an alternative to a singleton based
|
||||||
|
configuration system.<br/>
|
||||||
|
This is exactly its purpose in `EnTT`. Moreover, this implementation is thread
|
||||||
|
safe by design (hopefully).
|
||||||
|
|
||||||
|
Keys are integral values (easily obtained by hashed strings), values are basic
|
||||||
|
types like `int`s or `bool`s. Values of different types can be associated with
|
||||||
|
each key, even more than one at a time.<br/>
|
||||||
|
Because of this, one should pay attention to use the same type both during an
|
||||||
|
assignment and when trying to read back the data. Otherwise, there is the risk
|
||||||
|
to incur in unexpected results.
|
||||||
|
|
||||||
|
Example of use:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::monostate<entt::hashed_string{"mykey"}>{} = true;
|
||||||
|
entt::monostate<"mykey"_hs>{} = 42;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
const bool b = entt::monostate<"mykey"_hs>{};
|
||||||
|
const int i = entt::monostate<entt::hashed_string{"mykey"}>{};
|
||||||
|
```
|
||||||
|
|
||||||
|
# Type support
|
||||||
|
|
||||||
|
`EnTT` provides some basic information about types of all kinds.<br/>
|
||||||
|
It also offers additional features that are not yet available in the standard
|
||||||
|
library or that will never be.
|
||||||
|
|
||||||
|
## Built-in RTTI support
|
||||||
|
|
||||||
|
Runtime type identification support (or RTTI) is one of the most frequently
|
||||||
|
disabled features in the C++ world, especially in the gaming sector. Regardless
|
||||||
|
of the reasons for this, it is often a shame not to be able to rely on opaque
|
||||||
|
type information at runtime.<br/>
|
||||||
|
The library tries to fill this gap by offering a built-in system that does not
|
||||||
|
serve as a replacement but comes very close to being one and offers similar
|
||||||
|
information to that provided by its counterpart.
|
||||||
|
|
||||||
|
Basically, the whole system relies on a handful of classes. In particular:
|
||||||
|
|
||||||
|
* The unique sequential identifier associated with a given type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto index = entt::type_index<a_type>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
The returned value is not guaranteed to be stable across different runs.<br/>
|
||||||
|
However, it can be very useful as index in associative and unordered
|
||||||
|
associative containers or for positional accesses in a vector or an array.
|
||||||
|
|
||||||
|
An external generator can also be used if needed. In fact, `type_index` can be
|
||||||
|
specialized by type and is also _sfinae-friendly_ in order to allow more
|
||||||
|
refined specializations such as:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename Type>
|
||||||
|
struct entt::type_index<Type, std::void_d<decltype(Type::index())>> {
|
||||||
|
static entt::id_type value() noexcept {
|
||||||
|
return Type::index();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Indexes **must** be sequentially generated in this case.<br/>
|
||||||
|
The tool is widely used within `EnTT`. Generating indices not sequentially
|
||||||
|
would break an assumption and would likely lead to undesired behaviors.
|
||||||
|
|
||||||
|
* The hash value associated with a given type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto hash = entt::type_hash<a_type>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
In general, the `value` function exposed by `type_hash` is also `constexpr`
|
||||||
|
but this is not guaranteed for all compilers and platforms (although it is
|
||||||
|
valid with the most well-known and popular ones).
|
||||||
|
|
||||||
|
This function **can** use non-standard features of the language for its own
|
||||||
|
purposes. This makes it possible to provide compile-time identifiers that
|
||||||
|
remain stable across different runs.<br/>
|
||||||
|
Users can prevent the library from using these features by means of the
|
||||||
|
`ENTT_STANDARD_CPP` definition. In this case, there is no guarantee that
|
||||||
|
identifiers remain stable across executions. Moreover, they are generated
|
||||||
|
at runtime and are no longer a compile-time thing.
|
||||||
|
|
||||||
|
As it happens with `type_index`, also `type_hash` is a _sfinae-friendly_ class
|
||||||
|
that can be specialized in order to customize its behavior globally or on a
|
||||||
|
per-type or per-traits basis.
|
||||||
|
|
||||||
|
* The name associated with a given type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto name = entt::type_name<a_type>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
This value is extracted from some information generally made available by the
|
||||||
|
compiler in use. Therefore, it may differ depending on the compiler and may be
|
||||||
|
empty in the event that this information is not available.<br/>
|
||||||
|
For example, given the following class:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_type { /* ... */ };
|
||||||
|
```
|
||||||
|
|
||||||
|
The name is `my_type` when compiled with GCC or CLang and `struct my_type`
|
||||||
|
when MSVC is in use.<br/>
|
||||||
|
Most of the time the name is also retrieved at compile-time and is therefore
|
||||||
|
always returned through an `std::string_view`. Users can easily access it and
|
||||||
|
modify it as needed, for example by removing the word `struct` to normalize
|
||||||
|
the result. `EnTT` does not do this for obvious reasons, since it would be
|
||||||
|
creating a new string at runtime otherwise.
|
||||||
|
|
||||||
|
This function **can** use non-standard features of the language for its own
|
||||||
|
purposes. Users can prevent the library from using these features by means of
|
||||||
|
the `ENTT_STANDARD_CPP` definition. In this case, the name is just empty.
|
||||||
|
|
||||||
|
As it happens with `type_index`, also `type_name` is a _sfinae-friendly_ class
|
||||||
|
that can be specialized in order to customize its behavior globally or on a
|
||||||
|
per-type or per-traits basis.
|
||||||
|
|
||||||
|
These are then combined into utilities that aim to offer an API that is somewhat
|
||||||
|
similar to that made available by the standard library.
|
||||||
|
|
||||||
|
### Type info
|
||||||
|
|
||||||
|
The `type_info` class is not a drop-in replacement for `std::type_info` but can
|
||||||
|
provide similar information which are not implementation defined and do not
|
||||||
|
require to enable RTTI.<br/>
|
||||||
|
Therefore, they can sometimes be even more reliable than those obtained
|
||||||
|
otherwise.
|
||||||
|
|
||||||
|
Its type defines an opaque class that is also copyable and movable.<br/>
|
||||||
|
Objects of this type are generally returned by the `type_id` functions:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// by type
|
||||||
|
auto info = entt::type_id<a_type>();
|
||||||
|
|
||||||
|
// by value
|
||||||
|
auto other = entt::type_id(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
All elements thus received are nothing more than const references to instances
|
||||||
|
of `type_info` with static storage duration.<br/>
|
||||||
|
This is convenient for saving the entire object aside for the cost of a pointer.
|
||||||
|
However, nothing prevents from constructing `type_info` objects directly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::type_info info{std::in_place_type<int>};
|
||||||
|
```
|
||||||
|
|
||||||
|
These are the information made available by `type_info`:
|
||||||
|
|
||||||
|
* The index associated with a given type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto idx = entt::type_id<a_type>().index();
|
||||||
|
```
|
||||||
|
|
||||||
|
This is also an alias for the following:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto idx = entt::type_index<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
* The hash value associated with a given type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto hash = entt::type_id<a_type>().hash();
|
||||||
|
```
|
||||||
|
|
||||||
|
This is also an alias for the following:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto hash = entt::type_hash<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
* The name associated with a given type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto name = entt::type_id<my_type>().name();
|
||||||
|
```
|
||||||
|
|
||||||
|
This is also an alias for the following:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto name = entt::type_name<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
Where all accessed features are available at compile-time, the `type_info` class
|
||||||
|
is also fully `constexpr`. However, this cannot be guaranteed in advance and
|
||||||
|
depends mainly on the compiler in use and any specializations of the classes
|
||||||
|
described above.
|
||||||
|
|
||||||
|
### Almost unique identifiers
|
||||||
|
|
||||||
|
Since the default non-standard, compile-time implementation of `type_hash` makes
|
||||||
|
use of hashed strings, it may happen that two types are assigned the same hash
|
||||||
|
value.<br/>
|
||||||
|
In fact, although this is quite rare, it is not entirely excluded.
|
||||||
|
|
||||||
|
Another case where two types are assigned the same identifier is when classes
|
||||||
|
from different contexts (for example two or more libraries loaded at runtime)
|
||||||
|
have the same fully qualified name. In this case, `type_name` returns the same
|
||||||
|
value for the two types.<br/>
|
||||||
|
Fortunately, there are several easy ways to deal with this:
|
||||||
|
|
||||||
|
* The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime
|
||||||
|
identifiers do not suffer from the same problem in fact. However, this
|
||||||
|
solution does not work well with a plugin system, where the libraries are not
|
||||||
|
linked.
|
||||||
|
|
||||||
|
* Another possibility is to specialize the `type_name` class for one of the
|
||||||
|
conflicting types, in order to assign it a custom identifier. This is probably
|
||||||
|
the easiest solution that also preserves the feature of the tool.
|
||||||
|
|
||||||
|
* A fully customized identifier generation policy (based for example on enum
|
||||||
|
classes or preprocessing steps) may represent yet another option.
|
||||||
|
|
||||||
|
These are just some examples of possible approaches to the problem, but there
|
||||||
|
are many others. As already mentioned above, since users have full control over
|
||||||
|
their types, this problem is in any case easy to solve and should not worry too
|
||||||
|
much.<br/>
|
||||||
|
In all likelihood, it will never happen to run into a conflict anyway.
|
||||||
|
|
||||||
|
## Type traits
|
||||||
|
|
||||||
|
A handful of utilities and traits not present in the standard template library
|
||||||
|
but which can be useful in everyday life.<br/>
|
||||||
|
This list **is not** exhaustive and contains only some of the most useful
|
||||||
|
classes. Refer to the inline documentation for more information on the features
|
||||||
|
offered by this module.
|
||||||
|
|
||||||
|
### Size of
|
||||||
|
|
||||||
|
The standard operator `sizeof` complains if users provide it with functions or
|
||||||
|
incomplete types. On the other hand, it is guaranteed that its result is always
|
||||||
|
non-zero, even if applied to an empty class type.<br/>
|
||||||
|
This small class combines the two and offers an alternative to `sizeof` that
|
||||||
|
works under all circumstances, returning zero if the type is not supported:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
const auto size = entt::size_of_v<void>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Is applicable
|
||||||
|
|
||||||
|
The standard library offers the great `std::is_invocable` trait in several
|
||||||
|
forms. This takes a function type and a series of arguments and returns true if
|
||||||
|
the condition is satisfied.<br/>
|
||||||
|
Moreover, users are also provided with `std::apply`, a tool for combining
|
||||||
|
invocable elements and tuples of arguments.
|
||||||
|
|
||||||
|
It would therefore be a good idea to have a variant of `std::is_invocable` that
|
||||||
|
also accepts its arguments in the form of a tuple-like type, so as to complete
|
||||||
|
the offer:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
constexpr bool result = entt::is_applicable<Func, std::tuple<a_type, another_type>>;
|
||||||
|
```
|
||||||
|
|
||||||
|
This trait is built on top of `std::is_invocable` and does nothing but unpack a
|
||||||
|
tuple-like type and simplify the code at the call site.
|
||||||
|
|
||||||
|
### Constness as
|
||||||
|
|
||||||
|
A utility to easily transfer the constness of a type to another type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// type is const dst_type because of the constness of src_type
|
||||||
|
using type = entt::constness_as_t<dst_type, const src_type>;
|
||||||
|
```
|
||||||
|
|
||||||
|
The trait is subject to the rules of the language. For example, _transferring_
|
||||||
|
constness between references will not give the desired effect.
|
||||||
|
|
||||||
|
### Member class type
|
||||||
|
|
||||||
|
The `auto` template parameter introduced with C++17 made it possible to simplify
|
||||||
|
many class templates and template functions but also made the class type opaque
|
||||||
|
when members are passed as template arguments.<br/>
|
||||||
|
The purpose of this utility is to extract the class type in a few lines of code:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename Member>
|
||||||
|
using clazz = entt::member_class_t<Member>;
|
||||||
|
```
|
||||||
|
|
||||||
|
### N-th argument
|
||||||
|
|
||||||
|
A utility to quickly find the n-th argument of a function, member function or
|
||||||
|
data member (for blind operations on opaque types):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using type = entt::nth_argument_t<1u, decltype(&clazz::member)>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Disambiguation of overloaded functions is the responsibility of the user, should
|
||||||
|
it be needed.
|
||||||
|
|
||||||
|
### Integral constant
|
||||||
|
|
||||||
|
Since `std::integral_constant` may be annoying because of its form that requires
|
||||||
|
to specify both a type and a value of that type, there is a more user-friendly
|
||||||
|
shortcut for the creation of integral constants.<br/>
|
||||||
|
This shortcut is the alias template `entt::integral_constant`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
constexpr auto constant = entt::integral_constant<42>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Among the other uses, when combined with a hashed string it helps to define tags
|
||||||
|
as human-readable _names_ where actual types would be required otherwise:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
constexpr auto enemy_tag = entt::integral_constant<"enemy"_hs>;
|
||||||
|
registry.emplace<enemy_tag>(entity);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tag
|
||||||
|
|
||||||
|
Type `id_type` is very important and widely used in `EnTT`. Therefore, there is
|
||||||
|
a more user-friendly shortcut for the creation of constants based on it.<br/>
|
||||||
|
This shortcut is the alias template `entt::tag`.
|
||||||
|
|
||||||
|
If used in combination with hashed strings, it helps to use human-readable names
|
||||||
|
where types would be required otherwise. As an example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
registry.emplace<entt::tag<"enemy"_hs>>(entity);
|
||||||
|
```
|
||||||
|
|
||||||
|
However, this is not the only permitted use. Literally any value convertible to
|
||||||
|
`id_type` is a good candidate, such as the named constants of an unscoped enum.
|
||||||
|
|
||||||
|
### Type list and value list
|
||||||
|
|
||||||
|
There is no respectable library where the much desired _type list_ can be
|
||||||
|
missing.<br/>
|
||||||
|
`EnTT` is no exception and provides (making extensive use of it internally) the
|
||||||
|
`type_list` type, in addition to its `value_list` counterpart dedicated to
|
||||||
|
non-type template parameters.
|
||||||
|
|
||||||
|
Here is a (possibly incomplete) list of the functionalities that come with a
|
||||||
|
type list:
|
||||||
|
|
||||||
|
* `type_list_element[_t]` to get the N-th element of a type list.
|
||||||
|
* `type_list_index[_v]` to get the index of a given element of a type list.
|
||||||
|
* `type_list_cat[_t]` and a handy `operator+` to concatenate type lists.
|
||||||
|
* `type_list_unique[_t]` to remove duplicate types from a type list.
|
||||||
|
* `type_list_contains[_v]` to know if a type list contains a given type.
|
||||||
|
* `type_list_diff[_t]` to remove types from type lists.
|
||||||
|
* `type_list_transform[_t]` to _transform_ a range and create another type list.
|
||||||
|
|
||||||
|
I am also pretty sure that more and more utilities will be added over time as
|
||||||
|
needs become apparent.<br/>
|
||||||
|
Many of these functionalities also exist in their version dedicated to value
|
||||||
|
lists. We therefore have `value_list_element[_v]` as well as
|
||||||
|
`value_list_cat[_t]`and so on.
|
||||||
|
|
||||||
|
# Unique sequential identifiers
|
||||||
|
|
||||||
|
Sometimes it is useful to be able to give unique, sequential numeric identifiers
|
||||||
|
to types either at compile-time or runtime.<br/>
|
||||||
|
There are plenty of different solutions for this out there, and I could have
|
||||||
|
used one of them. However, I decided to spend my time to define a couple of
|
||||||
|
tools that fully embrace what modern C++ has to offer.
|
||||||
|
|
||||||
|
## Compile-time generator
|
||||||
|
|
||||||
|
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
|
||||||
|
`ident` class template:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// defines the identifiers for the given types
|
||||||
|
using id = entt::ident<a_type, another_type>;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
switch(a_type_identifier) {
|
||||||
|
case id::value<a_type>:
|
||||||
|
// ...
|
||||||
|
break;
|
||||||
|
case id::value<another_type>:
|
||||||
|
// ...
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This is what this class template has to offer: a `value` inline variable that
|
||||||
|
contains a numeric 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
|
||||||
|
stable across different runs. If used in a production environment where a type
|
||||||
|
needs to be removed, a placeholder can help to leave the other identifiers the
|
||||||
|
same:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename>
|
||||||
|
struct ignore_type {};
|
||||||
|
|
||||||
|
using id = entt::ident<
|
||||||
|
a_type_still_valid,
|
||||||
|
ignore_type<no_longer_valid_type>,
|
||||||
|
another_type_still_valid
|
||||||
|
>;
|
||||||
|
```
|
||||||
|
|
||||||
|
Perhaps a bit ugly to see in a codebase, but it gets the job done at least.
|
||||||
|
|
||||||
|
## Runtime generator
|
||||||
|
|
||||||
|
The `family` class template helps to generate sequential numeric identifiers for
|
||||||
|
types at runtime:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// defines a custom generator
|
||||||
|
using id = entt::family<struct my_tag>;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
const auto a_type_id = id::value<a_type>;
|
||||||
|
const auto another_type_id = id::value<another_type>;
|
||||||
|
```
|
||||||
|
|
||||||
|
This is what a _family_ has to offer: a `value` inline variable that contains a
|
||||||
|
numeric identifier for the given type.<br/>
|
||||||
|
The generator is customizable, so as to get different _sequences_ for different
|
||||||
|
purposes if needed.
|
||||||
|
|
||||||
|
Identifiers are not guaranteed to be stable across different runs. Indeed it
|
||||||
|
mostly depends on the flow of execution.
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
|
||||||
|
It is not possible to escape the temptation to add utilities of some kind to a
|
||||||
|
library. In fact, `EnTT` also provides a handful of tools to simplify the
|
||||||
|
life of developers:
|
||||||
|
|
||||||
|
* `entt::identity`: the identity function object that will be available with
|
||||||
|
C++20. It returns its argument unchanged and nothing more. It is useful as a
|
||||||
|
sort of _do nothing_ function in template programming.
|
||||||
|
|
||||||
|
* `entt::overload`: a tool to disambiguate different overloads from their
|
||||||
|
function type. It works with both free and member functions.<br/>
|
||||||
|
Consider the following definition:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct clazz {
|
||||||
|
void bar(int) {}
|
||||||
|
void bar() {}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
This utility can be used to get the _right_ overload as:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto *member = entt::overload<void(int)>(&clazz::bar);
|
||||||
|
```
|
||||||
|
|
||||||
|
The line above is literally equivalent to:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto *member = static_cast<void(clazz:: *)(int)>(&clazz::bar);
|
||||||
|
```
|
||||||
|
|
||||||
|
Just easier to read and shorter to type.
|
||||||
|
|
||||||
|
* `entt::overloaded`: a small class template used to create a new type with an
|
||||||
|
overloaded `operator()` from a bunch of lambdas or functors.<br/>
|
||||||
|
As an example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::overloaded func{
|
||||||
|
[](int value) { /* ... */ },
|
||||||
|
[](char value) { /* ... */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
func(42);
|
||||||
|
func('c');
|
||||||
|
```
|
||||||
|
|
||||||
|
Rather useful when doing metaprogramming and having to pass to a function a
|
||||||
|
callable object that supports multiple types at once.
|
||||||
|
|
||||||
|
* `entt::y_combinator`: this is a C++ implementation of **the** _y-combinator_.
|
||||||
|
If it is not clear what it is, there is probably no need for this
|
||||||
|
utility.<br/>
|
||||||
|
Below is a small example to show its use:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::y_combinator gauss([](const auto &self, auto value) -> unsigned int {
|
||||||
|
return value ? (value + self(value-1u)) : 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto result = gauss(3u);
|
||||||
|
```
|
||||||
|
|
||||||
|
Maybe convoluted at first glance but certainly effective. Unfortunately,
|
||||||
|
the language does not make it possible to do much better.
|
||||||
|
|
||||||
|
This is a rundown of the (actually few) utilities made available by `EnTT`. The
|
||||||
|
list will probably grow over time, but the size of each will remain rather
|
||||||
|
small, as has been the case so far.
|
||||||
Vendored
+2421
File diff suppressed because it is too large
Load Diff
Vendored
+208
@@ -0,0 +1,208 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [FAQ](#faq)
|
||||||
|
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
|
||||||
|
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
|
||||||
|
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
|
||||||
|
* [Warning C4003: the min, the max and the macro](#warning-c4003-the-min-the-max-and-the-macro)
|
||||||
|
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
|
||||||
|
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
|
||||||
|
* [Duplicate storage for the same component](#duplicate-storage-for-the-same-component)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
This is a constantly updated section where I am trying to put the answers to the
|
||||||
|
most frequently asked questions.<br/>
|
||||||
|
If you do not find your answer here, there are two cases: nobody has done it
|
||||||
|
yet, or this section needs updating. In both cases, you can
|
||||||
|
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter either
|
||||||
|
the [gitter channel](https://gitter.im/skypjack/entt) or the
|
||||||
|
[discord server](https://discord.gg/5BjPWBd) to ask for help.<br/>
|
||||||
|
Probably someone already has an answer for you and we can then integrate this
|
||||||
|
part of the documentation.
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
## Why is my debug build on Windows so slow?
|
||||||
|
|
||||||
|
`EnTT` is an experimental project that I also use to keep me up-to-date with the
|
||||||
|
latest revision of the language and the standard library. For this reason, it is
|
||||||
|
likely that some classes you are working with are using standard containers
|
||||||
|
under the hood.<br/>
|
||||||
|
Unfortunately, it is known that the standard containers are not particularly
|
||||||
|
performing in debugging (the reasons for this go beyond this document) and are
|
||||||
|
even less so on Windows, apparently. Fortunately, this can also be mitigated a
|
||||||
|
lot, achieving good results in many cases.
|
||||||
|
|
||||||
|
First of all, there are two things to do in a Windows project:
|
||||||
|
|
||||||
|
* Disable the [`/JMC`](https://docs.microsoft.com/cpp/build/reference/jmc)
|
||||||
|
option (_Just My Code_ debugging), available starting with Visual Studio 2017
|
||||||
|
version 15.8.
|
||||||
|
|
||||||
|
* Set the [`_ITERATOR_DEBUG_LEVEL`](https://docs.microsoft.com/cpp/standard-library/iterator-debug-level)
|
||||||
|
macro to 0. This will disable checked iterators and iterator debugging.
|
||||||
|
|
||||||
|
Moreover, set the `ENTT_DISABLE_ASSERT` variable or redefine the `ENTT_ASSERT`
|
||||||
|
macro to disable internal debug checks in `EnTT`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define ENTT_ASSERT(...) ((void)0)
|
||||||
|
```
|
||||||
|
|
||||||
|
These asserts are introduced to help the users, but they require access to the
|
||||||
|
underlying containers and therefore risk ruining the performance in some cases.
|
||||||
|
|
||||||
|
With these changes, debug performance should increase enough in most cases. If
|
||||||
|
you want something more, you can also switch to an optimization level `O0` or
|
||||||
|
preferably `O1`.
|
||||||
|
|
||||||
|
## How can I represent hierarchies with my components?
|
||||||
|
|
||||||
|
This is one of the first questions that anyone makes when starting to work with
|
||||||
|
the entity-component-system architectural pattern.<br/>
|
||||||
|
There are several approaches to the problem, and the best one depends mainly on
|
||||||
|
the real problem one is facing. In all cases, how to do it does not strictly
|
||||||
|
depend on the library in use, but the latter certainly allows or not different
|
||||||
|
techniques depending on how the data are laid out.
|
||||||
|
|
||||||
|
I tried to describe some of the approaches that fit well with the model of
|
||||||
|
`EnTT`. [This](https://skypjack.github.io/2019-06-25-ecs-baf-part-4/) is the
|
||||||
|
first post of a series that tries to _explore_ the problem. More will probably
|
||||||
|
come in the future.<br/>
|
||||||
|
In addition, `EnTT` also offers the possibility to create stable storage types
|
||||||
|
and therefore have pointer stability for one, all or some components. This is by
|
||||||
|
far the most convenient solution when it comes to creating hierarchies and
|
||||||
|
whatnot. See the documentation for the ECS part of the library and in particular
|
||||||
|
what concerns the `component_traits` class for further details.
|
||||||
|
|
||||||
|
## Custom entity identifiers: yay or nay?
|
||||||
|
|
||||||
|
Custom entity identifiers are definitely a good idea in two cases at least:
|
||||||
|
|
||||||
|
* If `std::uint32_t` is not large enough for your purposes, since this is the
|
||||||
|
underlying type of `entt::entity`.
|
||||||
|
|
||||||
|
* If you want to avoid conflicts when using multiple registries.
|
||||||
|
|
||||||
|
Identifiers can be defined through enum classes and class types that define an
|
||||||
|
`entity_type` member of type `std::uint32_t` or `std::uint64_t`.<br/>
|
||||||
|
In fact, this is a definition equivalent to that of `entt::entity`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
enum class entity: std::uint32_t {};
|
||||||
|
```
|
||||||
|
|
||||||
|
There is no limit to the number of identifiers that can be defined.
|
||||||
|
|
||||||
|
## Warning C4003: the min, the max and the macro
|
||||||
|
|
||||||
|
On Windows, a header file defines two macros `min` and `max` which may result in
|
||||||
|
conflicts with their counterparts in the standard library and therefore in
|
||||||
|
errors during compilation.
|
||||||
|
|
||||||
|
It is a pretty big problem. However, fortunately it is not a problem of `EnTT`
|
||||||
|
and there is a fairly simple solution to it.<br/>
|
||||||
|
It consists in defining the `NOMINMAX` macro before including any other header
|
||||||
|
so as to get rid of the extra definitions:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#define NOMINMAX
|
||||||
|
```
|
||||||
|
|
||||||
|
Please refer to [this](https://github.com/skypjack/entt/issues/96) issue for
|
||||||
|
more details.
|
||||||
|
|
||||||
|
## The standard and the non-copyable types
|
||||||
|
|
||||||
|
`EnTT` uses internally the trait `std::is_copy_constructible_v` to check if a
|
||||||
|
component is actually copyable. However, this trait does not really check
|
||||||
|
whether a type is actually copyable. Instead, it just checks that a suitable
|
||||||
|
copy constructor and copy operator exist.<br/>
|
||||||
|
This can lead to surprising results due to some idiosyncrasies of the standard.
|
||||||
|
|
||||||
|
For example, `std::vector` defines a copy constructor that is conditionally
|
||||||
|
enabled depending on whether the value type is copyable or not. As a result,
|
||||||
|
`std::is_copy_constructible_v` returns true for the following specialization:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct type {
|
||||||
|
std::vector<std::unique_ptr<action>> vec;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
However, the copy constructor is effectively disabled upon specialization.
|
||||||
|
Therefore, trying to assign an instance of this type to an entity may trigger a
|
||||||
|
compilation error.<br/>
|
||||||
|
As a workaround, users can mark the type explicitly as non-copyable. This also
|
||||||
|
suppresses the implicit generation of the move constructor and operator, which
|
||||||
|
will therefore have to be defaulted accordingly:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct type {
|
||||||
|
type(const type &) = delete;
|
||||||
|
type(type &&) = default;
|
||||||
|
|
||||||
|
type & operator=(const type &) = delete;
|
||||||
|
type & operator=(type &&) = default;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<action>> vec;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that aggregate initialization is also disabled as a consequence.<br/>
|
||||||
|
Fortunately, this type of trick is quite rare. The bad news is that there is no
|
||||||
|
way to deal with it at the library level, this being due to the design of the
|
||||||
|
language. On the other hand, the fact that the language itself also offers a way
|
||||||
|
to mitigate the problem makes it manageable.
|
||||||
|
|
||||||
|
## Which functions trigger which signals
|
||||||
|
|
||||||
|
Storage classes offer three _signals_ that are emitted following specific
|
||||||
|
operations. Maybe not everyone knows what these operations are, though.<br/>
|
||||||
|
If this is not clear, below you can find a _vademecum_ for this purpose:
|
||||||
|
|
||||||
|
* `on_created` is invoked when a component is first added (neither modified nor
|
||||||
|
replaced) to an entity.
|
||||||
|
|
||||||
|
* `on_update` is called whenever an existing component is modified or replaced.
|
||||||
|
|
||||||
|
* `on_destroyed` is called when a component is explicitly or implicitly removed
|
||||||
|
from an entity.
|
||||||
|
|
||||||
|
Among the most controversial functions can be found `emplace_or_replace` and
|
||||||
|
`destroy`. However, following the above rules, it is quite simple to know what
|
||||||
|
will happen.<br/>
|
||||||
|
In the first case, `on_created` is invoked if the entity has not the component,
|
||||||
|
otherwise the latter is replaced and therefore `on_update` is triggered. As for
|
||||||
|
the second case, components are removed from their entities and thus freed when
|
||||||
|
they are recycled. It means that `on_destroyed` is triggered for every component
|
||||||
|
owned by the entity that is destroyed.
|
||||||
|
|
||||||
|
## Duplicate storage for the same component
|
||||||
|
|
||||||
|
It is rare, but you can see double sometimes, especially when it comes to
|
||||||
|
storage. This can be caused by a conflict in the hash assigned to the various
|
||||||
|
component types (one of a kind) or by bugs in your compiler
|
||||||
|
([more common](https://github.com/skypjack/entt/issues/1063) apparently).<br/>
|
||||||
|
Regardless of the cause, `EnTT` offers a customization point that also serves as
|
||||||
|
a solution in this case:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<>
|
||||||
|
struct entt::type_hash<Type> final {
|
||||||
|
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||||
|
return hashed_string::value("Type");
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr operator id_type() const noexcept {
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Specializing `type_hash` directly bypasses the default implementation offered by
|
||||||
|
`EnTT`, thus avoiding any possible conflicts or compiler bugs.
|
||||||
Vendored
+367
@@ -0,0 +1,367 @@
|
|||||||
|
# Crash Course: graph
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Data structures](#data-structures)
|
||||||
|
* [Adjacency matrix](#adjacency-matrix)
|
||||||
|
* [Graphviz dot language](#graphviz-dot-language)
|
||||||
|
* [Flow builder](#flow-builder)
|
||||||
|
* [Tasks and resources](#tasks-and-resources)
|
||||||
|
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
|
||||||
|
* [Sync points](#sync-points)
|
||||||
|
* [Execution graph](#execution-graph)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
`EnTT` does not aim to offer everything one needs to work with graphs.
|
||||||
|
Therefore, anyone looking for this in the _graph_ submodule will be
|
||||||
|
disappointed.<br/>
|
||||||
|
Quite the opposite is true though. This submodule is minimal and contains only
|
||||||
|
the data structures and algorithms strictly necessary for the development of
|
||||||
|
some tools such as the _flow builder_.
|
||||||
|
|
||||||
|
# Data structures
|
||||||
|
|
||||||
|
As anticipated in the introduction, the aim is not to offer all possible data
|
||||||
|
structures suitable for representing and working with graphs. Many will likely
|
||||||
|
be added or refined over time. However, I want to discourage anyone expecting
|
||||||
|
tight scheduling on the subject.<br/>
|
||||||
|
The data structures presented in this section are mainly useful for the
|
||||||
|
development and support of some tools that are also part of the same submodule.
|
||||||
|
|
||||||
|
## Adjacency matrix
|
||||||
|
|
||||||
|
The adjacency matrix is designed to represent either a directed or an undirected
|
||||||
|
graph:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{};
|
||||||
|
```
|
||||||
|
|
||||||
|
The `directed_tag` type _creates_ the graph as directed. There is also an
|
||||||
|
`undirected_tag` counterpart which creates it as undirected.<br/>
|
||||||
|
The interface deviates slightly from the typical double indexing of C and offers
|
||||||
|
an API that is perhaps more familiar to a C++ programmer. Therefore, the access
|
||||||
|
and modification of an element takes place via the `contains`, `insert` and
|
||||||
|
`erase` functions rather than a double call to an `operator[]`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if(adjacency_matrix.contains(0u, 1u)) {
|
||||||
|
adjacency_matrix.erase(0u, 1u);
|
||||||
|
} else {
|
||||||
|
adjacency_matrix.insert(0u, 1u);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Both `insert` and` erase` are _idempotent_ functions which have no effect if the
|
||||||
|
element already exists or has already been deleted.<br/>
|
||||||
|
The first one returns an `std::pair` containing the iterator to the element and
|
||||||
|
a boolean value indicating whether the element was newly inserted or not. The
|
||||||
|
second one returns the number of deleted elements (0 or 1).
|
||||||
|
|
||||||
|
An adjacency matrix is initialized with the number of elements (vertices) when
|
||||||
|
constructing it but can also be resized later using the `resize` function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u};
|
||||||
|
```
|
||||||
|
|
||||||
|
To visit all vertices, the class offers a function named `vertices` that returns
|
||||||
|
an iterable object suitable for the purpose:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for(auto &&vertex: adjacency_matrix.vertices()) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The same result is obtained with the following snippet, since the vertices are
|
||||||
|
plain unsigned integral values:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
As for visiting the edges, a few functions are available.<br/>
|
||||||
|
When the purpose is to visit all the edges of a given adjacency matrix, the
|
||||||
|
`edges` function returns an iterable object that is used to get them as pairs of
|
||||||
|
vertices:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for(auto [lhs, rhs]: adjacency_matrix.edges()) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
If the goal is to visit all the in- or out-edges of a given vertex instead, the
|
||||||
|
`in_edges` and `out_edges` functions are meant for that:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Both the functions expect the vertex to visit (that is, to return the in- or
|
||||||
|
out-edges for) as an argument.<br/>
|
||||||
|
Finally, the adjacency matrix is an allocator-aware container and offers most of
|
||||||
|
the functionalities one would expect from this type of containers, such as
|
||||||
|
`clear` or `get_allocator` and so on.
|
||||||
|
|
||||||
|
## Graphviz dot language
|
||||||
|
|
||||||
|
As it is one of the most popular formats, the library offers minimal support for
|
||||||
|
converting a graph to a Graphviz dot snippet.<br/>
|
||||||
|
The simplest way is to pass both an output stream and a graph to the `dot`
|
||||||
|
function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::ostringstream output{};
|
||||||
|
entt::dot(output, adjacency_matrix);
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to provide a callback to which the vertices are passed and
|
||||||
|
which can be used to add (`dot`) properties to the output as needed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::ostringstream output{};
|
||||||
|
|
||||||
|
entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) {
|
||||||
|
out << "label=\"v\"" << vertex << ",shape=\"box\"";
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This second mode is particularly convenient when the user wants to associate
|
||||||
|
externally managed data to the graph being converted.
|
||||||
|
|
||||||
|
# Flow builder
|
||||||
|
|
||||||
|
A flow builder is used to create execution graphs from tasks and resources.<br/>
|
||||||
|
The implementation is as generic as possible and does not bind to any other part
|
||||||
|
of the library.
|
||||||
|
|
||||||
|
This class is designed as a sort of _state machine_ to which a specific task is
|
||||||
|
attached for which the resources accessed in read-only or read-write mode are
|
||||||
|
specified.<br/>
|
||||||
|
Most of the functions in the API also return the flow builder itself, according
|
||||||
|
to what is the common sense API when it comes to builder classes.
|
||||||
|
|
||||||
|
Once all tasks are registered and resources assigned to them, an execution graph
|
||||||
|
in the form of an adjacency matrix is returned to the user.<br/>
|
||||||
|
This graph contains all the tasks assigned to the flow builder in the form of
|
||||||
|
_vertices_. The _vertex_ itself is used as an index to get the identifier passed
|
||||||
|
during registration.
|
||||||
|
|
||||||
|
## Tasks and resources
|
||||||
|
|
||||||
|
Although these terms are used extensively in the documentation, the flow builder
|
||||||
|
has no real concept of tasks and resources.<br/>
|
||||||
|
This class works mainly with _identifiers_, that is, values of type `id_type`.
|
||||||
|
In other terms, both tasks and resources are identified by integral values.<br/>
|
||||||
|
This allows not to couple the class itself to the rest of the library or to any
|
||||||
|
particular data structure. On the other hand, it requires the user to keep track
|
||||||
|
of the association between identifiers and operations or actual data.
|
||||||
|
|
||||||
|
Once a flow builder is created (which requires no constructor arguments), the
|
||||||
|
first thing to do is to bind a task. This tells the builder _who_ intends to
|
||||||
|
consume the resources that are specified immediately after:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::flow builder{};
|
||||||
|
builder.bind("task_1"_hs);
|
||||||
|
```
|
||||||
|
|
||||||
|
The example uses the `EnTT` hashed string to generate an identifier for the
|
||||||
|
task.<br/>
|
||||||
|
Indeed, the use of `id_type` as an identifier type is not by accident. In fact,
|
||||||
|
it matches well with the internal hashed string class. Moreover, it is also the
|
||||||
|
same type returned by the hash function of the internal RTTI system, in case the
|
||||||
|
user wants to rely on that.<br/>
|
||||||
|
However, being an integral value, it leaves the user full freedom to rely on his
|
||||||
|
own tools if necessary.
|
||||||
|
|
||||||
|
Once a task is associated with the flow builder, it has also assigned read-only
|
||||||
|
or read-write resources as appropriate:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.ro("resource_1"_hs)
|
||||||
|
.ro("resource_2"_hs)
|
||||||
|
.bind("task_2"_hs)
|
||||||
|
.rw("resource_2"_hs)
|
||||||
|
```
|
||||||
|
|
||||||
|
As mentioned, many functions return the builder itself, and it is therefore easy
|
||||||
|
to concatenate the different calls.<br/>
|
||||||
|
Also in the case of resources, they are identified by numeric values of type
|
||||||
|
`id_type`. As above, the choice is not entirely random. This goes well with the
|
||||||
|
tools offered by the library while leaving room for maximum flexibility.
|
||||||
|
|
||||||
|
Finally, both the `ro` and` rw` functions also offer an overload that accepts a
|
||||||
|
pair of iterators, so that one can pass a range of resources in one go.
|
||||||
|
|
||||||
|
### Rebinding
|
||||||
|
|
||||||
|
The `flow` class is resource based rather than task based. This means that graph
|
||||||
|
generation is driven by resources and not by the order of _appearance_ of tasks
|
||||||
|
during flow definition.<br/>
|
||||||
|
Although this concept is particularly important, it is almost irrelevant for the
|
||||||
|
vast majority of cases. However, it becomes relevant when _rebinding_ resources
|
||||||
|
or tasks.
|
||||||
|
|
||||||
|
In fact, nothing prevents rebinding elements to a flow.<br/>
|
||||||
|
However, the behavior changes slightly from case to case and has some nuances
|
||||||
|
that it is worth knowing about.
|
||||||
|
|
||||||
|
Directly rebinding a resource without the task being replaced trivially results
|
||||||
|
in the task's access mode for that resource being updated:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder.bind("task"_hs).rw("resource"_hs).ro("resource"_hs)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the resource is accessed in read-only mode, regardless of the
|
||||||
|
first call to `rw`.<br/>
|
||||||
|
Behind the scenes, the call does not actually _replace_ the previous one but is
|
||||||
|
queued after it instead, overwriting it when generating the graph. Thus, a large
|
||||||
|
number of resource rebindings may even impact processing times (very difficult
|
||||||
|
to observe but theoretically possible).
|
||||||
|
|
||||||
|
Rebinding resources and also combining it with changes to tasks has far more
|
||||||
|
implications instead.<br/>
|
||||||
|
As mentioned, graph generation takes place starting from resources and not from
|
||||||
|
tasks. Therefore, the result may not be as expected:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.ro("resource"_hs)
|
||||||
|
.bind("task_2"_hs)
|
||||||
|
.ro("resource"_hs)
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.rw("resource"_hs);
|
||||||
|
```
|
||||||
|
|
||||||
|
What happens here is that the resource first _sees_ a read-only access request
|
||||||
|
from the first task, then a read-only request from the second task and finally
|
||||||
|
a read-write request from the first task.<br/>
|
||||||
|
Although this definition would probably be counted as an error, the resulting
|
||||||
|
graph may be unexpected. In fact, this consists of a single edge outgoing from
|
||||||
|
the second task and directed to the first task.<br/>
|
||||||
|
To intuitively understand what happens, it is enough to think of the fact that a
|
||||||
|
task never has an edge pointing to itself.
|
||||||
|
|
||||||
|
While not obvious, this approach has its pros and cons like any other solution.
|
||||||
|
For example, creating loops is actually simple in the context of resource-based
|
||||||
|
graph generations:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.rw("resource"_hs)
|
||||||
|
.bind("task_2"_hs)
|
||||||
|
.rw("resource"_hs)
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.rw("resource"_hs);
|
||||||
|
```
|
||||||
|
|
||||||
|
As expected, this definition leads to the creation of two edges that define a
|
||||||
|
loop between the two tasks.
|
||||||
|
|
||||||
|
As a general rule, rebinding resources and tasks is highly discouraged because
|
||||||
|
it could lead to subtle bugs if users do not know what they are doing.<br/>
|
||||||
|
However, once the mechanisms of resource-based graph generation are understood,
|
||||||
|
it can offer to the expert user flexibility and a range of possibilities
|
||||||
|
otherwise inaccessible.
|
||||||
|
|
||||||
|
## Fake resources and order of execution
|
||||||
|
|
||||||
|
The flow builder does not offer the ability to specify when a task should run
|
||||||
|
before or after another task.<br/>
|
||||||
|
In fact, the order of _registration_ on the resources also determines the order
|
||||||
|
in which the tasks are processed during the generation of the execution graph.
|
||||||
|
|
||||||
|
However, there is a way to _force_ the execution order of two processes.<br/>
|
||||||
|
Briefly, since accessing a resource in opposite modes requires sequential rather
|
||||||
|
than parallel scheduling, it is possible to make use of fake resources to rule
|
||||||
|
on the execution order:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.ro("resource_1"_hs)
|
||||||
|
.rw("fake"_hs)
|
||||||
|
.bind("task_2"_hs)
|
||||||
|
.ro("resource_2"_hs)
|
||||||
|
.ro("fake"_hs)
|
||||||
|
.bind("task_3"_hs)
|
||||||
|
.ro("resource_2"_hs)
|
||||||
|
.ro("fake"_hs)
|
||||||
|
```
|
||||||
|
|
||||||
|
This snippet forces the execution of `task_1` **before** `task_2` and `task_3`.
|
||||||
|
This is due to the fact that the former sets a read-write requirement on a fake
|
||||||
|
resource that the other tasks also want to access in read-only mode.<br/>
|
||||||
|
Similarly, it is possible to force a task to run **after** a certain group:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder
|
||||||
|
.bind("task_1"_hs)
|
||||||
|
.ro("resource_1"_hs)
|
||||||
|
.ro("fake"_hs)
|
||||||
|
.bind("task_2"_hs)
|
||||||
|
.ro("resource_1"_hs)
|
||||||
|
.ro("fake"_hs)
|
||||||
|
.bind("task_3"_hs)
|
||||||
|
.ro("resource_2"_hs)
|
||||||
|
.rw("fake"_hs)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, since there are a number of processes that want to read a specific
|
||||||
|
resource, they will do so in parallel by forcing `task_3` to run after all the
|
||||||
|
other tasks.
|
||||||
|
|
||||||
|
## Sync points
|
||||||
|
|
||||||
|
Sometimes it is useful to assign the role of _sync point_ to a node.<br/>
|
||||||
|
Whether it accesses new resources or is simply a watershed, the procedure for
|
||||||
|
assigning this role to a vertex is always the same. First it is tied to the flow
|
||||||
|
builder, then the `sync` function is invoked:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
builder.bind("sync_point"_hs).sync();
|
||||||
|
```
|
||||||
|
|
||||||
|
The choice to assign an _identity_ to this type of node lies in the fact that,
|
||||||
|
more often than not, they also perform operations on resources.<br/>
|
||||||
|
If this is not the case, it will still be possible to create no-op vertices to
|
||||||
|
which empty tasks are assigned.
|
||||||
|
|
||||||
|
## Execution graph
|
||||||
|
|
||||||
|
Once both the resources and their consumers have been properly registered, the
|
||||||
|
purpose of this tool is to generate an execution graph that takes into account
|
||||||
|
all specified constraints to return the best scheduling for the vertices:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::adjacency_matrix<entt::directed_tag> graph = builder.graph();
|
||||||
|
```
|
||||||
|
|
||||||
|
Searching for the main vertices (that is, those without in-edges) is usually the
|
||||||
|
first thing required:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for(auto &&vertex: graph) {
|
||||||
|
if(auto in_edges = graph.in_edges(vertex); in_edges.begin() == in_edges.end()) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Then it is possible to instantiate an execution graph by means of other
|
||||||
|
functions such as `out_edges` to retrieve the children of a given task or
|
||||||
|
`edges` to get the identifiers.
|
||||||
Vendored
+92
@@ -0,0 +1,92 @@
|
|||||||
|
# Push EnTT across boundaries
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Working across boundaries](#working-across-boundaries)
|
||||||
|
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
|
||||||
|
* [Meta context](#meta-context)
|
||||||
|
* [Memory management](#memory-management)
|
||||||
|
|
||||||
|
# Working across boundaries
|
||||||
|
|
||||||
|
`EnTT` has historically had a limit when used across boundaries on Windows in
|
||||||
|
general and on GNU/Linux when default visibility was set to hidden. The
|
||||||
|
limitation was mainly due to a custom utility used to assign unique, sequential
|
||||||
|
identifiers with different types.<br/>
|
||||||
|
Fortunately, nowadays `EnTT` works smoothly across boundaries.
|
||||||
|
|
||||||
|
## Smooth until proven otherwise
|
||||||
|
|
||||||
|
Many classes in `EnTT` make extensive use of type erasure for their purposes.
|
||||||
|
This raises the need to identify objects whose type has been erased.<br/>
|
||||||
|
The `type_hash` class template is how identifiers are generated and thus made
|
||||||
|
available to the rest of the library. In general, this class arouses little
|
||||||
|
interest. The only exception is when a conflict between identifiers occurs
|
||||||
|
(definitely uncommon though) or when the default solution proposed by `EnTT` is
|
||||||
|
not suitable for the user's purposes.<br/>
|
||||||
|
The section dedicated to `type_info` contains all the details to get around the
|
||||||
|
issue in a concise and elegant way. Please refer to the specific documentation.
|
||||||
|
|
||||||
|
When working with linked libraries, compile definitions `ENTT_API_EXPORT` and
|
||||||
|
`ENTT_API_IMPORT` are to import or export symbols, so as to make everything work
|
||||||
|
nicely across boundaries.<br/>
|
||||||
|
On the other hand, everything should run smoothly when working with plugins or
|
||||||
|
shared libraries that do not export any symbols.
|
||||||
|
|
||||||
|
For those who need more details, the test suite contains many examples covering
|
||||||
|
the most common cases (see the `lib` directory for all details).<br/>
|
||||||
|
It goes without saying that it is impossible to cover **all** possible cases.
|
||||||
|
However, what is offered should hopefully serve as a basis for all of them.
|
||||||
|
|
||||||
|
## Meta context
|
||||||
|
|
||||||
|
The runtime reflection system deserves a special mention when it comes to using
|
||||||
|
it across boundaries.<br/>
|
||||||
|
Since it is linked already to a static context to which the elements are
|
||||||
|
attached and different contexts do not relate to each other, they must be
|
||||||
|
_shared_ to allow the use of meta types across boundaries.
|
||||||
|
|
||||||
|
Fortunately, sharing a context is also trivial to do. First of all, the local
|
||||||
|
one is acquired in the main space:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto handle = entt::locator<entt::meta_ctx>::handle();
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, it is passed to the receiving space that sets it as its default context,
|
||||||
|
thus discarding or storing aside the local one:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::locator<entt::meta_ctx>::reset(handle);
|
||||||
|
```
|
||||||
|
|
||||||
|
From now on, both spaces refer to the same context and to it are all new meta
|
||||||
|
types attached, no matter where they are created.<br/>
|
||||||
|
Note that _replacing_ the main context does not also propagate changes across
|
||||||
|
boundaries. In other words, replacing a context results in the decoupling of the
|
||||||
|
two sides and therefore a divergence in the contents.
|
||||||
|
|
||||||
|
## Memory Management
|
||||||
|
|
||||||
|
There is another subtle problem due to memory management that can lead to
|
||||||
|
headaches.<br/>
|
||||||
|
It can occur where there are pools of objects (such as components or events)
|
||||||
|
dynamically created on demand. This is usually not a problem when working with
|
||||||
|
linked libraries that rely on the same dynamic runtime. However, it can occur in
|
||||||
|
the case of plugins or statically linked runtimes.
|
||||||
|
|
||||||
|
As an example, imagine creating an instance of `registry` in the main executable
|
||||||
|
and sharing it with a plugin. If the latter starts working with a component that
|
||||||
|
is unknown to the former, a dedicated pool is created within the registry on
|
||||||
|
first use.<br/>
|
||||||
|
As one can guess, this pool is instantiated on a different side of the boundary
|
||||||
|
from the `registry`. Therefore, the instance is now managing memory from
|
||||||
|
different spaces, and this can quickly lead to crashes if not properly
|
||||||
|
addressed.
|
||||||
|
|
||||||
|
To overcome the risk, it is recommended to use well-defined interfaces that make
|
||||||
|
fundamental types pass through the boundaries, isolating the instances of the
|
||||||
|
`EnTT` classes from time to time and as appropriate.<br/>
|
||||||
|
Refer to the test suite for some examples, read the documentation available
|
||||||
|
online about this type of issues or consult someone who has already had such
|
||||||
|
experiences to avoid problems.
|
||||||
Vendored
+355
@@ -0,0 +1,355 @@
|
|||||||
|
# EnTT in Action
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [EnTT in Action](#entt-in-action)
|
||||||
|
* [Games](#games)
|
||||||
|
* [Engines and the like](#engines-and-the-like)
|
||||||
|
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
|
||||||
|
* [Any Other Business](#any-other-business)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||||
|
mention most of them because of some signatures I put on some documents time
|
||||||
|
ago. Fortunately, there are also people who took the time to implement open
|
||||||
|
source projects based on `EnTT` and did not hold back when it came to
|
||||||
|
documenting them.
|
||||||
|
|
||||||
|
Below an incomplete list of games, applications and articles that can be used as
|
||||||
|
a reference.<br/>
|
||||||
|
Where I put the word _apparently_ means that the use of `EnTT` is documented but
|
||||||
|
the authors did not make explicit announcements or contacted me directly.
|
||||||
|
|
||||||
|
If you know of other resources out there that are about `EnTT`, feel free to
|
||||||
|
open an issue or a PR. I will be glad to add them to this page.<br/>
|
||||||
|
I hope the following lists can grow much more in the future.
|
||||||
|
|
||||||
|
# EnTT in Action
|
||||||
|
|
||||||
|
## Games
|
||||||
|
|
||||||
|
* [Minecraft](https://minecraft.net/en-us/attribution/) by
|
||||||
|
[Mojang](https://mojang.com/): of course, **that** Minecraft, see the
|
||||||
|
open source attributions page for more details.
|
||||||
|
* [Minecraft Legends](https://www.minecraft.net/it-it/about-legends) by
|
||||||
|
[Mojang](https://mojang.com/): an action strategy game where users have to
|
||||||
|
fight to defend the Overworld.
|
||||||
|
* [Minecraft Earth](https://www.minecraft.net/en-us/about-earth) by
|
||||||
|
[Mojang](https://mojang.com/): an augmented reality game for mobile, that
|
||||||
|
lets users bring Minecraft into the real world.
|
||||||
|
* [Ember Sword](https://embersword.com/): a modern Free-to-Play MMORPG with a
|
||||||
|
player-driven economy, a classless combat system, and scarce, tradable
|
||||||
|
cosmetic collectibles.
|
||||||
|
* Apparently [Diablo II: Resurrected](https://diablo2.blizzard.com/) by
|
||||||
|
[Blizzard](https://www.blizzard.com/): monsters, heroes, items, spells, all
|
||||||
|
resurrected. Thanks unknown insider.
|
||||||
|
* [Apparently](https://www.youtube.com/watch?v=P8xvOA3ikrQ&t=1105s)
|
||||||
|
[Call of Duty: Vanguard](https://www.callofduty.com/vanguard) by
|
||||||
|
[Sledgehammer Games](https://www.sledgehammergames.com/): I can neither
|
||||||
|
confirm nor deny, but there is a license I know in the credits.
|
||||||
|
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
|
||||||
|
[Wizards of the Coast](https://company.wizards.com): your party, their
|
||||||
|
funeral.
|
||||||
|
* [TiltedEvolution](https://github.com/tiltedphoques/TiltedEvolution) by
|
||||||
|
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
|
||||||
|
to play online.
|
||||||
|
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
|
||||||
|
simulation [game](https://antkeeper.com/).
|
||||||
|
* [Openblack](https://github.com/openblack/openblack): open source
|
||||||
|
reimplementation of the game _Black & White_ (2001).
|
||||||
|
* [Land of the Rair](https://github.com/LandOfTheRair/core2): the new backend
|
||||||
|
of [a retro-style MUD](https://rair.land/) for the new age.
|
||||||
|
* [Face Smash](https://play.google.com/store/apps/details?id=com.gamee.facesmash):
|
||||||
|
a game to play with your face.
|
||||||
|
* [EnTT Pacman](https://github.com/Kerndog73/EnTT-Pacman): an example of how
|
||||||
|
to make Pacman with `EnTT`.
|
||||||
|
* [Wacman](https://github.com/carlfindahl/wacman): a pacman clone with OpenGL.
|
||||||
|
* [Classic Tower Defence](https://github.com/kerndog73/Classic-Tower-Defence):
|
||||||
|
a tiny little tower defence game featuring a homemade font.
|
||||||
|
[Check it out](https://indi-kernick.itch.io/classic-tower-defence).
|
||||||
|
* [The Machine](https://github.com/Kerndog73/The-Machine): a box pushing
|
||||||
|
puzzler with logic gates and other cool stuff.
|
||||||
|
[Check it out](https://indi-kernick.itch.io/the-machine-web-version).
|
||||||
|
* [EnTTPong](https://github.com/DomRe/EnttPong): a basic game made to showcase
|
||||||
|
different parts of `EnTT` and C++17.
|
||||||
|
* [Randballs](https://github.com/gale93/randballs): simple `SFML` and `EnTT`
|
||||||
|
playground.
|
||||||
|
* [EnTT Tower Defense](https://github.com/Daivuk/tddod): a data oriented tower
|
||||||
|
defense example.
|
||||||
|
* [EnTT Breakout](https://github.com/vblanco20-1/entt-breakout): simple
|
||||||
|
example of a breakout game, using `SDL` and `EnTT`.
|
||||||
|
* [Arcade puzzle game with EnTT](https://github.com/MasonRG/ArcadePuzzleGame):
|
||||||
|
arcade puzzle game made in C++ using the `SDL2` and `EnTT` libraries.
|
||||||
|
* [Snake with EnTT](https://github.com/MasonRG/SnakeGame): simple snake game
|
||||||
|
made in C++ with the `SDL2` and `EnTT` libraries.
|
||||||
|
* [Mirrors lasers and robots](https://github.com/guillaume-haerinck/imac-tower-defense):
|
||||||
|
a small tower defense game based on mirror orientation.
|
||||||
|
* [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead/): 2D, Zombie,
|
||||||
|
RPG game made from scratch in C++.
|
||||||
|
* [Robotligan](https://github.com/Trisslotten/robotligan): multiplayer
|
||||||
|
football game.
|
||||||
|
* [DungeonSlayer](https://github.com/alohaeee/DungeonSlayer): 2D game made
|
||||||
|
from scratch in C++.
|
||||||
|
* [3DGame](https://github.com/kwarkGorny/3DGame): 2.5D top-down space shooter.
|
||||||
|
* [Pulcher](https://github.com/AODQ/pulcher): 2D cross-platform game inspired
|
||||||
|
by Quake.
|
||||||
|
* [Destroid](https://github.com/tyrannicaltoucan/destroid): _one-bazillionth_
|
||||||
|
arcade game about shooting dirty rocks in space, inspired by Asteroids.
|
||||||
|
* [Wanderer](https://github.com/albin-johansson/wanderer): a 2D
|
||||||
|
exploration-based indie game.
|
||||||
|
* [Spelunky® Classic remake](https://github.com/dbeef/spelunky-psp): a truly
|
||||||
|
multiplatform experience with a rewrite from scratch.
|
||||||
|
* [CubbyTower](https://github.com/utilForever/CubbyTower): a simple tower
|
||||||
|
defense game using C++ with Entity Component System (ECS).
|
||||||
|
* [Runeterra](https://github.com/utilForever/Runeterra): Legends of Runeterra
|
||||||
|
simulator using C++ with some reinforcement learning.
|
||||||
|
* [Black Sun](https://store.steampowered.com/app/1670930/Black_Sun/): fly your
|
||||||
|
spaceship through a large 2D open world.
|
||||||
|
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokémon Battle
|
||||||
|
simulator using C++ with some reinforcement learning.
|
||||||
|
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
|
||||||
|
town, before it is too late.
|
||||||
|
* [City Builder Game](https://github.com/PhiGei2000/CityBuilderGame): a simple
|
||||||
|
city-building game using C++ and OpenGL.
|
||||||
|
* [BattleSub](https://github.com/bfeldpw/battlesub): two player 2D submarine
|
||||||
|
game with some fluid dynamics.
|
||||||
|
* [Crimson Rush](https://github.com/WilKam01/LuaCGame): a dungeon-crawler and
|
||||||
|
rougelike inspired game about exploring and surviving as long as possible.
|
||||||
|
* [Space Fight](https://github.com/cholushkin/SpaceFight): one screen
|
||||||
|
multi-player arcade shooter game prototype.
|
||||||
|
* [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample
|
||||||
|
application as a starting point using `EnTT` and `SDL2`.
|
||||||
|
* [Hellbound](https://buas.itch.io/hellbound): a top-down action rogue-like
|
||||||
|
where to fight colossal demons in procedurally generated levels of hell.
|
||||||
|
* [Saurian Sorcery](https://github.com/cajallen/spellbook): a tower defense
|
||||||
|
game where to assemble a tribe of lizards to defend against robot invaders.
|
||||||
|
* [robotfindskitten](https://github.com/autogalkin/robotfindskitten): a clone
|
||||||
|
of `robotfindskitten` inside `Notepad.exe`, powered by `EnTT`.
|
||||||
|
* [Orion](https://github.com/alekskoloch/Orion): Outer-space Research and
|
||||||
|
Interstellar Observation Network (a space shooter game).
|
||||||
|
* [EnTT Boids](https://github.com/DanielEliasib/entt_boids): a simple boids
|
||||||
|
implementation using `EnTT` and `Raylib`.
|
||||||
|
* [PalmRide: After Flight](https://store.steampowered.com/app/2812540/PalmRide_After_Flight/):
|
||||||
|
an on-rails shooter with retro outrun aesthetics.
|
||||||
|
* [Exhibition of Speed](https://store.steampowered.com/app/2947450/Exhibition_of_Speed/):
|
||||||
|
build your own car and go racing.
|
||||||
|
* [Lichgate](https://buas.itch.io/lichgate): top-down action rogue-like where
|
||||||
|
users unlock abilities to fight horde of enemies in an endless world.
|
||||||
|
* [Letalka](https://github.com/dviglo2d/dviglo2d/tree/main/games/letalka):
|
||||||
|
small demo game with ships and bullets flying everywhere on the screen.
|
||||||
|
* [Lichgate](https://buas.itch.io/lichgate): step into the robes of a powerful
|
||||||
|
mage determined to halt the relentless hordes of undead.
|
||||||
|
|
||||||
|
## Engines and the like:
|
||||||
|
|
||||||
|
* [Hazel Engine](https://github.com/TheCherno/Hazel): a work in progress
|
||||||
|
engine created by [The Cherno](https://github.com/TheCherno/Hazel) during
|
||||||
|
one of his most famous video series.
|
||||||
|
* [Aether Engine](https://hadean.com/spatial-simulation/)
|
||||||
|
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
|
||||||
|
[Hadean](https://hadean.com/): a library designed for spatially partitioning
|
||||||
|
agent-based simulations.
|
||||||
|
* [Fling Engine](https://github.com/flingengine/FlingEngine): a Vulkan game
|
||||||
|
engine with a focus on data oriented design.
|
||||||
|
* [NovusCore](https://github.com/novuscore/NovusCore): a modern take on World
|
||||||
|
of Warcraft emulation.
|
||||||
|
* [Chrysalis](https://github.com/ivanhawkes/Chrysalis): action RPG SDK for
|
||||||
|
CRYENGINE games.
|
||||||
|
* [LM-Engine](https://github.com/Lawrencemm/LM-Engine): the Vim of game
|
||||||
|
engines.
|
||||||
|
* [Edyn](https://github.com/xissburg/edyn): a real-time physics engine
|
||||||
|
organized as an ECS.
|
||||||
|
* [MushMachine](https://github.com/MadeOfJelly/MushMachine): engine...
|
||||||
|
vrooooommm.
|
||||||
|
* [Antara Gaming SDK](https://github.com/KomodoPlatform/antara-gaming-sdk):
|
||||||
|
the Komodo Gaming Software Development Kit.
|
||||||
|
* [XVP](https://ravingbots.com/xvp-expansive-vehicle-physics-for-unreal-engine/):
|
||||||
|
[_eXpansive Vehicle Physics_](https://github.com/raving-bots/xvp/wiki/Plugin-integration-guide)
|
||||||
|
plugin for Unreal Engine.
|
||||||
|
* [Apparently](https://teamwisp.github.io/credits/)
|
||||||
|
[Wisp](https://teamwisp.github.io/product/) by
|
||||||
|
[Team Wisp](https://teamwisp.github.io/): an advanced real-time ray tracing
|
||||||
|
renderer built for the demands of video game artists.
|
||||||
|
* [shiva](https://github.com/Milerius/shiva): modern C++ engine with
|
||||||
|
modularity.
|
||||||
|
* [ImGui/EnTT editor](https://github.com/Green-Sky/imgui_entt_entity_editor):
|
||||||
|
a drop-in, single-file entity editor for `EnTT` that uses `ImGui` as
|
||||||
|
graphical backend (with
|
||||||
|
[demo code](https://github.com/Green-Sky/imgui_entt_entity_editor_demo)).
|
||||||
|
* [SgOgl](https://github.com/stwe/SgOgl): a game engine library for OpenGL
|
||||||
|
developed for educational purposes.
|
||||||
|
* [Lumos](https://github.com/jmorton06/Lumos): game engine written in C++
|
||||||
|
using OpenGL and Vulkan.
|
||||||
|
* [Silvanus](https://github.com/hobbyistmaker/silvanus): Silvanus Fusion 360
|
||||||
|
Box Generator.
|
||||||
|
* [Lina Engine](https://github.com/inanevin/LinaEngine): an open-source,
|
||||||
|
modular, tiny and fast C++ game engine, aimed to develop 3D desktop games.
|
||||||
|
* [Spike](https://github.com/FahimFuad/Spike): a powerful game engine which
|
||||||
|
can run on a toaster.
|
||||||
|
* [Helena Framework](https://github.com/NIKEA-SOFT/HelenaFramework): a modern
|
||||||
|
framework in C++17 for backend development.
|
||||||
|
* [Unity/EnTT](https://github.com/TongTungGiang/unity-entt): tech demo of a
|
||||||
|
native simulation layer using `EnTT` and `Unity` as a rendering engine.
|
||||||
|
* [OverEngine](https://github.com/OverShifted/OverEngine): an overengineered
|
||||||
|
game engine.
|
||||||
|
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
|
||||||
|
3D game engine with a high emphasis on rendering.
|
||||||
|
* [Kawaii](https://github.com/Mathieu-Lala/Kawaii_Engine): a modern data
|
||||||
|
oriented game engine.
|
||||||
|
* [Becketron](https://github.com/Doctor-Foxling/Becketron): a game engine
|
||||||
|
written mostly in C++.
|
||||||
|
* [Spatial Engine](https://github.com/luizgabriel/Spatial.Engine): a
|
||||||
|
cross-platform engine created on top of Google's filament rendering engine.
|
||||||
|
* [Kaguya](https://github.com/KaiH0717/Kaguya): D3D12 Rendering Engine.
|
||||||
|
* [OpenAWE](https://github.com/OpenAWE-Project/OpenAWE): open implementation
|
||||||
|
of the Alan Wake Engine.
|
||||||
|
* [Nazara Engine](https://github.com/DigitalPulseSoftware/NazaraEngine): fast,
|
||||||
|
cross-platform, object-oriented API to help in daily developer life.
|
||||||
|
* [Billy Engine](https://github.com/billy4479/BillyEngine): some kind of 2D
|
||||||
|
engine based on `SDL2` and `EnTT`.
|
||||||
|
* [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++
|
||||||
|
2D & 3D game engine that focuses on being fast and powerful.
|
||||||
|
* [The Worst Engine](https://github.com/Parasik72/TWE): a game engine based on
|
||||||
|
OpenGL.
|
||||||
|
* [Ecsact](https://ecsact.dev/): a language aimed at describing ECS, with a
|
||||||
|
[runtime implementation](https://github.com/ecsact-dev/ecsact_rt_entt) based
|
||||||
|
on `EnTT`.
|
||||||
|
* [AGE (Arc Game Engine)](https://github.com/MohitSethi99/ArcGameEngine): an
|
||||||
|
open-source engine for building 2D & 3D real-time rendering and interactive
|
||||||
|
contents.
|
||||||
|
* [Kengine](https://github.com/phisko/kengine): the _Koala engine_ is a game
|
||||||
|
engine entirely implemented as an entity-component-system.
|
||||||
|
* [Scion2D](https://github.com/dwjclark11/Scion2D): 2D game engine with
|
||||||
|
[YouTube series](https://www.youtube.com/playlist?list=PL3HUvSWOJR7XRDwVVQqqWO-zyyscb8L-v)
|
||||||
|
included.
|
||||||
|
* [EnTT Editor](https://github.com/TheDimin/EnttEditor): an editor for `EnTT`
|
||||||
|
libary that combines its built-in reflection system with `ImGui`.
|
||||||
|
* [Era Game Engine](https://github.com/EldarMuradov/EraGameEngine): a modern
|
||||||
|
ECS-based game engine.
|
||||||
|
* [Core SDK of Trollworks engine](https://github.com/trollworks/sdk-core): 2D
|
||||||
|
game engine based on procrastination.
|
||||||
|
* [Rocky](https://github.com/pelicanmapping/rocky): 3D geospatial application
|
||||||
|
engine.
|
||||||
|
* [Donner](https://github.com/jwmcglynn/donner): a modern C++20 SVG2 rendering
|
||||||
|
API with CSS3.
|
||||||
|
* [Coral Engine](https://github.com/GuusKemperman/CoralEngine): open-source
|
||||||
|
student engine with the tools to make games in C++ and Visual scripting.
|
||||||
|
* [Star Engine](https://github.com/HODAKdev/StarEngine): an Advanced C++
|
||||||
|
DirectX 11 game engine.
|
||||||
|
* [Darmok](https://github.com/miguelibero/darmok): another C++ game engine.
|
||||||
|
* [Magique](https://github.com/gk646/magique): 2D game engine for programmers
|
||||||
|
(or those yet to be).
|
||||||
|
|
||||||
|
## Articles, videos and blog posts:
|
||||||
|
|
||||||
|
* [Some posts](https://skypjack.github.io/tags/#entt) on my personal
|
||||||
|
[blog](https://skypjack.github.io/) are about `EnTT`, for those who want to
|
||||||
|
know **more** on this project.
|
||||||
|
* [Game Engine series](https://www.youtube.com/c/TheChernoProject/videos) by
|
||||||
|
[The Cherno](https://github.com/TheCherno) (not only about `EnTT` but also
|
||||||
|
on the use of an ECS in general):
|
||||||
|
- [Intro to EnTT](https://www.youtube.com/watch?v=D4hz0wEB978).
|
||||||
|
- [Entities and Components](https://www.youtube.com/watch?v=-B1iu4QJTUc).
|
||||||
|
- [The ENTITY Class](https://www.youtube.com/watch?v=GfSzeAcsBb0).
|
||||||
|
- [Camera Systems](https://www.youtube.com/watch?v=ubZn7BlrnTU).
|
||||||
|
- [Scene Camera](https://www.youtube.com/watch?v=UKVFRRufKzo).
|
||||||
|
- [Native Scripting](https://www.youtube.com/watch?v=iIUhg88MK5M).
|
||||||
|
- [Native Scripting (now with virtual functions!)](https://www.youtube.com/watch?v=1cHEcrIn8IQ).
|
||||||
|
- [Scene Hierarchy Panel](https://www.youtube.com/watch?v=wziDnE8guvI).
|
||||||
|
- [Properties Panel](https://www.youtube.com/watch?v=NBpB0qscF3E).
|
||||||
|
- [Camera Component UI](https://www.youtube.com/watch?v=RIMt_6agUiU).
|
||||||
|
- [Drawing Component UI](https://www.youtube.com/watch?v=u3yq8s3KuSE).
|
||||||
|
- [Transform Component UI](https://www.youtube.com/watch?v=8JqcXYbzPJc).
|
||||||
|
- [Adding/Removing Entities and Components UI](https://www.youtube.com/watch?v=PsyGmsIgp9M).
|
||||||
|
- [Saving and Loading Scenes](https://www.youtube.com/watch?v=IEiOP7Y-Mbc).
|
||||||
|
- ... And so on.
|
||||||
|
[Check out](https://www.youtube.com/channel/UCQ-W1KE9EYfdxhL6S4twUNw) the
|
||||||
|
_Game Engine Series_ by The Cherno for more videos.
|
||||||
|
* [Game Engine series](https://www.youtube.com/@JADE-iteGames/videos) by
|
||||||
|
[dwjclark11](https://github.com/dwjclark11) (not just `EnTT` but a lot of
|
||||||
|
it):
|
||||||
|
- [Getting into ECS](https://youtu.be/k9CbonLopJU?si=za3Tisyc96_92DWM)
|
||||||
|
- [Creating ECS Wrapper Classes](https://youtu.be/yetyuMJRdbo?si=PJTkmap4Ysqbzb_M)
|
||||||
|
- [Runtime Reflection using EnTT meta](https://youtu.be/GrXV5A07GTY?si=fKdWTj9AOhnhtiXq)
|
||||||
|
- [Adding entt::meta and Sol2 bindings](https://youtu.be/IM55JgxOqFA?si=rsbb4AG_NVh4IUmD)
|
||||||
|
(with [part two](https://youtu.be/-PTt-b1tzRw?si=zPJ4vEluyheMcNgO) too)
|
||||||
|
- ... And so on.
|
||||||
|
[Check it out](https://www.youtube.com/playlist?list=PL3HUvSWOJR7XRDwVVQqqWO-zyyscb8L-v)
|
||||||
|
for more videos.
|
||||||
|
* [Warmonger Dynasty devlog series](https://david-delassus.medium.com/list/warmonger-dynasty-devlogs-f64b71f556de)
|
||||||
|
by [linkdd](https://github.com/linkdd): an interesting walkthrough of
|
||||||
|
developing a game (also) with EnTT.
|
||||||
|
* [Use EnTT When You Need An ECS](https://www.codingwiththomas.com/blog/use-entt-when-you-need-an-ecs)
|
||||||
|
by [Thomas](https://www.codingwiththomas.com/): I could not have said it
|
||||||
|
better.
|
||||||
|
* [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`.
|
||||||
|
* [Implementing ECS architecture in UE4](https://forums.unrealengine.com/development-discussion/c-gameplay-programming/1449913-implementing-ecs-architecture-in-ue4-giant-space-battle):
|
||||||
|
giant space battle.
|
||||||
|
* [Conan Adventures (SFML and EnTT in C++)](https://leinnan.github.io/blog/conan-adventuressfml-and-entt-in-c.html):
|
||||||
|
create projects in modern C++ using `SFML`, `EnTT`, `Conan` and `CMake`.
|
||||||
|
* [Adding EnTT ECS to Chrysalis](https://www.tauradius.com/post/adding-an-ecs-to-chrysalis/):
|
||||||
|
a blog entry (and its
|
||||||
|
[follow-up](https://www.tauradius.com/post/chrysalis-update-2020-08-02/))
|
||||||
|
about the integration of `EnTT` into `Chrysalis`, an action RPG SDK for
|
||||||
|
CRYENGINE games.
|
||||||
|
* [Creating Minecraft in One Week with C++ and Vulkan](https://vazgriz.com/189/creating-minecraft-in-one-week-with-c-and-vulkan/):
|
||||||
|
a crack at recreating Minecraft in one week using a custom C++ engine and
|
||||||
|
Vulkan ([code included](https://github.com/vazgriz/VoxelGame)).
|
||||||
|
* [Ability Creator](https://www.erichildebrand.net/blog/ability-creator-project-retrospect):
|
||||||
|
project retrospect by [Eric Hildebrand](https://www.erichildebrand.net/).
|
||||||
|
* [EnTT Entity Component System Gaming Library](https://gamefromscratch.com/entt-entity-component-system-gaming-library/):
|
||||||
|
`EnTT` on GameFromScratch.com.
|
||||||
|
* [Custom C++ server for UE5](https://youtu.be/fbXZVNCOvjM) optimized for
|
||||||
|
MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about
|
||||||
|
player bots and full external ECS: a series definitely worth looking at.
|
||||||
|
|
||||||
|
## Any Other Business:
|
||||||
|
|
||||||
|
* [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by
|
||||||
|
[Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the
|
||||||
|
cross-platform C++ rendering engine. The SDKs are used by a lot of
|
||||||
|
enterprise custom apps, as well as by Esri for its own public applications
|
||||||
|
such as
|
||||||
|
[Explorer](https://play.google.com/store/apps/details?id=com.esri.explorer),
|
||||||
|
[Collector](https://play.google.com/store/apps/details?id=com.esri.arcgis.collector)
|
||||||
|
and
|
||||||
|
[Navigator](https://play.google.com/store/apps/details?id=com.esri.navigator).
|
||||||
|
* [FASTSUITE Edition 2](https://www.fastsuite.com/en_EN/fastsuite/fastsuite-edition-2.html)
|
||||||
|
by [Cenit](http://www.cenit.com/en_EN/about-us/overview.html): they use
|
||||||
|
`EnTT` to drive their simulation, that is, the communication between robot
|
||||||
|
controller emulator and renderer.
|
||||||
|
* [Ragdoll](https://ragdolldynamics.com/): real-time physics for Autodesk Maya
|
||||||
|
2020.
|
||||||
|
* [Project Lagrange](https://github.com/adobe/lagrange): a robust geometry
|
||||||
|
processing library by [Adobe](https://github.com/adobe).
|
||||||
|
* [AtomicDEX](https://github.com/KomodoPlatform/atomicDEX-Desktop): a secure
|
||||||
|
wallet and noncustodial decentralized exchange rolled into one application.
|
||||||
|
* [Apparently](https://www.linkedin.com/in/skypjack/)
|
||||||
|
[NIO](https://www.nio.io/): there was a collaboration to make some changes
|
||||||
|
to `EnTT`, at the time used for internal projects.
|
||||||
|
* [Apparently](https://www.linkedin.com/jobs/view/architekt-c%2B%2B-at-tieto-1219512333/)
|
||||||
|
[Tieto](https://www.tieto.com/): they published a job post where `EnTT` was
|
||||||
|
listed on their software stack.
|
||||||
|
* [Sequentity](https://github.com/alanjfs/sequentity): A MIDI-like
|
||||||
|
sequencer/tracker for C++ and `ImGui` (with `Magnum` and `EnTT`).
|
||||||
|
* [EnTT meets Sol2](https://github.com/skaarj1989/entt-meets-sol2): freely
|
||||||
|
available examples of how to combine `EnTT` and `Sol2`.
|
||||||
|
* [Godot meets EnTT](https://github.com/portaloffreedom/godot_entt_example/):
|
||||||
|
a simple example on how to use `EnTT` within
|
||||||
|
[`Godot`](https://godotengine.org/).
|
||||||
|
* [Godot and GameNetworkingSockets meet EnTT](https://github.com/portaloffreedom/godot_entt_net_example):
|
||||||
|
a simple example on how to use `EnTT` and
|
||||||
|
[`GameNetworkingSockets`](https://github.com/ValveSoftware/GameNetworkingSockets)
|
||||||
|
within [`Godot`](https://godotengine.org/).
|
||||||
|
* [MatchOneEntt](https://github.com/mhaemmerle/MatchOneEntt): port of
|
||||||
|
[Match One](https://github.com/sschmid/Match-One) for `Entitas-CSharp`.
|
||||||
|
* GitHub contains also
|
||||||
|
[many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code)
|
||||||
|
of use of `EnTT` from which to take inspiration if interested.
|
||||||
Vendored
+82
@@ -0,0 +1,82 @@
|
|||||||
|
# Crash Course: service locator
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Service locator](#service-locator)
|
||||||
|
* [Opaque handles](#opaque-handles)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Usually, service locators are tightly bound to the services they expose. It is
|
||||||
|
hard to define a general purpose solution.<br/>
|
||||||
|
This tiny class tries to fill the gap and gets rid of the burden of defining a
|
||||||
|
different specific locator for each application.
|
||||||
|
|
||||||
|
# Service locator
|
||||||
|
|
||||||
|
The service locator API tries to mimic that of `std::optional` and adds some
|
||||||
|
extra functionalities on top of it such as allocator support.<br/>
|
||||||
|
There are a couple of functions to set up a service, namely `emplace` and
|
||||||
|
`allocate_emplace`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::locator<interface>::emplace<service>(argument);
|
||||||
|
entt::locator<interface>::allocate_emplace<service>(allocator, argument);
|
||||||
|
```
|
||||||
|
|
||||||
|
The difference is that the latter expects an allocator as the first argument and
|
||||||
|
uses it to allocate the service itself.<br/>
|
||||||
|
Once a service is set up, it is retrieved using the `value` function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
interface &service = entt::locator<interface>::value();
|
||||||
|
```
|
||||||
|
|
||||||
|
Since the service may not be set (and therefore this function may result in an
|
||||||
|
undefined behavior), the `has_value` and `value_or` functions are also available
|
||||||
|
to test a service locator and to get a fallback service in case there is none:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if(entt::locator<interface>::has_value()) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
interface &service = entt::locator<interface>::value_or<fallback_impl>(argument);
|
||||||
|
```
|
||||||
|
|
||||||
|
All arguments are used only if necessary, that is, if a service does not already
|
||||||
|
exist and therefore the fallback service is constructed and returned. In all
|
||||||
|
other cases, they are discarded.<br/>
|
||||||
|
Finally, to reset a service, use the `reset` function.
|
||||||
|
|
||||||
|
## Opaque handles
|
||||||
|
|
||||||
|
Sometimes it is useful to _transfer_ a copy of a service to another locator. For
|
||||||
|
example, when working across boundaries it is common to _share_ a service with a
|
||||||
|
dynamically loaded module.<br/>
|
||||||
|
Options are not much in this case. Among these is the possibility of _exporting_
|
||||||
|
services and assigning them to a different locator.
|
||||||
|
|
||||||
|
This is what the `handle` and `reset` functions are meant for.<br/>
|
||||||
|
The former returns an opaque object useful for _exporting_ (or rather, obtaining
|
||||||
|
a reference to) a service. The latter also accepts an optional argument to a
|
||||||
|
handle which then allows users to reset a service by initializing it with an
|
||||||
|
opaque handle:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto handle = entt::locator<interface>::handle();
|
||||||
|
entt::locator<interface>::reset(handle);
|
||||||
|
```
|
||||||
|
|
||||||
|
It is worth noting that it is possible to get handles for uninitialized services
|
||||||
|
and use them with other locators. Of course, all a user will get is to have an
|
||||||
|
uninitialized service elsewhere as well.
|
||||||
|
|
||||||
|
Note that exporting a service allows users to _share_ the object currently set
|
||||||
|
in a locator. Replacing it will not replace the element, even where a service
|
||||||
|
has been configured with a handle to the previous item.<br/>
|
||||||
|
In other words, if an audio service is replaced with a null object to silence an
|
||||||
|
application and the original service was shared, this operation will not
|
||||||
|
propagate to the other locators. Therefore, a module that shares the ownership
|
||||||
|
of the original audio service is still able to emit sounds.
|
||||||
Vendored
+1011
File diff suppressed because it is too large
Load Diff
Vendored
+351
@@ -0,0 +1,351 @@
|
|||||||
|
# Crash Course: poly
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Other libraries](#other-libraries)
|
||||||
|
* [Concept and implementation](#concept-and-implementation)
|
||||||
|
* [Deduced interface](#deduced-interface)
|
||||||
|
* [Defined interface](#defined-interface)
|
||||||
|
* [Fulfill a concept](#fulfill-a-concept)
|
||||||
|
* [Inheritance](#inheritance)
|
||||||
|
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||||
|
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Static polymorphism is a very powerful tool in C++, albeit sometimes cumbersome
|
||||||
|
to obtain.<br/>
|
||||||
|
This module aims to make it simple and easy to use.
|
||||||
|
|
||||||
|
The library allows to define _concepts_ as interfaces to fulfill with concrete
|
||||||
|
classes without having to inherit from a common base.<br/>
|
||||||
|
Among others, this is one of the advantages of static polymorphism in general
|
||||||
|
and of a generic wrapper like that offered by the `poly` class template in
|
||||||
|
particular.<br/>
|
||||||
|
The result is an object to pass around as such and not through a reference or a
|
||||||
|
pointer, as it happens when it comes to working with dynamic polymorphism.
|
||||||
|
|
||||||
|
Since the `poly` class template makes use of `entt::any` internally, it also
|
||||||
|
supports most of its feature. For example, the possibility to create aliases to
|
||||||
|
existing and thus unmanaged objects. This allows users to exploit the static
|
||||||
|
polymorphism while maintaining ownership of objects.<br/>
|
||||||
|
Likewise, the `poly` class template also benefits from the small buffer
|
||||||
|
optimization offered by the `entt::any` class and therefore minimizes the number
|
||||||
|
of allocations, avoiding them altogether where possible.
|
||||||
|
|
||||||
|
## Other libraries
|
||||||
|
|
||||||
|
There are some very interesting libraries regarding static polymorphism.<br/>
|
||||||
|
The ones that I like more are:
|
||||||
|
|
||||||
|
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
||||||
|
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
||||||
|
a class template that makes it easy to define a type-erasing polymorphic
|
||||||
|
object wrapper.
|
||||||
|
|
||||||
|
The former is admittedly an experimental library, with many interesting ideas.
|
||||||
|
I have some doubts about the usefulness of some features in real world projects,
|
||||||
|
but perhaps my lack of experience comes into play here. In my opinion, its only
|
||||||
|
flaw is the API that I find slightly more cumbersome than other solutions.<br/>
|
||||||
|
The latter was undoubtedly a source of inspiration for this module. Although I
|
||||||
|
opted for different choices in the implementation of both the final API and some
|
||||||
|
features.
|
||||||
|
|
||||||
|
Either way, the authors are gurus of the C++ community, people I only have to
|
||||||
|
learn from.
|
||||||
|
|
||||||
|
# Concept and implementation
|
||||||
|
|
||||||
|
The first thing to do to create a _type-erasing polymorphic object wrapper_ (to
|
||||||
|
use the terminology introduced by Eric Niebler) is to define a _concept_ that
|
||||||
|
types will have to adhere to.<br/>
|
||||||
|
For this purpose, the library offers a single class that supports both deduced
|
||||||
|
and fully defined interfaces. Although having interfaces deduced automatically
|
||||||
|
is convenient and allows users to write less code in most cases, it has some
|
||||||
|
limitations. It is therefore useful to be able to get around the deduction by
|
||||||
|
providing a custom definition for the static virtual table.
|
||||||
|
|
||||||
|
Once the interface is defined, a generic implementation is needed to fulfill the
|
||||||
|
concept itself.<br/>
|
||||||
|
Also in this case, the library allows customizations based on types or families
|
||||||
|
of types, so as to be able to go beyond the generic case where necessary.
|
||||||
|
|
||||||
|
## Deduced interface
|
||||||
|
|
||||||
|
This is how a concept with a deduced interface is defined:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Drawable: entt::type_list<> {
|
||||||
|
template<typename Base>
|
||||||
|
struct type: Base {
|
||||||
|
void draw() { this->template invoke<0>(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
It is recognizable by the fact that it inherits from an empty type list.<br/>
|
||||||
|
Functions can also be const, accept any number of parameters and return a type
|
||||||
|
other than `void`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Drawable: entt::type_list<> {
|
||||||
|
template<typename Base>
|
||||||
|
struct type: Base {
|
||||||
|
bool draw(int pt) const { return this->template invoke<0>(*this, pt); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, all parameters are passed to `invoke` after the reference to
|
||||||
|
`this` and the return value is whatever the internal call returns.<br/>
|
||||||
|
As for `invoke`, this is a name that is injected into the _concept_ through
|
||||||
|
`Base`, from which one must necessarily inherit. Since it is also a dependent
|
||||||
|
name, the `this-> template` form is unfortunately necessary due to the rules of
|
||||||
|
the language. However, there also exists an alternative that goes through an
|
||||||
|
external call:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Drawable: entt::type_list<> {
|
||||||
|
template<typename Base>
|
||||||
|
struct type: Base {
|
||||||
|
void draw() const { entt::poly_call<0>(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Once the _concept_ is defined, users must provide a generic implementation of it
|
||||||
|
in order to tell the system how any type can satisfy its requirements. This is
|
||||||
|
done via an alias template within the concept itself.<br/>
|
||||||
|
The index passed as a template parameter to either `invoke` or `poly_call`
|
||||||
|
refers to how this alias is defined.
|
||||||
|
|
||||||
|
## Defined interface
|
||||||
|
|
||||||
|
A fully defined concept is no different to one for which the interface is
|
||||||
|
deduced, with the only difference that the list of types is not empty this time:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Drawable: entt::type_list<void()> {
|
||||||
|
template<typename Base>
|
||||||
|
struct type: Base {
|
||||||
|
void draw() { entt::poly_call<0>(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Again, parameters and return values other than `void` are allowed. Also, the
|
||||||
|
function type must be const when the method to bind to it is const:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Drawable: entt::type_list<bool(int) const> {
|
||||||
|
template<typename Base>
|
||||||
|
struct type: Base {
|
||||||
|
bool draw(int pt) const { return entt::poly_call<0>(*this, pt); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Why should a user fully define a concept if the function types are the same as
|
||||||
|
the deduced ones?<br>
|
||||||
|
In fact, this is the limitation that can be worked around by manually defining
|
||||||
|
the static virtual table.
|
||||||
|
|
||||||
|
When things are deduced, there is an implicit constraint.<br/>
|
||||||
|
If the concept exposes a member function called `draw` with function type
|
||||||
|
`void()`, a concept is satisfied:
|
||||||
|
|
||||||
|
* Either by a class that exposes a member function with the same name and the
|
||||||
|
same signature.
|
||||||
|
|
||||||
|
* Or through a lambda that makes use of existing member functions from the
|
||||||
|
interface itself.
|
||||||
|
|
||||||
|
In other words, it is not possible to make use of functions not belonging to the
|
||||||
|
interface, even if they are part of the types that fulfill the concept.<br/>
|
||||||
|
Similarly, it is not possible to deduce a function in the static virtual table
|
||||||
|
with a function type different from that of the associated member function in
|
||||||
|
the interface itself.
|
||||||
|
|
||||||
|
Explicitly defining a static virtual table suppresses the deduction step and
|
||||||
|
allows maximum flexibility when providing the implementation for a concept.
|
||||||
|
|
||||||
|
## Fulfill a concept
|
||||||
|
|
||||||
|
The `impl` alias template of a concept is used to define how it is fulfilled:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Drawable: entt::type_list<> {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
using impl = entt::value_list<&Type::draw>;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, it is stated that the `draw` method of a generic type is enough to
|
||||||
|
satisfy the requirements of the `Drawable` concept.<br/>
|
||||||
|
Both member functions and free functions are supported to fulfill concepts:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename Type>
|
||||||
|
void print(Type &self) { self.print(); }
|
||||||
|
|
||||||
|
struct Drawable: entt::type_list<void()> {
|
||||||
|
// ...
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
using impl = entt::value_list<&print<Type>>;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Likewise, as long as the parameter types and return type support conversions to
|
||||||
|
and from those of the function type referenced in the static virtual table, the
|
||||||
|
actual implementation may differ in its function type since it is erased
|
||||||
|
internally.<br/>
|
||||||
|
Moreover, the `self` parameter is not strictly required by the system and can be
|
||||||
|
left out for free functions if not required.
|
||||||
|
|
||||||
|
Refer to the inline documentation for more details.
|
||||||
|
|
||||||
|
# Inheritance
|
||||||
|
|
||||||
|
_Concept inheritance_ is straightforward due to how poly looks like in `EnTT`.
|
||||||
|
Therefore, it is quite easy to build hierarchies of concepts if necessary.<br/>
|
||||||
|
The only constraint is that all concepts in a hierarchy must belong to the same
|
||||||
|
_family_, that is, they must be either all deduced or all defined.
|
||||||
|
|
||||||
|
For a deduced concept, inheritance is achieved in a few steps:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct DrawableAndErasable: entt::type_list<> {
|
||||||
|
template<typename Base>
|
||||||
|
struct type: typename Drawable::type<Base> {
|
||||||
|
static constexpr auto base = Drawable::impl<Drawable::type<entt::poly_inspector>>::size;
|
||||||
|
void erase() { entt::poly_call<base + 0>(*this); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
using impl = entt::value_list_cat_t<
|
||||||
|
typename Drawable::impl<Type>,
|
||||||
|
entt::value_list<&Type::erase>
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The static virtual table is empty and must remain so.<br/>
|
||||||
|
On the other hand, `type` no longer inherits from `Base`. Instead, it forwards
|
||||||
|
its template parameter to the type exposed by the _base class_. Internally, the
|
||||||
|
_size_ of the static virtual table of the base class is used as an offset for
|
||||||
|
the local indexes.<br/>
|
||||||
|
Finally, by means of the `value_list_cat_t` utility, the implementation consists
|
||||||
|
in appending the new functions to the previous list.
|
||||||
|
|
||||||
|
As for a defined concept instead, the list of types is _extended_ in a similar
|
||||||
|
way to what is shown for the implementation of the above concept.<br/>
|
||||||
|
To do this, it is useful to declare a function that allows to convert a
|
||||||
|
_concept_ into its underlying `type_list` object:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
template<typename... Type>
|
||||||
|
entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &);
|
||||||
|
```
|
||||||
|
|
||||||
|
The definition is not strictly required, since the function is only used through
|
||||||
|
a `decltype` as it follows:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct DrawableAndErasable: entt::type_list_cat_t<
|
||||||
|
decltype(as_type_list(std::declval<Drawable>())),
|
||||||
|
entt::type_list<void()>> {
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Similar to above, `type_list_cat_t` is used to concatenate the underlying static
|
||||||
|
virtual table with the new function types.<br/>
|
||||||
|
Everything else is the same as already shown instead.
|
||||||
|
|
||||||
|
# Static polymorphism in the wild
|
||||||
|
|
||||||
|
Once the _concept_ and implementation are defined, it is possible to use the
|
||||||
|
`poly` class template to _wrap_ instances that meet the requirements:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using drawable = entt::poly<Drawable>;
|
||||||
|
|
||||||
|
struct circle {
|
||||||
|
void draw() { /* ... */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct square {
|
||||||
|
void draw() { /* ... */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
drawable instance{circle{}};
|
||||||
|
instance->draw();
|
||||||
|
|
||||||
|
instance = square{};
|
||||||
|
instance->draw();
|
||||||
|
```
|
||||||
|
|
||||||
|
This class offers a wide range of constructors, from the default one (which
|
||||||
|
returns an uninitialized `poly` object) to the copy and move constructors, as
|
||||||
|
well as the ability to create objects in-place.<br/>
|
||||||
|
Among others, there is also a constructor that allows users to wrap unmanaged
|
||||||
|
objects in a `poly` instance (either const or non-const ones):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
circle shape;
|
||||||
|
drawable instance{std::in_place_type<circle &>, shape};
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, it is possible to create non-owning copies of `poly` from an existing
|
||||||
|
object:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
drawable other = instance.as_ref();
|
||||||
|
```
|
||||||
|
|
||||||
|
In both cases, although the interface of the `poly` object does not change, it
|
||||||
|
does not construct any element or take care of destroying the referenced
|
||||||
|
objects.
|
||||||
|
|
||||||
|
Note also how the underlying concept is accessed via a call to `operator->` and
|
||||||
|
not directly as `instance.draw()`.<br/>
|
||||||
|
This allows users to decouple the API of the wrapper from that of the concept.
|
||||||
|
Therefore, where `instance.data()` invokes the `data` member function of the
|
||||||
|
poly object, `instance->data()` maps directly to the functionality exposed by
|
||||||
|
the underlying concept.
|
||||||
|
|
||||||
|
# Storage size and alignment requirement
|
||||||
|
|
||||||
|
Under the hood, the `poly` class template makes use of `entt::any`. Therefore,
|
||||||
|
it can take advantage of the possibility of defining at compile-time the size of
|
||||||
|
the storage suitable for the small buffer optimization as well as the alignment
|
||||||
|
requirements:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])>
|
||||||
|
```
|
||||||
|
|
||||||
|
The default size is `sizeof(double[2])`, which seems like a good compromise
|
||||||
|
between a buffer that is too large and one unable to hold anything larger than
|
||||||
|
an integer. The alignment requirement is optional, and by default such that it
|
||||||
|
is the most stringent (the largest) for any object whose size is at most equal
|
||||||
|
to the one provided.<br/>
|
||||||
|
It is worth noting that providing a size of 0 (which is an accepted value in all
|
||||||
|
respects) will force the system to dynamically allocate the contained objects in
|
||||||
|
all cases.
|
||||||
Vendored
+211
@@ -0,0 +1,211 @@
|
|||||||
|
# Crash Course: cooperative scheduler
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [The process](#the-process)
|
||||||
|
* [Adaptor](#adaptor)
|
||||||
|
* [The scheduler](#the-scheduler)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
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 other component
|
||||||
|
types.<br/>
|
||||||
|
`EnTT` offers minimal support to this paradigm by introducing a few classes used
|
||||||
|
to define and execute cooperative processes.
|
||||||
|
|
||||||
|
# The process
|
||||||
|
|
||||||
|
A typical task inherits from the `process` class template that stays true to the
|
||||||
|
CRTP idiom. Moreover, derived classes specify what the intended type for elapsed
|
||||||
|
times is.
|
||||||
|
|
||||||
|
A process should expose publicly the following member functions whether needed
|
||||||
|
(note that it is not required to define a function unless the derived class
|
||||||
|
wants to _override_ the default behavior):
|
||||||
|
|
||||||
|
* `void update(Delta, void *);`
|
||||||
|
|
||||||
|
This is invoked once per tick until a process is explicitly aborted or ends
|
||||||
|
either with or without errors. Technically speaking, this member function is
|
||||||
|
not strictly required. However, each process should at least define it to work
|
||||||
|
_properly_. The `void *` parameter is an opaque pointer to user data (if any)
|
||||||
|
forwarded directly to the process during an update.
|
||||||
|
|
||||||
|
* `void init();`
|
||||||
|
|
||||||
|
This is invoked when the process joins the running queue of a scheduler. It
|
||||||
|
happens usually when the process is attached to the scheduler if it is a top
|
||||||
|
level one, or when it replaces its parent if it is a _continuation_.
|
||||||
|
|
||||||
|
* `void succeeded();`
|
||||||
|
|
||||||
|
This is invoked in case of success, immediately after an update and during the
|
||||||
|
same tick.
|
||||||
|
|
||||||
|
* `void failed();`
|
||||||
|
|
||||||
|
This is invoked in case of errors, immediately after an update and during the
|
||||||
|
same tick.
|
||||||
|
|
||||||
|
* `void aborted();`
|
||||||
|
|
||||||
|
This is invoked only if a process is explicitly aborted. There is no guarantee
|
||||||
|
that it executes in the same tick, it depends solely on whether the process is
|
||||||
|
aborted immediately or not.
|
||||||
|
|
||||||
|
Derived classes can also change the internal state of a process by invoking
|
||||||
|
`succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/>
|
||||||
|
All these are protected member functions made available to manage the life cycle
|
||||||
|
of a process from a derived class.
|
||||||
|
|
||||||
|
Here is a minimal example for the sake of curiosity:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_process: entt::process<my_process, std::uint32_t> {
|
||||||
|
using delta_type = std::uint32_t;
|
||||||
|
|
||||||
|
my_process(delta_type delay)
|
||||||
|
: remaining{delay}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void update(delta_type delta, void *) {
|
||||||
|
remaining -= std::min(remaining, delta);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
if(!remaining) {
|
||||||
|
succeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
delta_type remaining;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adaptor
|
||||||
|
|
||||||
|
Lambdas and functors cannot be used directly with a scheduler because they are
|
||||||
|
not properly defined processes with managed life cycles.<br/>
|
||||||
|
This class helps in filling the gap and turning lambdas and functors into
|
||||||
|
full-featured processes usable by a scheduler.
|
||||||
|
|
||||||
|
The function call operator has a signature similar to the one of the `update`
|
||||||
|
function of a process but for the fact that it receives two extra callbacks to
|
||||||
|
invoke whenever a process terminates with success or with an error:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void(Delta delta, void *data, auto succeed, auto fail);
|
||||||
|
```
|
||||||
|
|
||||||
|
Parameters have the following meaning:
|
||||||
|
|
||||||
|
* `delta` is the elapsed time.
|
||||||
|
* `data` is an opaque pointer to user data if any, `nullptr` otherwise.
|
||||||
|
* `succeed` is a function to call when a process terminates with success.
|
||||||
|
* `fail` is a function to call when a process terminates with errors.
|
||||||
|
|
||||||
|
Both `succeed` and `fail` accept no parameters at all.
|
||||||
|
|
||||||
|
Note that usually users should not worry about creating adaptors at all. A
|
||||||
|
scheduler creates them internally, each and every time a lambda or a functor is
|
||||||
|
used as a process.
|
||||||
|
|
||||||
|
# The scheduler
|
||||||
|
|
||||||
|
A cooperative scheduler runs different processes and helps manage their life
|
||||||
|
cycles.
|
||||||
|
|
||||||
|
Each process is invoked once per tick. If it terminates, it is removed
|
||||||
|
automatically from the scheduler, and it is never invoked again. Otherwise,
|
||||||
|
it is a good candidate to run one more time the next tick.<br/>
|
||||||
|
A process can also have a _child_. In this case, the parent process is replaced
|
||||||
|
with its child when it terminates and only if it returns with success. In case
|
||||||
|
of errors, both the parent process and its child are discarded. This way, it is
|
||||||
|
easy to create a chain of processes to run sequentially.
|
||||||
|
|
||||||
|
Using a scheduler is straightforward. To create it, users must provide only the
|
||||||
|
type for the elapsed times and no arguments at all:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::basic_scheduler<std::uint64_t> scheduler;
|
||||||
|
```
|
||||||
|
|
||||||
|
Otherwise, the `scheduler` alias is also available for the most common cases. It
|
||||||
|
uses `std::uint32_t` as a default type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::scheduler scheduler;
|
||||||
|
```
|
||||||
|
|
||||||
|
The class has member functions to query its internal data structures, like
|
||||||
|
`empty` or `size`, as well as a `clear` utility to reset it to a clean state:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// checks if there are processes still running
|
||||||
|
const auto empty = scheduler.empty();
|
||||||
|
|
||||||
|
// gets the number of processes still running
|
||||||
|
entt::scheduler::size_type size = scheduler.size();
|
||||||
|
|
||||||
|
// resets the scheduler to its initial state and discards all the processes
|
||||||
|
scheduler.clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
To attach a process to a scheduler, there are mainly two ways:
|
||||||
|
|
||||||
|
* If the process inherits from the `process` class template, it is enough to
|
||||||
|
indicate its type and submit all the parameters required to construct it to
|
||||||
|
the `attach` member function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
scheduler.attach<my_process>(1000u);
|
||||||
|
```
|
||||||
|
|
||||||
|
* Otherwise, in case of a lambda or a functor, it is enough to provide an
|
||||||
|
instance of the class to the `attach` member function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
scheduler.attach([](auto...){ /* ... */ });
|
||||||
|
```
|
||||||
|
|
||||||
|
In both cases, the scheduler is returned and its `then` member function can be
|
||||||
|
used to create chains of processes to run sequentially.<br/>
|
||||||
|
As a minimal example of use:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// schedules a task in the form of a lambda function
|
||||||
|
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
// appends a child in the form of another lambda function
|
||||||
|
.then([](auto delta, void *, auto succeed, auto fail) {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
// appends a child in the form of a process class
|
||||||
|
.then<my_process>(1000u);
|
||||||
|
```
|
||||||
|
|
||||||
|
To update a scheduler and therefore all its processes, the `update` member
|
||||||
|
function is the way to go:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// updates all the processes, no user data are provided
|
||||||
|
scheduler.update(delta);
|
||||||
|
|
||||||
|
// updates all the processes and provides them with custom data
|
||||||
|
scheduler.update(delta, &data);
|
||||||
|
```
|
||||||
|
|
||||||
|
In addition to these functions, the scheduler offers an `abort` member function
|
||||||
|
that is used to discard all the running processes at once:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// aborts all the processes abruptly ...
|
||||||
|
scheduler.abort(true);
|
||||||
|
|
||||||
|
// ... or gracefully during the next tick
|
||||||
|
scheduler.abort();
|
||||||
|
```
|
||||||
Vendored
+91
@@ -0,0 +1,91 @@
|
|||||||
|
# Similar projects
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Similar projects](#similar-projects)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
There are many projects similar to `EnTT`, both open source and not.<br/>
|
||||||
|
Some even borrowed some ideas from this library and expressed them in different
|
||||||
|
languages.<br/>
|
||||||
|
Others developed different architectures from scratch and therefore offer
|
||||||
|
alternative solutions with their pros and cons.
|
||||||
|
|
||||||
|
If you know of other similar projects out there, feel free to open an issue or a
|
||||||
|
PR, and I will be glad to add them to this page.<br/>
|
||||||
|
I hope the following lists can grow much more in the future.
|
||||||
|
|
||||||
|
# Similar projects
|
||||||
|
|
||||||
|
Below an incomplete list of similar projects that I have come across so
|
||||||
|
far.<br/>
|
||||||
|
If some terms or designs are not clear, I recommend referring to the
|
||||||
|
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
|
||||||
|
details.
|
||||||
|
|
||||||
|
* C:
|
||||||
|
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
|
||||||
|
on sparse sets.
|
||||||
|
* [Diana](https://github.com/discoloda/Diana): an ECS that uses sparse sets to
|
||||||
|
keep track of entities in systems.
|
||||||
|
* [Flecs](https://github.com/SanderMertens/flecs): a multithreaded archetype
|
||||||
|
ECS based on semi-contiguous arrays rather than chunks.
|
||||||
|
* [lent](https://github.com/nem0/lent): the Donald Trump of the ECS libraries.
|
||||||
|
|
||||||
|
* C++:
|
||||||
|
* [decs](https://github.com/vblanco20-1/decs): a chunk-based archetype ECS.
|
||||||
|
* [ecst](https://github.com/SuperV1234/ecst): a multithreaded compile-time
|
||||||
|
ECS that uses sparse sets to keep track of entities in systems.
|
||||||
|
* [EntityX](https://github.com/alecthomas/entityx): a bitset based ECS that
|
||||||
|
uses a single large matrix of components indexed with entities.
|
||||||
|
* [Gaia-ECS](https://github.com/richardbiely/gaia-ecs): a chunk-based
|
||||||
|
archetype ECS.
|
||||||
|
* [Polypropylene](https://github.com/pmbittner/Polypropylene): a hybrid
|
||||||
|
solution between an ECS and dynamic mixins.
|
||||||
|
|
||||||
|
* C#
|
||||||
|
* [Arch](https://github.com/genaray/Arch): a simple, fast and _unity entities_
|
||||||
|
inspired archetype ECS with optional multithreading.
|
||||||
|
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
|
||||||
|
C# and Unity, where _reactive systems_ were invented.
|
||||||
|
* [Fennecs](https://github.com/outfox/fennecs): the little archetype ECS that
|
||||||
|
loves you back.
|
||||||
|
* [Friflo ECS](https://github.com/friflo/Friflo.Engine.ECS): an archetype ECS
|
||||||
|
with focus on performance and minimal GC allocations.
|
||||||
|
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
|
||||||
|
Component System framework.
|
||||||
|
* [Massive ECS](https://github.com/nilpunch/massive): sparse set based ECS
|
||||||
|
featuring rollbacks.
|
||||||
|
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
|
||||||
|
platform-agnostic and table-based ECS framework.
|
||||||
|
|
||||||
|
* Go:
|
||||||
|
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
|
||||||
|
by `EnTT`.
|
||||||
|
|
||||||
|
* Javascript:
|
||||||
|
* [\@javelin/ecs](https://github.com/3mcd/javelin/tree/master/packages/ecs):
|
||||||
|
an archetype ECS in TypeScript.
|
||||||
|
* [ecsy](https://github.com/MozillaReality/ecsy): I have not had the time to
|
||||||
|
investigate the underlying design of `ecsy` but it looks cool anyway.
|
||||||
|
|
||||||
|
* Perl:
|
||||||
|
* [Game::Entities](https://gitlab.com/jjatria/perl-game-entities): a simple
|
||||||
|
entity registry for ECS designs inspired by `EnTT`.
|
||||||
|
|
||||||
|
* Raku:
|
||||||
|
* [Game::Entities](https://gitlab.com/jjatria/raku-game-entities): a simple
|
||||||
|
entity registry for ECS designs inspired by `EnTT`.
|
||||||
|
|
||||||
|
* Rust:
|
||||||
|
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
|
||||||
|
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
|
||||||
|
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS
|
||||||
|
written in Rust.
|
||||||
|
* [Specs](https://github.com/amethyst/specs): a parallel ECS based mainly on
|
||||||
|
hierarchical bitsets that allows different types of storage as needed.
|
||||||
|
|
||||||
|
* Zig
|
||||||
|
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
|
||||||
Vendored
+186
@@ -0,0 +1,186 @@
|
|||||||
|
# Crash Course: resource management
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
|
||||||
|
* [Resource handle](#resource-handle)
|
||||||
|
* [Loaders](#loaders)
|
||||||
|
* [The cache class](#the-cache-class)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Resource management is usually one of the most critical parts of a game.
|
||||||
|
Solutions are often tuned to the particular application. There exist several
|
||||||
|
approaches and all of them are perfectly fine as long as they fit the
|
||||||
|
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.
|
||||||
|
|
||||||
|
`EnTT` does not pretend to offer a _one-fits-all_ solution for the different
|
||||||
|
cases.<br/>
|
||||||
|
Instead, the library comes with a minimal, general purpose resource cache that
|
||||||
|
might be useful in many cases.
|
||||||
|
|
||||||
|
# The resource, the loader and the cache
|
||||||
|
|
||||||
|
Resource, loader and cache are the three main actors for the purpose.<br/>
|
||||||
|
The _resource_ is an image, an audio, a video or any other type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_resource { const int value; };
|
||||||
|
```
|
||||||
|
|
||||||
|
The _loader_ is a callable type, the aim of which is to load a specific
|
||||||
|
resource:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_loader final {
|
||||||
|
using result_type = std::shared_ptr<my_resource>;
|
||||||
|
|
||||||
|
result_type operator()(int value) const {
|
||||||
|
// ...
|
||||||
|
return std::make_shared<my_resource>(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Its function operator can accept any arguments and should return a value of the
|
||||||
|
declared result type (`std::shared_ptr<my_resource>` in the example).<br/>
|
||||||
|
A loader can also overload its function call operator to make it possible to
|
||||||
|
construct the same or another resource from different lists of arguments.
|
||||||
|
|
||||||
|
Finally, a cache is a specialization of a class template tailored to a specific
|
||||||
|
resource and (optionally) a loader:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using my_cache = entt::resource_cache<my_resource, my_loader>;
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
my_cache cache{};
|
||||||
|
```
|
||||||
|
|
||||||
|
The class is designed to create different caches for different resource types
|
||||||
|
and to manage each one independently in the most appropriate way.<br/>
|
||||||
|
As a (very) trivial example, audio tracks can survive in most of the scenes of
|
||||||
|
an application while meshes can be associated with a single scene only, then
|
||||||
|
discarded when a player leaves it.
|
||||||
|
|
||||||
|
## Resource handle
|
||||||
|
|
||||||
|
Resources are not returned directly to the caller. Instead, they are wrapped in
|
||||||
|
a _resource handle_, an instance of the `entt::resource` class template.<br/>
|
||||||
|
For those who know the _flyweight design pattern_ already, that is exactly what
|
||||||
|
it is. To all others, this is the time to brush up on some notions instead.
|
||||||
|
|
||||||
|
A shared pointer could have been used as a resource handle. In fact, the default
|
||||||
|
implementation mostly maps the interface of its standard counterpart and only
|
||||||
|
adds a few things on top of it.<br/>
|
||||||
|
However, the handle in `EnTT` is designed as a standalone class template. This
|
||||||
|
is due to the fact that specializing a class in the standard library is often
|
||||||
|
undefined behavior while having the ability to specialize the handle for one,
|
||||||
|
more or all resource types could help over time.
|
||||||
|
|
||||||
|
## Loaders
|
||||||
|
|
||||||
|
A loader is responsible for _loading_ resources (quite obviously).<br/>
|
||||||
|
By default, it is just a callable object that forwards its arguments to the
|
||||||
|
resource itself. That is, a _passthrough type_. All the work is demanded to the
|
||||||
|
constructor(s) of the resource itself.<br/>
|
||||||
|
Loaders also are fully customizable as expected.
|
||||||
|
|
||||||
|
A custom loader is a class with at least one function call operator and a member
|
||||||
|
type named `result_type`.<br/>
|
||||||
|
The loader is not required to return a resource handle. As long as `return_type`
|
||||||
|
is suitable for constructing a handle, that is fine.
|
||||||
|
|
||||||
|
When using the default handle, it expects a resource type which is convertible
|
||||||
|
to or suitable for constructing an `std::shared_ptr<Type>` (where `Type` is the
|
||||||
|
actual resource type).<br/>
|
||||||
|
In other terms, the loader should return shared pointers to the given resource
|
||||||
|
type. However, this is not mandatory. Users can easily get around this
|
||||||
|
constraint by specializing both the handle and the loader.
|
||||||
|
|
||||||
|
A cache forwards all its arguments to the loader if required. This means that
|
||||||
|
loaders can also support tag dispatching to offer different loading policies:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_loader {
|
||||||
|
using result_type = std::shared_ptr<my_resource>;
|
||||||
|
|
||||||
|
struct from_disk_tag{};
|
||||||
|
struct from_network_tag{};
|
||||||
|
|
||||||
|
template<typename Args>
|
||||||
|
result_type operator()(from_disk_tag, Args&&... args) {
|
||||||
|
// ...
|
||||||
|
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Args>
|
||||||
|
result_type operator()(from_network_tag, Args&&... args) {
|
||||||
|
// ...
|
||||||
|
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This makes the whole loading logic quite flexible and easy to extend over time.
|
||||||
|
|
||||||
|
## The cache class
|
||||||
|
|
||||||
|
The cache is the class that is asked to _connect the dots_.<br/>
|
||||||
|
It loads the resources, stores them aside and returns handles as needed:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::resource_cache<my_resource, my_loader> cache{};
|
||||||
|
```
|
||||||
|
|
||||||
|
Under the hood, a cache is nothing more than a map where the key value has type
|
||||||
|
`entt::id_type` while the mapped value is whatever type its loader returns.<br/>
|
||||||
|
For this reason, it offers most of the functionalities a user would expect from
|
||||||
|
a map, such as `empty` or `size` and so on. Similarly, it is an iterable type
|
||||||
|
that also supports indexing by resource id:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
for(auto [id, res]: cache) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
|
||||||
|
if(entt::resource<my_resource> res = cache["resource/id"_hs]; res) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Please, refer to the inline documentation for all the details about the other
|
||||||
|
functions (such as `contains` or `erase`).
|
||||||
|
|
||||||
|
Set aside the part of the API that this class _shares_ with a map, it also adds
|
||||||
|
something on top of it in order to address the most common requirements of a
|
||||||
|
resource cache.<br/>
|
||||||
|
In particular, it does not have an `emplace` member function which is replaced
|
||||||
|
by `load` and `force_load` instead (where the former loads a new resource only
|
||||||
|
if not present while the second triggers a forced loading in any case):
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto ret = cache.load("resource/id"_hs);
|
||||||
|
|
||||||
|
// true only if the resource was not already present
|
||||||
|
const bool loaded = ret.second;
|
||||||
|
|
||||||
|
// takes the resource handle pointed to by the returned iterator
|
||||||
|
entt::resource<my_resource> res = ret.first->second;
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the hashed string is used for convenience in the example above.<br/>
|
||||||
|
Resource identifiers are nothing more than integral values. Therefore, plain
|
||||||
|
numbers as well as non-class enum value are accepted.
|
||||||
|
|
||||||
|
It is worth mentioning that the iterators of a cache as well as its indexing
|
||||||
|
operators return resource handles rather than instances of the mapped type.<br/>
|
||||||
|
Since the cache has no control over the loader and a resource is not required to
|
||||||
|
also be convertible to bool, these handles can be invalid. This usually means an
|
||||||
|
error in the user logic, but it may also be an _expected_ event.<br/>
|
||||||
|
It is therefore recommended to verify handles validity with a check in debug
|
||||||
|
(for example, when loading) or an appropriate logic in retail.
|
||||||
Vendored
+559
@@ -0,0 +1,559 @@
|
|||||||
|
# Crash Course: events, signals and everything in between
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Introduction](#introduction)
|
||||||
|
* [Delegate](#delegate)
|
||||||
|
* [Runtime arguments](#runtime-arguments)
|
||||||
|
* [Lambda support](#lambda-support)
|
||||||
|
* [Raw access](#raw-access)
|
||||||
|
* [Signals](#signals)
|
||||||
|
* [Event dispatcher](#event-dispatcher)
|
||||||
|
* [Named queues](#named-queues)
|
||||||
|
* [Event emitter](#event-emitter)
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Signals are more often than not a core part of games and software architectures
|
||||||
|
in general.<br/>
|
||||||
|
They help to decouple the various parts of a system while allowing them to
|
||||||
|
communicate with each other somehow.
|
||||||
|
|
||||||
|
The so-called _modern C++_ comes with a tool that can be useful in this regard,
|
||||||
|
the `std::function`. As an example, it can be used to create delegates.<br/>
|
||||||
|
However, there is no guarantee that an `std::function` does not perform
|
||||||
|
allocations under the hood and this could be problematic sometimes. Furthermore,
|
||||||
|
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 power of an `std::function` is not required or
|
||||||
|
if the price to pay for them is too high, `EnTT` offers a complete set of
|
||||||
|
lightweight classes to solve the same and many other problems.
|
||||||
|
|
||||||
|
# Delegate
|
||||||
|
|
||||||
|
A delegate can be used as a general purpose invoker with no memory overhead for
|
||||||
|
free functions, lambdas and members provided along with an instance on which to
|
||||||
|
invoke them.<br/>
|
||||||
|
It does not claim to be a drop-in replacement for an `std::function`, so do not
|
||||||
|
expect to use it whenever an `std::function` fits well. That said, it is most
|
||||||
|
likely even a better fit than an `std::function` in a lot of cases, so expect to
|
||||||
|
use it quite a lot anyway.
|
||||||
|
|
||||||
|
The interface is trivial. It offers a default constructor to create empty
|
||||||
|
delegates:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::delegate<int(int)> delegate{};
|
||||||
|
```
|
||||||
|
|
||||||
|
What is needed to create an instance is to specify the type of the function the
|
||||||
|
delegate _accepts_, that is the signature of the functions it models.<br/>
|
||||||
|
However, attempting to use an empty delegate by invoking its function call
|
||||||
|
operator results in undefined behavior or most likely a crash.
|
||||||
|
|
||||||
|
There exist a few overloads of the `connect` member function to initialize a
|
||||||
|
delegate:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int f(int i) { return i; }
|
||||||
|
|
||||||
|
struct my_struct {
|
||||||
|
int f(const int &i) const { return i; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// bind a free function to the delegate
|
||||||
|
delegate.connect<&f>();
|
||||||
|
|
||||||
|
// bind a member function to the delegate
|
||||||
|
my_struct instance;
|
||||||
|
delegate.connect<&my_struct::f>(instance);
|
||||||
|
```
|
||||||
|
|
||||||
|
The delegate class also accepts data members, if needed. In this case, the
|
||||||
|
function type of the delegate is such that the parameter list is empty and the
|
||||||
|
value of the data member is at least convertible to the return type.
|
||||||
|
|
||||||
|
Free functions having type equivalent to `void(T &, args...)` are accepted as
|
||||||
|
well. The first argument `T &` is considered a payload, and the function will
|
||||||
|
receive it back every time it is invoked. In other terms, this works just fine
|
||||||
|
with the above definition:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void g(const char &c, int i) { /* ... */ }
|
||||||
|
const char c = 'c';
|
||||||
|
|
||||||
|
delegate.connect<&g>(c);
|
||||||
|
delegate(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
Function `g` is invoked with a reference to `c` and `42`. However, the function
|
||||||
|
type of the delegate is still `void(int)`. This is also the signature of its
|
||||||
|
function call operator.<br/>
|
||||||
|
Another interesting aspect of the delegate class is that it accepts functions
|
||||||
|
with a list of parameters that is shorter than that of its function type:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void g() { /* ... */ }
|
||||||
|
delegate.connect<&g>();
|
||||||
|
delegate(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
Where the function type of the delegate is `void(int)` as above. It goes without
|
||||||
|
saying that the extra arguments are silently discarded internally. This is a
|
||||||
|
nice-to-have feature in a lot of cases, as an example when the `delegate` class
|
||||||
|
is used as a building block of a signal-slot system.<br/>
|
||||||
|
In fact, this filtering works both ways. The class tries to pass its first
|
||||||
|
_count_ arguments **first**, then the last _count_. Watch out for conversion
|
||||||
|
rules if in doubt when connecting a listener!<br/>
|
||||||
|
Arbitrary functions that pull random arguments from the delegate list are not
|
||||||
|
supported instead. Other features were preferred, such as support for functions
|
||||||
|
with compatible argument lists although not equal to those of the delegate.
|
||||||
|
|
||||||
|
To create and initialize a delegate at once, there are a few specialized
|
||||||
|
constructors. Because of the rules of the language, the listener is provided by
|
||||||
|
means of the `entt::connect_arg` variable template:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::delegate<int(int)> func{entt::connect_arg<&f>};
|
||||||
|
```
|
||||||
|
|
||||||
|
Aside `connect`, a `disconnect` counterpart is not provided. Instead, there
|
||||||
|
exists a `reset` member function to use to clear a delegate.<br/>
|
||||||
|
To know if a delegate is empty, it can be used explicitly in every conditional
|
||||||
|
statement:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if(delegate) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, to invoke a delegate, the function call operator is the way to go as
|
||||||
|
already shown in the examples above:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto ret = delegate(42);
|
||||||
|
```
|
||||||
|
|
||||||
|
In all cases, listeners do not have to strictly follow the signature of the
|
||||||
|
delegate. As long as a listener can be invoked with the given arguments to yield
|
||||||
|
a result that is convertible to the given result type, everything works just
|
||||||
|
fine.
|
||||||
|
|
||||||
|
As a side note, members of classes may or may not be associated with instances.
|
||||||
|
If they are not, the first argument of the function type must be that of the
|
||||||
|
class on which the members operate, and an instance of this class must obviously
|
||||||
|
be passed when invoking the delegate:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::delegate<void(my_struct &, int)> delegate;
|
||||||
|
delegate.connect<&my_struct::f>();
|
||||||
|
|
||||||
|
my_struct instance;
|
||||||
|
delegate(instance, 42);
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, it is not possible to _deduce_ the function type since the first
|
||||||
|
argument does not necessarily have to be a reference (for example, it can be a
|
||||||
|
pointer, as well as a const reference).<br/>
|
||||||
|
Therefore, the function type must be declared explicitly for unbound members.
|
||||||
|
|
||||||
|
## Runtime arguments
|
||||||
|
|
||||||
|
The `delegate` class is meant to be used primarily with template arguments.
|
||||||
|
However, as a consequence of its design, it also offers minimal support for
|
||||||
|
runtime arguments.<br/>
|
||||||
|
When used like this, some features are not supported though. In particular:
|
||||||
|
|
||||||
|
* Curried functions are not accepted.
|
||||||
|
* Functions with an argument list that differs from that of the delegate are not
|
||||||
|
supported.
|
||||||
|
* Return type and types of arguments **must** coincide with those of the
|
||||||
|
delegate and _being at least convertible_ is not enough anymore.
|
||||||
|
|
||||||
|
Moreover, for a given function type `Ret(Args...)`, the signature of the
|
||||||
|
functions connected at runtime must necessarily be `Ret(const void *, Args...)`.
|
||||||
|
|
||||||
|
Runtime arguments can be passed both to the constructor of a delegate and to the
|
||||||
|
`connect` member function. An optional parameter is also accepted in both cases.
|
||||||
|
This argument is used to pass arbitrary user data back and forth as a
|
||||||
|
`const void *` upon invocation.<br/>
|
||||||
|
To connect a function to a delegate _in the hard way_:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int func(const void *ptr, int i) { return *static_cast<const int *>(ptr) * i; }
|
||||||
|
const int value = 42;
|
||||||
|
|
||||||
|
// use the constructor ...
|
||||||
|
entt::delegate delegate{&func, &value};
|
||||||
|
|
||||||
|
// ... or the connect member function
|
||||||
|
delegate.connect(&func, &value);
|
||||||
|
```
|
||||||
|
|
||||||
|
The type of the delegate is deduced from the function if possible. In this case,
|
||||||
|
since the first argument is an implementation detail, the deduced function type
|
||||||
|
is `int(int)`.<br/>
|
||||||
|
Invoking a delegate built in this way follows the same rules as previously
|
||||||
|
explained.
|
||||||
|
|
||||||
|
## Lambda support
|
||||||
|
|
||||||
|
In general, the `delegate` class does not fully support lambda functions in all
|
||||||
|
their nuances. The reason is pretty simple: a `delegate` is not a drop-in
|
||||||
|
replacement for an `std::function`. Instead, it tries to overcome the problems
|
||||||
|
with the latter.<br/>
|
||||||
|
That being said, non-capturing lambda functions are supported, even though some
|
||||||
|
features are not available in this case.
|
||||||
|
|
||||||
|
This is a logical consequence of the support for connecting functions at
|
||||||
|
runtime. Therefore, lambda functions undergo the same rules and
|
||||||
|
limitations.<br/>
|
||||||
|
In fact, since non-capturing lambda functions decay to pointers to functions,
|
||||||
|
they can be used with a `delegate` as if they were _normal functions_ with
|
||||||
|
optional payload:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
my_struct instance;
|
||||||
|
|
||||||
|
// use the constructor ...
|
||||||
|
entt::delegate delegate{+[](const void *ptr, int value) {
|
||||||
|
return static_cast<const my_struct *>(ptr)->f(value);
|
||||||
|
}, &instance};
|
||||||
|
|
||||||
|
// ... or the connect member function
|
||||||
|
delegate.connect([](const void *ptr, int value) {
|
||||||
|
return static_cast<const my_struct *>(ptr)->f(value);
|
||||||
|
}, &instance);
|
||||||
|
```
|
||||||
|
|
||||||
|
As above, the first parameter (`const void *`) is not part of the function type
|
||||||
|
of the delegate and is used to dispatch arbitrary user data back and forth. In
|
||||||
|
other terms, the function type of the delegate above is `int(int)`.
|
||||||
|
|
||||||
|
## Raw access
|
||||||
|
|
||||||
|
While not recommended, a delegate also allows direct access to the stored
|
||||||
|
callable function target and underlying data, if any.<br/>
|
||||||
|
This makes it possible to bypass the behavior of the delegate itself and force
|
||||||
|
calls on different instances:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
my_struct other;
|
||||||
|
delegate.target(&other, 42);
|
||||||
|
```
|
||||||
|
|
||||||
|
It goes without saying that this type of approach is **very** risky, especially
|
||||||
|
since there is no way of knowing whether the contained function was originally a
|
||||||
|
member function of some class, a free function or a lambda.<br/>
|
||||||
|
Another possible (and meaningful) use of this feature is that of identifying a
|
||||||
|
particular delegate through its descriptive _traits_ instead.
|
||||||
|
|
||||||
|
# Signals
|
||||||
|
|
||||||
|
Signal handlers work with references to classes, function pointers, and pointers
|
||||||
|
to members. Listeners can be any kind of objects, and users are in charge of
|
||||||
|
connecting and disconnecting them from a signal to avoid crashes due to
|
||||||
|
different lifetimes. On the other side, performance should not be affected that
|
||||||
|
much by the presence of such a signal handler.<br/>
|
||||||
|
Signals make use of delegates internally, and therefore they undergo the same
|
||||||
|
rules and offer similar functionalities. It may be a good idea to consult the
|
||||||
|
documentation of the `delegate` class for further information.
|
||||||
|
|
||||||
|
A signal handler is can be used as a private data member without exposing any
|
||||||
|
_publish_ functionality to the clients of a class.<br/>
|
||||||
|
The basic idea is to impose a clear separation between the signal itself and the
|
||||||
|
`sink` class, that is a tool to be used to connect and disconnect listeners on
|
||||||
|
the fly.
|
||||||
|
|
||||||
|
The API of a signal handler is straightforward. If a collector is supplied to
|
||||||
|
the signal when something is published, all the values returned by its listeners
|
||||||
|
are literally _collected_ and used later by the caller. Otherwise, the class
|
||||||
|
works just like a plain signal that emits events from time to time.<br/>
|
||||||
|
To create instances of signal handlers it is sufficient to provide the type of
|
||||||
|
function to which they refer:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
entt::sigh<void(int, char)> signal;
|
||||||
|
```
|
||||||
|
|
||||||
|
Signals offer all the basic functionalities required to know how many listeners
|
||||||
|
they contain (`size`) or if they contain at least a listener (`empty`), as well
|
||||||
|
as a function to use to swap 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:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void foo(int, char) { /* ... */ }
|
||||||
|
|
||||||
|
struct listener {
|
||||||
|
void bar(const int &, char) { /* ... */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
entt::sink sink{signal};
|
||||||
|
listener instance;
|
||||||
|
|
||||||
|
sink.connect<&foo>();
|
||||||
|
sink.connect<&listener::bar>(instance);
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// disconnects a free function
|
||||||
|
sink.disconnect<&foo>();
|
||||||
|
|
||||||
|
// disconnect a member function of an instance
|
||||||
|
sink.disconnect<&listener::bar>(instance);
|
||||||
|
|
||||||
|
// disconnect all member functions of an instance, if any
|
||||||
|
sink.disconnect(&instance);
|
||||||
|
|
||||||
|
// discards all listeners at once
|
||||||
|
sink.disconnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
As shown above, listeners do not have to strictly follow the signature of the
|
||||||
|
signal. As long as a listener can be invoked with the given arguments to yield a
|
||||||
|
result that is convertible to the given return type, everything works just
|
||||||
|
fine.<br/>
|
||||||
|
In all cases, the `connect` member function returns by default a `connection`
|
||||||
|
object to be used as an alternative to break a connection by means of its
|
||||||
|
`release` member function.<br/>
|
||||||
|
A `scoped_connection` can also be created from a connection. In this case, the
|
||||||
|
link is broken automatically as soon as the object goes out of scope.
|
||||||
|
|
||||||
|
Once listeners are attached (or even if there are no listeners at all), events
|
||||||
|
and data in general are published through a signal by means of the `publish`
|
||||||
|
member function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
signal.publish(42, 'c');
|
||||||
|
```
|
||||||
|
|
||||||
|
To collect data, the `collect` member function is used instead:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
int f() { return 0; }
|
||||||
|
int g() { return 1; }
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
entt::sigh<int()> signal;
|
||||||
|
entt::sink sink{signal};
|
||||||
|
|
||||||
|
sink.connect<&f>();
|
||||||
|
sink.connect<&g>();
|
||||||
|
|
||||||
|
std::vector<int> vec{};
|
||||||
|
signal.collect([&vec](int value) { vec.push_back(value); });
|
||||||
|
|
||||||
|
assert(vec[0] == 0);
|
||||||
|
assert(vec[1] == 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
A collector must expose a function operator that accepts as an argument a type
|
||||||
|
to which the return type of the listeners can be converted. Moreover, it can
|
||||||
|
optionally return a boolean value that is true to stop collecting data, false
|
||||||
|
otherwise. This way one can avoid calling all the listeners in case it is not
|
||||||
|
necessary.<br/>
|
||||||
|
Functors can also be used in place of a lambda. Since the collector is copied
|
||||||
|
when invoking the `collect` member function, `std::ref` is the way to go in this
|
||||||
|
case:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_collector {
|
||||||
|
std::vector<int> vec{};
|
||||||
|
|
||||||
|
bool operator()(int v) {
|
||||||
|
vec.push_back(v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
my_collector collector;
|
||||||
|
signal.collect(std::ref(collector));
|
||||||
|
```
|
||||||
|
|
||||||
|
# Event dispatcher
|
||||||
|
|
||||||
|
The event dispatcher class allows users to trigger immediate events or to queue
|
||||||
|
and publish them all together later.<br/>
|
||||||
|
This class lazily instantiates its queues. Therefore, it is not necessary to
|
||||||
|
_announce_ the event types in advance:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// define a general purpose dispatcher
|
||||||
|
entt::dispatcher dispatcher{};
|
||||||
|
```
|
||||||
|
|
||||||
|
A listener registered with a dispatcher is such that its type offers one or more
|
||||||
|
member functions that take arguments of type `Event &` for any type of event,
|
||||||
|
regardless of the return value.<br/>
|
||||||
|
These functions are linked directly via `connect` to a _sink_:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct an_event { int value; };
|
||||||
|
struct another_event {};
|
||||||
|
|
||||||
|
struct listener {
|
||||||
|
void receive(const an_event &) { /* ... */ }
|
||||||
|
void method(const another_event &) { /* ... */ }
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
listener listener;
|
||||||
|
dispatcher.sink<an_event>().connect<&listener::receive>(listener);
|
||||||
|
dispatcher.sink<another_event>().connect<&listener::method>(listener);
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that connecting listeners within event handlers can result in undefined
|
||||||
|
behavior.<br/>
|
||||||
|
The `disconnect` member function is used to remove one listener at a time or all
|
||||||
|
of them at once:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
dispatcher.sink<an_event>().disconnect<&listener::receive>(listener);
|
||||||
|
dispatcher.sink<another_event>().disconnect(&listener);
|
||||||
|
```
|
||||||
|
|
||||||
|
The `trigger` member function serves the purpose of sending an immediate event
|
||||||
|
to all the listeners registered so far:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
dispatcher.trigger(an_event{42});
|
||||||
|
dispatcher.trigger<another_event>();
|
||||||
|
```
|
||||||
|
|
||||||
|
Listeners are invoked immediately, order of execution is not guaranteed. This
|
||||||
|
method can be used to push around urgent messages like an _is terminating_
|
||||||
|
notification on a mobile app.
|
||||||
|
|
||||||
|
On the other hand, the `enqueue` member function queues messages together and
|
||||||
|
helps to maintain control over the moment they are sent to listeners:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
dispatcher.enqueue<an_event>(42);
|
||||||
|
dispatcher.enqueue(another_event{});
|
||||||
|
```
|
||||||
|
|
||||||
|
Events are stored aside until the `update` member function is invoked:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// emits all the events of the given type at once
|
||||||
|
dispatcher.update<an_event>();
|
||||||
|
|
||||||
|
// emits all the events queued so far at once
|
||||||
|
dispatcher.update();
|
||||||
|
```
|
||||||
|
|
||||||
|
This way users can embed the dispatcher in a loop and literally dispatch events
|
||||||
|
once per tick to their systems.
|
||||||
|
|
||||||
|
## Named queues
|
||||||
|
|
||||||
|
All queues within a dispatcher are associated by default with an event type and
|
||||||
|
then retrieved from it.<br/>
|
||||||
|
However, it is possible to create queues with different _names_ (and therefore
|
||||||
|
also multiple queues for a single type). In fact, more or less all functions
|
||||||
|
also take an additional parameter. As an example:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
dispatcher.sink<an_event>("custom"_hs).connect<&listener::receive>(listener);
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, the term _name_ is misused as these are actual numeric identifiers
|
||||||
|
of type `id_type`.<br/>
|
||||||
|
An exception to this rule is the `enqueue` function. There is no additional
|
||||||
|
parameter for it but rather a different function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
dispatcher.enqueue_hint<an_event>("custom"_hs, 42);
|
||||||
|
```
|
||||||
|
|
||||||
|
This is mainly due to the template argument deduction rules, and there is no
|
||||||
|
real (elegant) way to avoid it.
|
||||||
|
|
||||||
|
# Event emitter
|
||||||
|
|
||||||
|
A general purpose event emitter thought mainly for those cases where it comes to
|
||||||
|
working with asynchronous stuff.<br/>
|
||||||
|
Originally designed to fit the requirements of
|
||||||
|
[`uvw`](https://github.com/skypjack/uvw) (a wrapper for `libuv` written in
|
||||||
|
modern C++), it was adapted later to be included in this library.
|
||||||
|
|
||||||
|
To create an emitter type, derived classes must inherit from the base as:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_emitter: emitter<my_emitter> {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Handlers for the different events are created internally on the fly. It is not
|
||||||
|
required to specify in advance the full list of accepted events.<br/>
|
||||||
|
Moreover, whenever an event is published, an emitter also passes a reference
|
||||||
|
to itself to its listeners.
|
||||||
|
|
||||||
|
To create new instances of an emitter, no arguments are required:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
my_emitter emitter{};
|
||||||
|
```
|
||||||
|
|
||||||
|
Listeners are movable and callable objects (free functions, lambdas, functors,
|
||||||
|
`std::function`s, whatever) whose function type is compatible with:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
void(Type &, my_emitter &)
|
||||||
|
```
|
||||||
|
|
||||||
|
Where `Type` is the type of event they want to receive.<br/>
|
||||||
|
To attach a listener to an emitter, there exists the `on` member function:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Similarly, the `reset` member function is used to disconnect listeners given a
|
||||||
|
type while `clear` is used to disconnect all listeners at once:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// resets the listener for my_event
|
||||||
|
emitter.erase<my_event>();
|
||||||
|
|
||||||
|
// resets all listeners
|
||||||
|
emitter.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
To send an event to the listener registered on a given type, the `publish`
|
||||||
|
function is the way to go:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct my_event { int i; };
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
emitter.publish(my_event{42});
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, the `empty` member function tests if there exists at least a listener
|
||||||
|
registered with the event emitter while `contains` is used to check if a given
|
||||||
|
event type is associated with a valid listener:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
if(emitter.contains<my_event>()) {
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This class introduces a _nice-to-have_ model based on events and listeners.<br/>
|
||||||
|
More in general, it is a handy tool when the derived classes _wrap_ asynchronous
|
||||||
|
operations, but it is not limited to such uses.
|
||||||
Vendored
+103
@@ -0,0 +1,103 @@
|
|||||||
|
# EnTT and Unreal Engine
|
||||||
|
|
||||||
|
# Table of Contents
|
||||||
|
|
||||||
|
* [Enable Cpp17](#enable-cpp17)
|
||||||
|
* [EnTT as a third party module](#entt-as-a-third-party-module)
|
||||||
|
* [Include EnTT](#include-entt)
|
||||||
|
|
||||||
|
## Enable Cpp17
|
||||||
|
|
||||||
|
> Skip this part if you are working with UE5, Since UE5 uses cpp17 by default.
|
||||||
|
|
||||||
|
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
|
||||||
|
is C++14.<br/>
|
||||||
|
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in
|
||||||
|
the main module of the project there should be a `<Game Name>.Build.cs` file,
|
||||||
|
the constructor of which must contain the following lines:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
PCHUsage = PCHUsageMode.NoSharedPCHs;
|
||||||
|
PrivatePCHHeaderFile = "<PCH filename>.h";
|
||||||
|
CppStandard = CppStandardVersion.Cpp17;
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace `<PCH filename>.h` with the name of the already existing PCH header
|
||||||
|
file, if any.<br/>
|
||||||
|
In case the project does not already contain a file of this type, it is possible
|
||||||
|
to create one with the following content:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#pragma once
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
```
|
||||||
|
|
||||||
|
Remember to remove any old `PCHUsage = <...>` line that was previously there. At
|
||||||
|
this point, C++17 support should be in place.<br/>
|
||||||
|
Try to compile the project to ensure it works as expected before following
|
||||||
|
further steps.
|
||||||
|
|
||||||
|
Note that updating a *project* to C++17 does not necessarily mean that the IDE
|
||||||
|
in use will also start to recognize its syntax.<br/>
|
||||||
|
If the plan is to use C++17 in the project too, check the specific instructions
|
||||||
|
for the IDE in use.
|
||||||
|
|
||||||
|
## EnTT as a third party module
|
||||||
|
|
||||||
|
Once this point is reached, the `Source` directory should look like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
Source
|
||||||
|
| MyGame.Target.cs
|
||||||
|
| MyGameEditor.Target.cs
|
||||||
|
|
|
||||||
|
+---MyGame
|
||||||
|
| | MyGame.Build.cs
|
||||||
|
| | MyGame.h (PCH Header file)
|
||||||
|
|
|
||||||
|
\---ThirdParty
|
||||||
|
\---EnTT
|
||||||
|
| EnTT.Build.cs
|
||||||
|
|
|
||||||
|
\---entt (GitHub repository content inside)
|
||||||
|
```
|
||||||
|
|
||||||
|
To make this happen, create the folder `ThirdParty` under `Source` if it does
|
||||||
|
not exist already. Then, add an `EnTT` folder under `ThirdParty`.<br/>
|
||||||
|
Within the latter, create a new file `EnTT.Build.cs` with the following content:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
using System.IO;
|
||||||
|
using UnrealBuildTool;
|
||||||
|
|
||||||
|
public class EnTT: ModuleRules {
|
||||||
|
public EnTT(ReadOnlyTargetRules Target) : base(Target) {
|
||||||
|
Type = ModuleType.External;
|
||||||
|
PublicIncludePaths.Add(Path.Combine(ModuleDirectory, "entt", "src", "entt"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The last line indicates that the actual files will be found in the directory
|
||||||
|
`EnTT/entt/src/entt`.<br/>
|
||||||
|
Download the repository for `EnTT` and place it next to `EnTT.Build.cs` or
|
||||||
|
update the path above accordingly.
|
||||||
|
|
||||||
|
Finally, open the `<Game Name>.Build.cs` file and add `EnTT` as a dependency at
|
||||||
|
the end of the list:
|
||||||
|
|
||||||
|
```cs
|
||||||
|
PublicDependencyModuleNames.AddRange(new[] {
|
||||||
|
"Core", "CoreUObject", "Engine", "InputCore", [...], "EnTT"
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that some IDEs might require a restart to start recognizing the new module
|
||||||
|
for code-highlighting features and such.
|
||||||
|
|
||||||
|
## Include EnTT
|
||||||
|
|
||||||
|
In any source file of the project, add `#include "entt.hpp"` or any other path
|
||||||
|
to the file from `EnTT` to use it.<br/>
|
||||||
|
Try to create a registry as `entt::registry registry;` to make sure everything
|
||||||
|
compiles fine.
|
||||||
Vendored
+52
@@ -0,0 +1,52 @@
|
|||||||
|
[
|
||||||
|
# gtest only
|
||||||
|
{ "include": [ "@<gtest/internal/.*>", "private", "<gtest/gtest.h>", "public" ] },
|
||||||
|
{ "include": [ "@<gtest/gtest-.*>", "private", "<gtest/gtest.h>", "public" ] },
|
||||||
|
# forward files
|
||||||
|
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/table.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/compressed_pair.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/component.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/mixin.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/ranges.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/adjacency_matrix.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/dot.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/flow.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/meta/fwd\\.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/poly/fwd\\.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/process.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/scheduler.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
|
||||||
|
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] },
|
||||||
|
# symbols
|
||||||
|
{ symbol: [ "std::allocator", private, "<entt/container/fwd.hpp>", public ] },
|
||||||
|
{ symbol: [ "std::allocator", private, "<entt/entity/fwd.hpp>", public ] },
|
||||||
|
{ symbol: [ "std::allocator", private, "<entt/graph/fwd.hpp>", public ] },
|
||||||
|
{ symbol: [ "std::allocator", private, "<entt/process/fwd.hpp>", public ] },
|
||||||
|
{ symbol: [ "std::allocator", private, "<entt/resource/fwd.hpp>", public ] },
|
||||||
|
{ symbol: [ "std::allocator", private, "<entt/signal/fwd.hpp>", public ] }
|
||||||
|
]
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
</AutoVisualizer>
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::dense_map<*>">
|
||||||
|
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
|
||||||
|
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
|
||||||
|
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[capacity]" ExcludeView="simple">packed.first_base::value.capacity()</Item>
|
||||||
|
<Item Name="[bucket_count]" ExcludeView="simple">bucket_count()</Item>
|
||||||
|
<Item Name="[load_factor]" ExcludeView="simple">(float)size() / (float)bucket_count()</Item>
|
||||||
|
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>size()</Size>
|
||||||
|
<ValueNode>packed.first_base::value[$i].element</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::dense_set<*>">
|
||||||
|
<Intrinsic Name="size" Expression="packed.first_base::value.size()"/>
|
||||||
|
<Intrinsic Name="bucket_count" Expression="sparse.first_base::value.size()"/>
|
||||||
|
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[capacity]" ExcludeView="simple">packed.first_base::value.capacity()</Item>
|
||||||
|
<Item Name="[bucket_count]" ExcludeView="simple">bucket_count()</Item>
|
||||||
|
<Item Name="[load_factor]" ExcludeView="simple">(float)size() / (float)bucket_count()</Item>
|
||||||
|
<Item Name="[max_load_factor]" ExcludeView="simple">threshold</Item>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>size()</Size>
|
||||||
|
<ValueNode>packed.first_base::value[$i].second</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_table<*>">
|
||||||
|
<DisplayString>{ payload }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>payload</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::basic_any<*>">
|
||||||
|
<DisplayString>{{ type={ info->alias,na }, policy={ mode,en } }}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::compressed_pair<*>">
|
||||||
|
<Intrinsic Name="first" Optional="true" Expression="((first_base*)this)->value"/>
|
||||||
|
<Intrinsic Name="first" Optional="true" Expression="*(first_base::base_type*)this"/>
|
||||||
|
<Intrinsic Name="second" Optional="true" Expression="((second_base*)this)->value"/>
|
||||||
|
<Intrinsic Name="second" Optional="true" Expression="*(second_base::base_type*)this"/>
|
||||||
|
<DisplayString>({ first() }, { second() })</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[first]">first()</Item>
|
||||||
|
<Item Name="[second]">second()</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_hashed_string<*>">
|
||||||
|
<DisplayString Condition="base_type::repr != nullptr">{{ hash={ base_type::hash } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[data]">base_type::repr,na</Item>
|
||||||
|
<Item Name="[length]">base_type::length</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::type_info">
|
||||||
|
<DisplayString>{{ name={ alias,na } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[hash]">identifier</Item>
|
||||||
|
<Item Name="[index]">seq</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+181
@@ -0,0 +1,181 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::basic_registry<*>">
|
||||||
|
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[entities]">entities</Item>
|
||||||
|
<Synthetic Name="[pools]">
|
||||||
|
<DisplayString>{ pools.size() }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0" />
|
||||||
|
<Variable Name="last" InitialValue="pools.size()"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<Item Name="[{ pools.packed.first_base::value[pos].element.first }]">
|
||||||
|
*pools.packed.first_base::value[pos].element.second,view(simple)
|
||||||
|
</Item>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Item Name="[groups]">groups.size()</Item>
|
||||||
|
<Synthetic Name="[vars]">
|
||||||
|
<DisplayString>{ vars.ctx.size() }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0" />
|
||||||
|
<Variable Name="last" InitialValue="vars.ctx.size()"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<Item Name="[{ vars.ctx.packed.first_base::value[pos].element.first }]">
|
||||||
|
vars.ctx.packed.first_base::value[pos].element.second
|
||||||
|
</Item>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_sparse_set<*>">
|
||||||
|
<Intrinsic Name="is_valid_position" Expression="sparse[page] && ((*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask) != traits_type::entity_mask)">
|
||||||
|
<Parameter Name="page" Type="traits_type::entity_type"/>
|
||||||
|
<Parameter Name="offset" Type="traits_type::entity_type"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<Intrinsic Name="is_valid_entity" Expression="!traits_type::version_mask || (*((traits_type::entity_type *)&entity) < (traits_type::version_mask << traits_type::length))">
|
||||||
|
<Parameter Name="entity" Type="const traits_type::value_type &"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||||
|
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
|
||||||
|
<Item Name="[free_list]" ExcludeView="simple">head</Item>
|
||||||
|
<Synthetic Name="[sparse]">
|
||||||
|
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0"/>
|
||||||
|
<Variable Name="page" InitialValue="0"/>
|
||||||
|
<Variable Name="offset" InitialValue="0"/>
|
||||||
|
<Variable Name="last" InitialValue="sparse.size() * traits_type::page_size"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<Exec>page = pos / traits_type::page_size</Exec>
|
||||||
|
<Exec>offset = pos & (traits_type::page_size - 1)</Exec>
|
||||||
|
<If Condition="is_valid_position(page, offset)">
|
||||||
|
<Item Name="[{ pos }]">*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask</Item>
|
||||||
|
</If>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
<Synthetic Name="[packed]">
|
||||||
|
<DisplayString>{ packed.size() }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0"/>
|
||||||
|
<Variable Name="last" InitialValue="packed.size()"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<If Condition="is_valid_entity(packed[pos])">
|
||||||
|
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||||
|
</If>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_storage<*>">
|
||||||
|
<Intrinsic Name="is_valid_entity" Expression="!base_type::traits_type::version_mask || (*((base_type::traits_type::entity_type *)&entity) < (base_type::traits_type::version_mask << base_type::traits_type::length))">
|
||||||
|
<Parameter Name="entity" Type="const base_type::traits_type::value_type &"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
|
||||||
|
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
|
||||||
|
<Item Name="[placeholder]" Optional="true" ExcludeView="simple">placeholder</Item>
|
||||||
|
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
|
||||||
|
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
|
||||||
|
<CustomListItems Condition="payload.size() != 0" Optional="true">
|
||||||
|
<Variable Name="pos" InitialValue="0" />
|
||||||
|
<Variable Name="last" InitialValue="base_type::packed.size()"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<If Condition="is_valid_entity(base_type::packed[pos])">
|
||||||
|
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos & (traits_type::page_size - 1)]</Item>
|
||||||
|
</If>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_common_view<*,*,*>">
|
||||||
|
<DisplayString Condition="index != $T2">{{ size_hint={ pools[index]->packed.size() } }}</DisplayString>
|
||||||
|
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[pools]">pools,na</Item>
|
||||||
|
<Item Name="[filter]">filter,na</Item>
|
||||||
|
<Item Name="[handle]" Condition="index != $T2">pools[index],na</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_storage_view<*>">
|
||||||
|
<DisplayString Condition="leading != nullptr">{{ size={ leading->packed.size() } }}</DisplayString>
|
||||||
|
<DisplayString>{{ size=0 }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_view<*>">
|
||||||
|
<DisplayString>{ *(base_type*)this }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>*(base_type*)this</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_runtime_view<*>">
|
||||||
|
<DisplayString Condition="pools.size() != 0u">{{ size_hint={ pools[0]->packed.size() } }}</DisplayString>
|
||||||
|
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[pools]">pools,na</Item>
|
||||||
|
<Item Name="[filter]">filter,na</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_handle<*>">
|
||||||
|
<Intrinsic Name="pool_at" Expression="owner->pools.packed.first_base::value[index].element.second._Ptr">
|
||||||
|
<Parameter Name="index" Type="unsigned int"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString>{{ entity={ entt } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[entity]">entt</Item>
|
||||||
|
<Item Name="[registry]" Condition="owner != nullptr">owner,na</Item>
|
||||||
|
<Synthetic Name="[components]" Condition="owner != nullptr">
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="entity_mask" InitialValue="traits_type::entity_mask"/>
|
||||||
|
<Variable Name="page" InitialValue="((*((traits_type::entity_type *)&entt)) & entity_mask) / traits_type::page_size"/>
|
||||||
|
<Variable Name="offset" InitialValue="(*((traits_type::entity_type *)&entt)) & (traits_type::page_size - 1u)"/>
|
||||||
|
<Variable Name="last" InitialValue="owner->pools.packed.first_base::value.size()"/>
|
||||||
|
<Variable Name="pos" InitialValue="0u"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<If Condition="pool_at(pos)->sparse.size() > page && pool_at(pos)->sparse[page] != nullptr && ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask">
|
||||||
|
<Item Name="[{ pool_at(pos)->info->alias,na }:{ ((*((traits_type::entity_type *)&pool_at(pos)->sparse[page][offset])) & entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
|
||||||
|
</If>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Synthetic>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::null_t">
|
||||||
|
<DisplayString><null></DisplayString>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::tombstone_t">
|
||||||
|
<DisplayString><tombstone></DisplayString>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::adjacency_matrix<*>">
|
||||||
|
<DisplayString>{{ size={ vert } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0" />
|
||||||
|
<Variable Name="last" InitialValue="vert * vert"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<If Condition="matrix[pos] != 0u">
|
||||||
|
<Item Name="{pos / vert}">pos % vert</Item>
|
||||||
|
</If>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
</AutoVisualizer>
|
||||||
+188
@@ -0,0 +1,188 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::internal::meta_base_node">
|
||||||
|
<DisplayString Condition="resolve != nullptr">{{ type={ type } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[type]">type</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_conv_node">
|
||||||
|
<DisplayString Condition="conv != nullptr">{{ type={ type } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[type]">type</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_ctor_node">
|
||||||
|
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[id]">id</Item>
|
||||||
|
<Item Name="[arity]">arity</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_custom_node">
|
||||||
|
<DisplayString Condition="value != nullptr">{{ type={ type } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[type]">type</Item>
|
||||||
|
<Item Name="[value]">value</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_data_node">
|
||||||
|
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||||
|
<Parameter Name="property" Type="int"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[id]">id</Item>
|
||||||
|
<Item Name="[arity]">arity</Item>
|
||||||
|
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
|
||||||
|
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
|
||||||
|
<Item Name="[custom]">custom</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_dtor_node">
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand/>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_func_node" >
|
||||||
|
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||||
|
<Parameter Name="property" Type="int"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[id]">id</Item>
|
||||||
|
<Item Name="[arity]">arity</Item>
|
||||||
|
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
|
||||||
|
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
|
||||||
|
<Item Name="[next]" Condition="next != nullptr">*next</Item>
|
||||||
|
<Item Name="[custom]">custom</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_template_node">
|
||||||
|
<DisplayString Condition="arity != 0u">{{ arity={ arity } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[arity]">arity</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_type_descriptor">
|
||||||
|
<DisplayString/>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[ctor]">ctor,view(simple)</Item>
|
||||||
|
<Item Name="[base]">base,view(simple)</Item>
|
||||||
|
<Item Name="[conv]">conv,view(simple)</Item>
|
||||||
|
<Item Name="[data]">data,view(simple)</Item>
|
||||||
|
<Item Name="[func]">func,view(simple)</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::meta_type_node">
|
||||||
|
<Intrinsic Name="has_trait" Expression="!!(traits & property)">
|
||||||
|
<Parameter Name="property" Type="int"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[id]">id</Item>
|
||||||
|
<Item Name="[sizeof]">size_of</Item>
|
||||||
|
<Item Name="[is_arithmetic]">has_trait(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||||
|
<Item Name="[is_integral]">has_trait(entt::internal::meta_traits::is_integral)</Item>
|
||||||
|
<Item Name="[is_signed]">has_trait(entt::internal::meta_traits::is_signed)</Item>
|
||||||
|
<Item Name="[is_array]">has_trait(entt::internal::meta_traits::is_array)</Item>
|
||||||
|
<Item Name="[is_enum]">has_trait(entt::internal::meta_traits::is_enum)</Item>
|
||||||
|
<Item Name="[is_class]">has_trait(entt::internal::meta_traits::is_class)</Item>
|
||||||
|
<Item Name="[is_pointer]">has_trait(entt::internal::meta_traits::is_pointer)</Item>
|
||||||
|
<Item Name="[is_pointer_like]">has_trait(entt::internal::meta_traits::is_pointer_like)</Item>
|
||||||
|
<Item Name="[is_sequence_container]">has_trait(entt::internal::meta_traits::is_sequence_container)</Item>
|
||||||
|
<Item Name="[is_associative_container]">has_trait(entt::internal::meta_traits::is_associative_container)</Item>
|
||||||
|
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
|
||||||
|
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
|
||||||
|
<Item Name="[from_void]">from_void != nullptr</Item>
|
||||||
|
<Item Name="[template_info]">templ</Item>
|
||||||
|
<Item Name="[custom]">custom</Item>
|
||||||
|
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_any">
|
||||||
|
<DisplayString Condition="node.info != nullptr">{{ type={ node.info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>node</ExpandedItem>
|
||||||
|
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_handle">
|
||||||
|
<DisplayString>{ any }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>any</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_associative_container">
|
||||||
|
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||||
|
<Item Name="[const]">const_only</Item>
|
||||||
|
<Item Name="[data]">data</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_sequence_container">
|
||||||
|
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||||
|
<Item Name="[const]">const_only</Item>
|
||||||
|
<Item Name="[data]">data</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_custom">
|
||||||
|
<DisplayString>{ node }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>node</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_data">
|
||||||
|
<DisplayString Condition="node.get != nullptr">{ node }</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="node.get != nullptr">node</ExpandedItem>
|
||||||
|
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_func">
|
||||||
|
<DisplayString Condition="node.invoke != nullptr">{ node }</DisplayString>
|
||||||
|
<DisplayString>{{}}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem Condition="node.invoke != nullptr">node</ExpandedItem>
|
||||||
|
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_type">
|
||||||
|
<DisplayString>{ node }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>node</ExpandedItem>
|
||||||
|
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::meta_ctx">
|
||||||
|
<Intrinsic Name="element_at" Expression="value.packed.first_base::value[pos].element">
|
||||||
|
<Parameter Name="pos" Type="int"/>
|
||||||
|
</Intrinsic>
|
||||||
|
<DisplayString>{ value }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0"/>
|
||||||
|
<Variable Name="last" InitialValue="value.size()"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<Item Name="[{ element_at(pos).first }]">element_at(pos).second</Item>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::basic_poly<*>">
|
||||||
|
<DisplayString>{ storage }</DisplayString>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
</AutoVisualizer>
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::resource<*>">
|
||||||
|
<DisplayString>{ value }</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<ExpandedItem>value</ExpandedItem>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::resource_cache<*>">
|
||||||
|
<Intrinsic Name="size" Expression="pool.first_base::value.size()"/>
|
||||||
|
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<CustomListItems>
|
||||||
|
<Variable Name="pos" InitialValue="0" />
|
||||||
|
<Variable Name="last" InitialValue="size()"/>
|
||||||
|
<Loop>
|
||||||
|
<Break Condition="pos == last"/>
|
||||||
|
<Item Name="[{ pool.first_base::value.packed.first_base::value[pos].element.first }]">
|
||||||
|
*pool.first_base::value.packed.first_base::value[pos].element.second
|
||||||
|
</Item>
|
||||||
|
<Exec>++pos</Exec>
|
||||||
|
</Loop>
|
||||||
|
</CustomListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
+50
@@ -0,0 +1,50 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||||
|
<Type Name="entt::delegate<*>">
|
||||||
|
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[empty]">fn == nullptr</Item>
|
||||||
|
<Item Name="[data]">instance</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::basic_dispatcher<*>">
|
||||||
|
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
|
||||||
|
<DisplayString>{{ size={ size() } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>size()</Size>
|
||||||
|
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::internal::dispatcher_handler<*>">
|
||||||
|
<DisplayString>{{ size={ events.size() }, event={ "$T1" } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[signal]">signal</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::emitter<*>">
|
||||||
|
<DisplayString>{{ size={ handlers.first_base::value.size() } }}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::connection">
|
||||||
|
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::scoped_connection">
|
||||||
|
<DisplayString>{ conn }</DisplayString>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::sigh<*>">
|
||||||
|
<DisplayString>{{ size={ calls.size() }, type={ "$T1" } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<IndexListItems>
|
||||||
|
<Size>calls.size()</Size>
|
||||||
|
<ValueNode>calls[$i]</ValueNode>
|
||||||
|
</IndexListItems>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
<Type Name="entt::sink<*>">
|
||||||
|
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||||
|
<Expand>
|
||||||
|
<Item Name="[signal]">signal,na</Item>
|
||||||
|
</Expand>
|
||||||
|
</Type>
|
||||||
|
</AutoVisualizer>
|
||||||
Vendored
+299
@@ -0,0 +1,299 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# coding=utf-8
|
||||||
|
|
||||||
|
# amalgamate.py - Amalgamate C source and header files.
|
||||||
|
# Copyright (c) 2012, Erik Edlund <erik.edlund@32767.se>
|
||||||
|
#
|
||||||
|
# Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
# are permitted provided that the following conditions are met:
|
||||||
|
#
|
||||||
|
# * Redistributions of source code must retain the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer.
|
||||||
|
#
|
||||||
|
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
# this list of conditions and the following disclaimer in the documentation
|
||||||
|
# and/or other materials provided with the distribution.
|
||||||
|
#
|
||||||
|
# * Neither the name of Erik Edlund, nor the names of its contributors may
|
||||||
|
# be used to endorse or promote products derived from this software without
|
||||||
|
# specific prior written permission.
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||||
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||||
|
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||||
|
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
from __future__ import division
|
||||||
|
from __future__ import print_function
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class Amalgamation(object):
|
||||||
|
|
||||||
|
# Prepends self.source_path to file_path if needed.
|
||||||
|
def actual_path(self, file_path):
|
||||||
|
if not os.path.isabs(file_path):
|
||||||
|
file_path = os.path.join(self.source_path, file_path)
|
||||||
|
return file_path
|
||||||
|
|
||||||
|
# Search included file_path in self.include_paths and
|
||||||
|
# in source_dir if specified.
|
||||||
|
def find_included_file(self, file_path, source_dir):
|
||||||
|
search_dirs = self.include_paths[:]
|
||||||
|
if source_dir:
|
||||||
|
search_dirs.insert(0, source_dir)
|
||||||
|
|
||||||
|
for search_dir in search_dirs:
|
||||||
|
search_path = os.path.join(search_dir, file_path)
|
||||||
|
if os.path.isfile(self.actual_path(search_path)):
|
||||||
|
return search_path
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __init__(self, args):
|
||||||
|
with open(args.config, 'r') as f:
|
||||||
|
config = json.loads(f.read())
|
||||||
|
for key in config:
|
||||||
|
setattr(self, key, config[key])
|
||||||
|
|
||||||
|
self.verbose = args.verbose == "yes"
|
||||||
|
self.prologue = args.prologue
|
||||||
|
self.source_path = args.source_path
|
||||||
|
self.included_files = []
|
||||||
|
|
||||||
|
# Generate the amalgamation and write it to the target file.
|
||||||
|
def generate(self):
|
||||||
|
amalgamation = ""
|
||||||
|
|
||||||
|
if self.prologue:
|
||||||
|
with open(self.prologue, 'r') as f:
|
||||||
|
amalgamation += datetime.datetime.now().strftime(f.read())
|
||||||
|
|
||||||
|
if self.verbose:
|
||||||
|
print("Config:")
|
||||||
|
print(" target = {0}".format(self.target))
|
||||||
|
print(" working_dir = {0}".format(os.getcwd()))
|
||||||
|
print(" include_paths = {0}".format(self.include_paths))
|
||||||
|
print("Creating amalgamation:")
|
||||||
|
for file_path in self.sources:
|
||||||
|
# Do not check the include paths while processing the source
|
||||||
|
# list, all given source paths must be correct.
|
||||||
|
# actual_path = self.actual_path(file_path)
|
||||||
|
print(" - processing \"{0}\"".format(file_path))
|
||||||
|
t = TranslationUnit(file_path, self, True)
|
||||||
|
amalgamation += t.content
|
||||||
|
|
||||||
|
with open(self.target, 'w') as f:
|
||||||
|
f.write(amalgamation)
|
||||||
|
|
||||||
|
print("...done!\n")
|
||||||
|
if self.verbose:
|
||||||
|
print("Files processed: {0}".format(self.sources))
|
||||||
|
print("Files included: {0}".format(self.included_files))
|
||||||
|
print("")
|
||||||
|
|
||||||
|
|
||||||
|
def _is_within(match, matches):
|
||||||
|
for m in matches:
|
||||||
|
if match.start() > m.start() and \
|
||||||
|
match.end() < m.end():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class TranslationUnit(object):
|
||||||
|
# // C++ comment.
|
||||||
|
cpp_comment_pattern = re.compile(r"//.*?\n")
|
||||||
|
|
||||||
|
# /* C comment. */
|
||||||
|
c_comment_pattern = re.compile(r"/\*.*?\*/", re.S)
|
||||||
|
|
||||||
|
# "complex \"stri\\\ng\" value".
|
||||||
|
string_pattern = re.compile("[^']" r'".*?(?<=[^\\])"', re.S)
|
||||||
|
|
||||||
|
# Handle simple include directives. Support for advanced
|
||||||
|
# directives where macros and defines needs to expanded is
|
||||||
|
# not a concern right now.
|
||||||
|
include_pattern = re.compile(
|
||||||
|
r'#\s*include\s+(<|")(?P<path>.*?)("|>)', re.S)
|
||||||
|
|
||||||
|
# #pragma once
|
||||||
|
pragma_once_pattern = re.compile(r'#\s*pragma\s+once', re.S)
|
||||||
|
|
||||||
|
# Search for pattern in self.content, add the match to
|
||||||
|
# contexts if found and update the index accordingly.
|
||||||
|
def _search_content(self, index, pattern, contexts):
|
||||||
|
match = pattern.search(self.content, index)
|
||||||
|
if match:
|
||||||
|
contexts.append(match)
|
||||||
|
return match.end()
|
||||||
|
return index + 2
|
||||||
|
|
||||||
|
# Return all the skippable contexts, i.e., comments and strings
|
||||||
|
def _find_skippable_contexts(self):
|
||||||
|
# Find contexts in the content in which a found include
|
||||||
|
# directive should not be processed.
|
||||||
|
skippable_contexts = []
|
||||||
|
|
||||||
|
# Walk through the content char by char, and try to grab
|
||||||
|
# skippable contexts using regular expressions when found.
|
||||||
|
i = 1
|
||||||
|
content_len = len(self.content)
|
||||||
|
while i < content_len:
|
||||||
|
j = i - 1
|
||||||
|
current = self.content[i]
|
||||||
|
previous = self.content[j]
|
||||||
|
|
||||||
|
if current == '"':
|
||||||
|
# String value.
|
||||||
|
i = self._search_content(j, self.string_pattern,
|
||||||
|
skippable_contexts)
|
||||||
|
elif current == '*' and previous == '/':
|
||||||
|
# C style comment.
|
||||||
|
i = self._search_content(j, self.c_comment_pattern,
|
||||||
|
skippable_contexts)
|
||||||
|
elif current == '/' and previous == '/':
|
||||||
|
# C++ style comment.
|
||||||
|
i = self._search_content(j, self.cpp_comment_pattern,
|
||||||
|
skippable_contexts)
|
||||||
|
else:
|
||||||
|
# Skip to the next char.
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
return skippable_contexts
|
||||||
|
|
||||||
|
# Returns True if the match is within list of other matches
|
||||||
|
|
||||||
|
# Removes pragma once from content
|
||||||
|
def _process_pragma_once(self):
|
||||||
|
content_len = len(self.content)
|
||||||
|
if content_len < len("#include <x>"):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Find contexts in the content in which a found include
|
||||||
|
# directive should not be processed.
|
||||||
|
skippable_contexts = self._find_skippable_contexts()
|
||||||
|
|
||||||
|
pragmas = []
|
||||||
|
pragma_once_match = self.pragma_once_pattern.search(self.content)
|
||||||
|
while pragma_once_match:
|
||||||
|
if not _is_within(pragma_once_match, skippable_contexts):
|
||||||
|
pragmas.append(pragma_once_match)
|
||||||
|
|
||||||
|
pragma_once_match = self.pragma_once_pattern.search(self.content,
|
||||||
|
pragma_once_match.end())
|
||||||
|
|
||||||
|
# Handle all collected pragma once directives.
|
||||||
|
prev_end = 0
|
||||||
|
tmp_content = ''
|
||||||
|
for pragma_match in pragmas:
|
||||||
|
tmp_content += self.content[prev_end:pragma_match.start()]
|
||||||
|
prev_end = pragma_match.end()
|
||||||
|
tmp_content += self.content[prev_end:]
|
||||||
|
self.content = tmp_content
|
||||||
|
|
||||||
|
# Include all trivial #include directives into self.content.
|
||||||
|
def _process_includes(self):
|
||||||
|
content_len = len(self.content)
|
||||||
|
if content_len < len("#include <x>"):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Find contexts in the content in which a found include
|
||||||
|
# directive should not be processed.
|
||||||
|
skippable_contexts = self._find_skippable_contexts()
|
||||||
|
|
||||||
|
# Search for include directives in the content, collect those
|
||||||
|
# which should be included into the content.
|
||||||
|
includes = []
|
||||||
|
include_match = self.include_pattern.search(self.content)
|
||||||
|
while include_match:
|
||||||
|
if not _is_within(include_match, skippable_contexts):
|
||||||
|
include_path = include_match.group("path")
|
||||||
|
search_same_dir = include_match.group(1) == '"'
|
||||||
|
found_included_path = self.amalgamation.find_included_file(
|
||||||
|
include_path, self.file_dir if search_same_dir else None)
|
||||||
|
if found_included_path:
|
||||||
|
includes.append((include_match, found_included_path))
|
||||||
|
|
||||||
|
include_match = self.include_pattern.search(self.content,
|
||||||
|
include_match.end())
|
||||||
|
|
||||||
|
# Handle all collected include directives.
|
||||||
|
prev_end = 0
|
||||||
|
tmp_content = ''
|
||||||
|
for include in includes:
|
||||||
|
include_match, found_included_path = include
|
||||||
|
tmp_content += self.content[prev_end:include_match.start()]
|
||||||
|
tmp_content += "// {0}\n".format(include_match.group(0))
|
||||||
|
if found_included_path not in self.amalgamation.included_files:
|
||||||
|
t = TranslationUnit(found_included_path, self.amalgamation, False)
|
||||||
|
tmp_content += t.content
|
||||||
|
prev_end = include_match.end()
|
||||||
|
tmp_content += self.content[prev_end:]
|
||||||
|
self.content = tmp_content
|
||||||
|
|
||||||
|
return len(includes)
|
||||||
|
|
||||||
|
# Make all content processing
|
||||||
|
def _process(self):
|
||||||
|
if not self.is_root:
|
||||||
|
self._process_pragma_once()
|
||||||
|
self._process_includes()
|
||||||
|
|
||||||
|
def __init__(self, file_path, amalgamation, is_root):
|
||||||
|
self.file_path = file_path
|
||||||
|
self.file_dir = os.path.dirname(file_path)
|
||||||
|
self.amalgamation = amalgamation
|
||||||
|
self.is_root = is_root
|
||||||
|
|
||||||
|
self.amalgamation.included_files.append(self.file_path)
|
||||||
|
|
||||||
|
actual_path = self.amalgamation.actual_path(file_path)
|
||||||
|
if not os.path.isfile(actual_path):
|
||||||
|
raise IOError("File not found: \"{0}\"".format(file_path))
|
||||||
|
with open(actual_path, 'r') as f:
|
||||||
|
self.content = f.read()
|
||||||
|
self._process()
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
description = "Amalgamate C source and header files."
|
||||||
|
usage = " ".join([
|
||||||
|
"amalgamate.py",
|
||||||
|
"[-v]",
|
||||||
|
"-c path/to/config.json",
|
||||||
|
"-s path/to/source/dir",
|
||||||
|
"[-p path/to/prologue.(c|h)]"
|
||||||
|
])
|
||||||
|
argsparser = argparse.ArgumentParser(
|
||||||
|
description=description, usage=usage)
|
||||||
|
|
||||||
|
argsparser.add_argument("-v", "--verbose", dest="verbose",
|
||||||
|
choices=["yes", "no"], metavar="", help="be verbose")
|
||||||
|
|
||||||
|
argsparser.add_argument("-c", "--config", dest="config",
|
||||||
|
required=True, metavar="", help="path to a JSON config file")
|
||||||
|
|
||||||
|
argsparser.add_argument("-s", "--source", dest="source_path",
|
||||||
|
required=True, metavar="", help="source code path")
|
||||||
|
|
||||||
|
argsparser.add_argument("-p", "--prologue", dest="prologue",
|
||||||
|
required=False, metavar="", help="path to a C prologue file")
|
||||||
|
|
||||||
|
amalgamation = Amalgamation(argsparser.parse_args())
|
||||||
|
amalgamation.generate()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Vendored
+8
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"project": "entt",
|
||||||
|
"target": "single_include/entt/entt.hpp",
|
||||||
|
"sources": [
|
||||||
|
"src/entt/entt.hpp"
|
||||||
|
],
|
||||||
|
"include_paths": ["src"]
|
||||||
|
}
|
||||||
+28
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||||
|
VERSION_HEADER=$(realpath "$SCRIPT_DIR/../src/entt/config/version.h" --relative-to=$(pwd))
|
||||||
|
BAZEL_MODULE=$(realpath "$SCRIPT_DIR/../MODULE.bazel" --relative-to=$(pwd))
|
||||||
|
|
||||||
|
if [[ -z "${VERSION_HEADER}" ]]; then
|
||||||
|
echo "Cannot find version header"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Getting version from $VERSION_HEADER ..."
|
||||||
|
|
||||||
|
ENTT_MAJOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MAJOR ([0-9]+)/\1/p' $VERSION_HEADER)
|
||||||
|
ENTT_MINOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MINOR ([0-9]+)/\1/p' $VERSION_HEADER)
|
||||||
|
ENTT_PATCH_VERSION=$(sed -nr 's/#define ENTT_VERSION_PATCH ([0-9]+)/\1/p' $VERSION_HEADER)
|
||||||
|
|
||||||
|
VERSION="$ENTT_MAJOR_VERSION.$ENTT_MINOR_VERSION.$ENTT_PATCH_VERSION"
|
||||||
|
|
||||||
|
echo "Found $VERSION"
|
||||||
|
|
||||||
|
buildozer "set version $VERSION" //MODULE.bazel:%module
|
||||||
|
|
||||||
|
# a commit is needed for 'git archive'
|
||||||
|
git add $BAZEL_MODULE
|
||||||
|
git commit -m "chore: update MODULE.bazel version to $VERSION"
|
||||||
+60
@@ -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="$(echo "$URL" | sed -e 's/[\/&]/\\&/g')"
|
||||||
|
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 -rf "$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 ..
|
||||||
+93963
File diff suppressed because it is too large
Load Diff
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||||
|
load("//bazel:copts.bzl", "COPTS")
|
||||||
|
|
||||||
|
package(default_visibility = ["//:__subpackages__"])
|
||||||
|
|
||||||
|
cc_library(
|
||||||
|
name = "entt",
|
||||||
|
includes = ["."],
|
||||||
|
hdrs = glob(["**/*.h", "**/*.hpp"]),
|
||||||
|
copts = COPTS,
|
||||||
|
)
|
||||||
+107
@@ -0,0 +1,107 @@
|
|||||||
|
#ifndef ENTT_CONFIG_CONFIG_H
|
||||||
|
#define ENTT_CONFIG_CONFIG_H
|
||||||
|
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||||
|
|
||||||
|
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
|
||||||
|
# define ENTT_CONSTEXPR
|
||||||
|
# define ENTT_THROW throw
|
||||||
|
# define ENTT_TRY try
|
||||||
|
# define ENTT_CATCH catch(...)
|
||||||
|
#else
|
||||||
|
# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20)
|
||||||
|
# define ENTT_THROW
|
||||||
|
# define ENTT_TRY if(true)
|
||||||
|
# define ENTT_CATCH if(false)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __has_include(<version>)
|
||||||
|
# include <version>
|
||||||
|
#
|
||||||
|
# if defined(__cpp_consteval)
|
||||||
|
# define ENTT_CONSTEVAL consteval
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENTT_CONSTEVAL
|
||||||
|
# define ENTT_CONSTEVAL constexpr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENTT_USE_ATOMIC
|
||||||
|
# include <atomic>
|
||||||
|
# define ENTT_MAYBE_ATOMIC(Type) std::atomic<Type>
|
||||||
|
#else
|
||||||
|
# define ENTT_MAYBE_ATOMIC(Type) Type
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENTT_ID_TYPE
|
||||||
|
# include <cstdint>
|
||||||
|
# define ENTT_ID_TYPE std::uint32_t
|
||||||
|
#else
|
||||||
|
# include <cstdint> // provides coverage for types in the std namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENTT_SPARSE_PAGE
|
||||||
|
# define ENTT_SPARSE_PAGE 4096
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENTT_PACKED_PAGE
|
||||||
|
# define ENTT_PACKED_PAGE 1024
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENTT_DISABLE_ASSERT
|
||||||
|
# undef ENTT_ASSERT
|
||||||
|
# define ENTT_ASSERT(condition, msg) (void(0))
|
||||||
|
#elif !defined ENTT_ASSERT
|
||||||
|
# include <cassert>
|
||||||
|
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENTT_DISABLE_ASSERT
|
||||||
|
# undef ENTT_ASSERT_CONSTEXPR
|
||||||
|
# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0))
|
||||||
|
#elif !defined ENTT_ASSERT_CONSTEXPR
|
||||||
|
# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg);
|
||||||
|
|
||||||
|
#ifdef ENTT_NO_ETO
|
||||||
|
# define ENTT_ETO_TYPE(Type) void
|
||||||
|
#else
|
||||||
|
# define ENTT_ETO_TYPE(Type) Type
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENTT_NO_MIXIN
|
||||||
|
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
|
||||||
|
#else
|
||||||
|
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENTT_STANDARD_CPP
|
||||||
|
# define ENTT_NONSTD false
|
||||||
|
#else
|
||||||
|
# define ENTT_NONSTD true
|
||||||
|
# if defined __clang__ || defined __GNUC__
|
||||||
|
# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__
|
||||||
|
# define ENTT_PRETTY_FUNCTION_PREFIX '='
|
||||||
|
# define ENTT_PRETTY_FUNCTION_SUFFIX ']'
|
||||||
|
# elif defined _MSC_VER
|
||||||
|
# define ENTT_PRETTY_FUNCTION __FUNCSIG__
|
||||||
|
# define ENTT_PRETTY_FUNCTION_PREFIX '<'
|
||||||
|
# define ENTT_PRETTY_FUNCTION_SUFFIX '>'
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined _MSC_VER
|
||||||
|
# pragma detect_mismatch("entt.version", ENTT_VERSION)
|
||||||
|
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
|
||||||
|
# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE))
|
||||||
|
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||||
|
|
||||||
|
#endif
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef ENTT_CONFIG_MACRO_H
|
||||||
|
#define ENTT_CONFIG_MACRO_H
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||||
|
|
||||||
|
#define ENTT_STR(arg) #arg
|
||||||
|
#define ENTT_XSTR(arg) ENTT_STR(arg)
|
||||||
|
|
||||||
|
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||||
|
|
||||||
|
#endif
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef ENTT_CONFIG_VERSION_H
|
||||||
|
#define ENTT_CONFIG_VERSION_H
|
||||||
|
|
||||||
|
#include "macro.h"
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||||
|
|
||||||
|
#define ENTT_VERSION_MAJOR 3
|
||||||
|
#define ENTT_VERSION_MINOR 15
|
||||||
|
#define ENTT_VERSION_PATCH 0
|
||||||
|
|
||||||
|
#define ENTT_VERSION \
|
||||||
|
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||||
|
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
|
||||||
|
|
||||||
|
// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||||
|
|
||||||
|
#endif
|
||||||
+1047
File diff suppressed because it is too large
Load Diff
+929
@@ -0,0 +1,929 @@
|
|||||||
|
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
|
||||||
|
#define ENTT_CONTAINER_DENSE_SET_HPP
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <memory>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include "../config/config.h"
|
||||||
|
#include "../core/bit.hpp"
|
||||||
|
#include "../core/compressed_pair.hpp"
|
||||||
|
#include "../core/type_traits.hpp"
|
||||||
|
#include "fwd.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/*! @cond TURN_OFF_DOXYGEN */
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
class dense_set_iterator final {
|
||||||
|
template<typename>
|
||||||
|
friend class dense_set_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = typename It::value_type::second_type;
|
||||||
|
using pointer = const value_type *;
|
||||||
|
using reference = const value_type &;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
|
||||||
|
constexpr dense_set_iterator() noexcept
|
||||||
|
: it{} {}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator(const It iter) noexcept
|
||||||
|
: it{iter} {}
|
||||||
|
|
||||||
|
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
|
||||||
|
constexpr dense_set_iterator(const dense_set_iterator<Other> &other) noexcept
|
||||||
|
: it{other.it} {}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator &operator++() noexcept {
|
||||||
|
return ++it, *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator operator++(int) noexcept {
|
||||||
|
const dense_set_iterator orig = *this;
|
||||||
|
return ++(*this), orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator &operator--() noexcept {
|
||||||
|
return --it, *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator operator--(int) noexcept {
|
||||||
|
const dense_set_iterator orig = *this;
|
||||||
|
return operator--(), orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
|
||||||
|
it += value;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
|
||||||
|
dense_set_iterator copy = *this;
|
||||||
|
return (copy += value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
|
||||||
|
return (*this += -value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
|
||||||
|
return (*this + -value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||||
|
return it[value].second;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||||
|
return std::addressof(operator[](0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||||
|
return operator[](0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
It it;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return lhs.it - rhs.it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return lhs.it == rhs.it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return lhs.it < rhs.it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return rhs < lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return !(lhs > rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return !(lhs < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
class dense_set_local_iterator final {
|
||||||
|
template<typename>
|
||||||
|
friend class dense_set_local_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = typename It::value_type::second_type;
|
||||||
|
using pointer = const value_type *;
|
||||||
|
using reference = const value_type &;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::forward_iterator_tag;
|
||||||
|
|
||||||
|
constexpr dense_set_local_iterator() noexcept
|
||||||
|
: it{},
|
||||||
|
offset{} {}
|
||||||
|
|
||||||
|
constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
|
||||||
|
: it{iter},
|
||||||
|
offset{pos} {}
|
||||||
|
|
||||||
|
template<typename Other, typename = std::enable_if_t<!std::is_same_v<It, Other> && std::is_constructible_v<It, Other>>>
|
||||||
|
constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
|
||||||
|
: it{other.it},
|
||||||
|
offset{other.offset} {}
|
||||||
|
|
||||||
|
constexpr dense_set_local_iterator &operator++() noexcept {
|
||||||
|
return offset = it[static_cast<typename It::difference_type>(offset)].first, *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr dense_set_local_iterator operator++(int) noexcept {
|
||||||
|
const dense_set_local_iterator orig = *this;
|
||||||
|
return ++(*this), orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||||
|
return std::addressof(it[static_cast<typename It::difference_type>(offset)].second);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||||
|
return *operator->();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::size_t index() const noexcept {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
It it;
|
||||||
|
std::size_t offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return lhs.index() == rhs.index();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Lhs, typename Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
/*! @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Associative container for unique objects of a given type.
|
||||||
|
*
|
||||||
|
* Internally, elements are organized into buckets. Which bucket an element is
|
||||||
|
* placed into depends entirely on its hash. Elements with the same hash code
|
||||||
|
* appear in the same bucket.
|
||||||
|
*
|
||||||
|
* @tparam Type Value type of the associative container.
|
||||||
|
* @tparam Hash Type of function to use to hash the values.
|
||||||
|
* @tparam KeyEqual Type of function to use to compare the values for equality.
|
||||||
|
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
||||||
|
class dense_set {
|
||||||
|
static constexpr float default_threshold = 0.875f;
|
||||||
|
static constexpr std::size_t minimum_capacity = 8u;
|
||||||
|
|
||||||
|
using node_type = std::pair<std::size_t, Type>;
|
||||||
|
using alloc_traits = std::allocator_traits<Allocator>;
|
||||||
|
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
|
||||||
|
using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>;
|
||||||
|
using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
|
||||||
|
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept {
|
||||||
|
return fast_mod(static_cast<size_type>(sparse.second()(value)), bucket_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) {
|
||||||
|
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
|
||||||
|
if(packed.second()(*it, value)) {
|
||||||
|
return begin() + static_cast<difference_type>(it.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const {
|
||||||
|
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||||
|
if(packed.second()(*it, value)) {
|
||||||
|
return cbegin() + static_cast<difference_type>(it.index());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
|
||||||
|
const auto index = value_to_bucket(value);
|
||||||
|
|
||||||
|
if(auto it = constrained_find(value, index); it != end()) {
|
||||||
|
return std::make_pair(it, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
packed.first().emplace_back(sparse.first()[index], std::forward<Other>(value));
|
||||||
|
sparse.first()[index] = packed.first().size() - 1u;
|
||||||
|
rehash_if_required();
|
||||||
|
|
||||||
|
return std::make_pair(--end(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_and_pop(const std::size_t pos) {
|
||||||
|
if(const auto last = size() - 1u; pos != last) {
|
||||||
|
size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
|
||||||
|
packed.first()[pos] = std::move(packed.first().back());
|
||||||
|
for(; *curr != last; curr = &packed.first()[*curr].first) {}
|
||||||
|
*curr = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
packed.first().pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
void rehash_if_required() {
|
||||||
|
if(const auto bc = bucket_count(); size() > static_cast<size_type>(static_cast<float>(bc) * max_load_factor())) {
|
||||||
|
rehash(bc * 2u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Allocator type. */
|
||||||
|
using allocator_type = Allocator;
|
||||||
|
/*! @brief Key type of the container. */
|
||||||
|
using key_type = Type;
|
||||||
|
/*! @brief Value type of the container. */
|
||||||
|
using value_type = Type;
|
||||||
|
/*! @brief Unsigned integer type. */
|
||||||
|
using size_type = std::size_t;
|
||||||
|
/*! @brief Signed integer type. */
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
/*! @brief Type of function to use to hash the elements. */
|
||||||
|
using hasher = Hash;
|
||||||
|
/*! @brief Type of function to use to compare the elements for equality. */
|
||||||
|
using key_equal = KeyEqual;
|
||||||
|
/*! @brief Random access iterator type. */
|
||||||
|
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
|
||||||
|
/*! @brief Constant random access iterator type. */
|
||||||
|
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
|
||||||
|
/*! @brief Reverse iterator type. */
|
||||||
|
using reverse_iterator = std::reverse_iterator<iterator>;
|
||||||
|
/*! @brief Constant reverse iterator type. */
|
||||||
|
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
|
||||||
|
/*! @brief Forward iterator type. */
|
||||||
|
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
|
||||||
|
/*! @brief Constant forward iterator type. */
|
||||||
|
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_iterator>;
|
||||||
|
|
||||||
|
/*! @brief Default constructor. */
|
||||||
|
dense_set()
|
||||||
|
: dense_set{minimum_capacity} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs an empty container with a given allocator.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
explicit dense_set(const allocator_type &allocator)
|
||||||
|
: dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs an empty container with a given allocator and user
|
||||||
|
* supplied minimal number of buckets.
|
||||||
|
* @param cnt Minimal number of buckets.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
dense_set(const size_type cnt, const allocator_type &allocator)
|
||||||
|
: dense_set{cnt, hasher{}, key_equal{}, allocator} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs an empty container with a given allocator, hash
|
||||||
|
* function and user supplied minimal number of buckets.
|
||||||
|
* @param cnt Minimal number of buckets.
|
||||||
|
* @param hash Hash function to use.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator)
|
||||||
|
: dense_set{cnt, hash, key_equal{}, allocator} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs an empty container with a given allocator, hash
|
||||||
|
* function, compare function and user supplied minimal number of buckets.
|
||||||
|
* @param cnt Minimal number of buckets.
|
||||||
|
* @param hash Hash function to use.
|
||||||
|
* @param equal Compare function to use.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||||
|
: sparse{allocator, hash},
|
||||||
|
packed{allocator, equal} {
|
||||||
|
rehash(cnt);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Default copy constructor. */
|
||||||
|
dense_set(const dense_set &) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocator-extended copy constructor.
|
||||||
|
* @param other The instance to copy from.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
dense_set(const dense_set &other, const allocator_type &allocator)
|
||||||
|
: sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())},
|
||||||
|
packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())},
|
||||||
|
threshold{other.threshold} {}
|
||||||
|
|
||||||
|
/*! @brief Default move constructor. */
|
||||||
|
dense_set(dense_set &&) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocator-extended move constructor.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
dense_set(dense_set &&other, const allocator_type &allocator)
|
||||||
|
: sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))},
|
||||||
|
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
|
||||||
|
threshold{other.threshold} {}
|
||||||
|
|
||||||
|
/*! @brief Default destructor. */
|
||||||
|
~dense_set() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default copy assignment operator.
|
||||||
|
* @return This container.
|
||||||
|
*/
|
||||||
|
dense_set &operator=(const dense_set &) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default move assignment operator.
|
||||||
|
* @return This container.
|
||||||
|
*/
|
||||||
|
dense_set &operator=(dense_set &&) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exchanges the contents with those of a given container.
|
||||||
|
* @param other Container to exchange the content with.
|
||||||
|
*/
|
||||||
|
void swap(dense_set &other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
swap(sparse, other.sparse);
|
||||||
|
swap(packed, other.packed);
|
||||||
|
swap(threshold, other.threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the associated allocator.
|
||||||
|
* @return The associated allocator.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||||
|
return sparse.first().get_allocator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning.
|
||||||
|
*
|
||||||
|
* If the array is empty, the returned iterator will be equal to `end()`.
|
||||||
|
*
|
||||||
|
* @return An iterator to the first instance of the internal array.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_iterator cbegin() const noexcept {
|
||||||
|
return packed.first().begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc cbegin */
|
||||||
|
[[nodiscard]] const_iterator begin() const noexcept {
|
||||||
|
return cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc begin */
|
||||||
|
[[nodiscard]] iterator begin() noexcept {
|
||||||
|
return packed.first().begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end.
|
||||||
|
* @return An iterator to the element following the last instance of the
|
||||||
|
* internal array.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_iterator cend() const noexcept {
|
||||||
|
return packed.first().end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc cend */
|
||||||
|
[[nodiscard]] const_iterator end() const noexcept {
|
||||||
|
return cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc end */
|
||||||
|
[[nodiscard]] iterator end() noexcept {
|
||||||
|
return packed.first().end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a reverse iterator to the beginning.
|
||||||
|
*
|
||||||
|
* If the array is empty, the returned iterator will be equal to `rend()`.
|
||||||
|
*
|
||||||
|
* @return An iterator to the first instance of the reversed internal array.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
|
||||||
|
return std::make_reverse_iterator(cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc crbegin */
|
||||||
|
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||||
|
return crbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc rbegin */
|
||||||
|
[[nodiscard]] reverse_iterator rbegin() noexcept {
|
||||||
|
return std::make_reverse_iterator(end());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a reverse iterator to the end.
|
||||||
|
* @return An iterator to the element following the last instance of the
|
||||||
|
* reversed internal array.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_reverse_iterator crend() const noexcept {
|
||||||
|
return std::make_reverse_iterator(cbegin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc crend */
|
||||||
|
[[nodiscard]] const_reverse_iterator rend() const noexcept {
|
||||||
|
return crend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc rend */
|
||||||
|
[[nodiscard]] reverse_iterator rend() noexcept {
|
||||||
|
return std::make_reverse_iterator(begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether a container is empty.
|
||||||
|
* @return True if the container is empty, false otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool empty() const noexcept {
|
||||||
|
return packed.first().empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of elements in a container.
|
||||||
|
* @return Number of elements in a container.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type size() const noexcept {
|
||||||
|
return packed.first().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the maximum possible number of elements.
|
||||||
|
* @return Maximum possible number of elements.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type max_size() const noexcept {
|
||||||
|
return packed.first().max_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Clears the container. */
|
||||||
|
void clear() noexcept {
|
||||||
|
sparse.first().clear();
|
||||||
|
packed.first().clear();
|
||||||
|
rehash(0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inserts an element into the container, if it does not exist.
|
||||||
|
* @param value An element to insert into the container.
|
||||||
|
* @return A pair consisting of an iterator to the inserted element (or to
|
||||||
|
* the element that prevented the insertion) and a bool denoting whether the
|
||||||
|
* insertion took place.
|
||||||
|
*/
|
||||||
|
std::pair<iterator, bool> insert(const value_type &value) {
|
||||||
|
return insert_or_do_nothing(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc insert */
|
||||||
|
std::pair<iterator, bool> insert(value_type &&value) {
|
||||||
|
return insert_or_do_nothing(std::move(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Inserts elements into the container, if they do not exist.
|
||||||
|
* @tparam It Type of input iterator.
|
||||||
|
* @param first An iterator to the first element of the range of elements.
|
||||||
|
* @param last An iterator past the last element of the range of elements.
|
||||||
|
*/
|
||||||
|
template<typename It>
|
||||||
|
void insert(It first, It last) {
|
||||||
|
for(; first != last; ++first) {
|
||||||
|
insert(*first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs an element in-place, if it does not exist.
|
||||||
|
*
|
||||||
|
* The element is also constructed when the container already has the key,
|
||||||
|
* in which case the newly constructed object is destroyed immediately.
|
||||||
|
*
|
||||||
|
* @tparam Args Types of arguments to forward to the constructor of the
|
||||||
|
* element.
|
||||||
|
* @param args Arguments to forward to the constructor of the element.
|
||||||
|
* @return A pair consisting of an iterator to the inserted element (or to
|
||||||
|
* the element that prevented the insertion) and a bool denoting whether the
|
||||||
|
* insertion took place.
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
std::pair<iterator, bool> emplace(Args &&...args) {
|
||||||
|
if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::decay_t<Args>, value_type>)) {
|
||||||
|
return insert_or_do_nothing(std::forward<Args>(args)...);
|
||||||
|
} else {
|
||||||
|
auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward<Args>(args)...));
|
||||||
|
const auto index = value_to_bucket(node.second);
|
||||||
|
|
||||||
|
if(auto it = constrained_find(node.second, index); it != end()) {
|
||||||
|
packed.first().pop_back();
|
||||||
|
return std::make_pair(it, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::swap(node.first, sparse.first()[index]);
|
||||||
|
rehash_if_required();
|
||||||
|
|
||||||
|
return std::make_pair(--end(), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes an element from a given position.
|
||||||
|
* @param pos An iterator to the element to remove.
|
||||||
|
* @return An iterator following the removed element.
|
||||||
|
*/
|
||||||
|
iterator erase(const_iterator pos) {
|
||||||
|
const auto diff = pos - cbegin();
|
||||||
|
erase(*pos);
|
||||||
|
return begin() + diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes the given elements from a container.
|
||||||
|
* @param first An iterator to the first element of the range of elements.
|
||||||
|
* @param last An iterator past the last element of the range of elements.
|
||||||
|
* @return An iterator following the last removed element.
|
||||||
|
*/
|
||||||
|
iterator erase(const_iterator first, const_iterator last) {
|
||||||
|
const auto dist = first - cbegin();
|
||||||
|
|
||||||
|
for(auto from = last - cbegin(); from != dist; --from) {
|
||||||
|
erase(packed.first()[static_cast<size_type>(from) - 1u].second);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (begin() + dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes the element associated with a given value.
|
||||||
|
* @param value Value of an element to remove.
|
||||||
|
* @return Number of elements removed (either 0 or 1).
|
||||||
|
*/
|
||||||
|
size_type erase(const value_type &value) {
|
||||||
|
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
|
||||||
|
if(packed.second()(packed.first()[*curr].second, value)) {
|
||||||
|
const auto index = *curr;
|
||||||
|
*curr = packed.first()[*curr].first;
|
||||||
|
move_and_pop(index);
|
||||||
|
return 1u;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of elements matching a value (either 1 or 0).
|
||||||
|
* @param key Key value of an element to search for.
|
||||||
|
* @return Number of elements matching the key (either 1 or 0).
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type count(const value_type &key) const {
|
||||||
|
return find(key) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of elements matching a key (either 1 or 0).
|
||||||
|
* @tparam Other Type of the key value of an element to search for.
|
||||||
|
* @param key Key value of an element to search for.
|
||||||
|
* @return Number of elements matching the key (either 1 or 0).
|
||||||
|
*/
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, size_type>>
|
||||||
|
count(const Other &key) const {
|
||||||
|
return find(key) != end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds an element with a given value.
|
||||||
|
* @param value Value of an element to search for.
|
||||||
|
* @return An iterator to an element with the given value. If no such
|
||||||
|
* element is found, a past-the-end iterator is returned.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] iterator find(const value_type &value) {
|
||||||
|
return constrained_find(value, value_to_bucket(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc find */
|
||||||
|
[[nodiscard]] const_iterator find(const value_type &value) const {
|
||||||
|
return constrained_find(value, value_to_bucket(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Finds an element that compares _equivalent_ to a given value.
|
||||||
|
* @tparam Other Type of an element to search for.
|
||||||
|
* @param value Value of an element to search for.
|
||||||
|
* @return An iterator to an element with the given value. If no such
|
||||||
|
* element is found, a past-the-end iterator is returned.
|
||||||
|
*/
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, iterator>>
|
||||||
|
find(const Other &value) {
|
||||||
|
return constrained_find(value, value_to_bucket(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc find */
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, const_iterator>>
|
||||||
|
find(const Other &value) const {
|
||||||
|
return constrained_find(value, value_to_bucket(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a range containing all elements with a given value.
|
||||||
|
* @param value Value of an element to search for.
|
||||||
|
* @return A pair of iterators pointing to the first element and past the
|
||||||
|
* last element of the range.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::pair<iterator, iterator> equal_range(const value_type &value) {
|
||||||
|
const auto it = find(value);
|
||||||
|
return {it, it + !(it == end())};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc equal_range */
|
||||||
|
[[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const value_type &value) const {
|
||||||
|
const auto it = find(value);
|
||||||
|
return {it, it + !(it == cend())};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a range containing all elements that compare _equivalent_
|
||||||
|
* to a given value.
|
||||||
|
* @tparam Other Type of an element to search for.
|
||||||
|
* @param value Value of an element to search for.
|
||||||
|
* @return A pair of iterators pointing to the first element and past the
|
||||||
|
* last element of the range.
|
||||||
|
*/
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<iterator, iterator>>>
|
||||||
|
equal_range(const Other &value) {
|
||||||
|
const auto it = find(value);
|
||||||
|
return {it, it + !(it == end())};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc equal_range */
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>>
|
||||||
|
equal_range(const Other &value) const {
|
||||||
|
const auto it = find(value);
|
||||||
|
return {it, it + !(it == cend())};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the container contains an element with a given value.
|
||||||
|
* @param value Value of an element to search for.
|
||||||
|
* @return True if there is such an element, false otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool contains(const value_type &value) const {
|
||||||
|
return (find(value) != cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if the container contains an element that compares
|
||||||
|
* _equivalent_ to a given value.
|
||||||
|
* @tparam Other Type of an element to search for.
|
||||||
|
* @param value Value of an element to search for.
|
||||||
|
* @return True if there is such an element, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Other>
|
||||||
|
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, bool>>
|
||||||
|
contains(const Other &value) const {
|
||||||
|
return (find(value) != cend());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning of a given bucket.
|
||||||
|
* @param index An index of a bucket to access.
|
||||||
|
* @return An iterator to the beginning of the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_local_iterator cbegin(const size_type index) const {
|
||||||
|
return {packed.first().begin(), sparse.first()[index]};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning of a given bucket.
|
||||||
|
* @param index An index of a bucket to access.
|
||||||
|
* @return An iterator to the beginning of the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_local_iterator begin(const size_type index) const {
|
||||||
|
return cbegin(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning of a given bucket.
|
||||||
|
* @param index An index of a bucket to access.
|
||||||
|
* @return An iterator to the beginning of the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] local_iterator begin(const size_type index) {
|
||||||
|
return {packed.first().begin(), sparse.first()[index]};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end of a given bucket.
|
||||||
|
* @param index An index of a bucket to access.
|
||||||
|
* @return An iterator to the end of the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||||
|
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end of a given bucket.
|
||||||
|
* @param index An index of a bucket to access.
|
||||||
|
* @return An iterator to the end of the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_local_iterator end(const size_type index) const {
|
||||||
|
return cend(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end of a given bucket.
|
||||||
|
* @param index An index of a bucket to access.
|
||||||
|
* @return An iterator to the end of the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||||
|
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of buckets.
|
||||||
|
* @return The number of buckets.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type bucket_count() const {
|
||||||
|
return sparse.first().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the maximum number of buckets.
|
||||||
|
* @return The maximum number of buckets.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type max_bucket_count() const {
|
||||||
|
return sparse.first().max_size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of elements in a given bucket.
|
||||||
|
* @param index The index of the bucket to examine.
|
||||||
|
* @return The number of elements in the given bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type bucket_size(const size_type index) const {
|
||||||
|
return static_cast<size_type>(std::distance(begin(index), end(index)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the bucket for a given element.
|
||||||
|
* @param value The value of the element to examine.
|
||||||
|
* @return The bucket for the given element.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type bucket(const value_type &value) const {
|
||||||
|
return value_to_bucket(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the average number of elements per bucket.
|
||||||
|
* @return The average number of elements per bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] float load_factor() const {
|
||||||
|
return static_cast<float>(size()) / static_cast<float>(bucket_count());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the maximum average number of elements per bucket.
|
||||||
|
* @return The maximum average number of elements per bucket.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] float max_load_factor() const {
|
||||||
|
return threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the desired maximum average number of elements per bucket.
|
||||||
|
* @param value A desired maximum average number of elements per bucket.
|
||||||
|
*/
|
||||||
|
void max_load_factor(const float value) {
|
||||||
|
ENTT_ASSERT(value > 0.f, "Invalid load factor");
|
||||||
|
threshold = value;
|
||||||
|
rehash(0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reserves at least the specified number of buckets and regenerates
|
||||||
|
* the hash table.
|
||||||
|
* @param cnt New number of buckets.
|
||||||
|
*/
|
||||||
|
void rehash(const size_type cnt) {
|
||||||
|
auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
|
||||||
|
const auto cap = static_cast<size_type>(static_cast<float>(size()) / max_load_factor());
|
||||||
|
value = value > cap ? value : cap;
|
||||||
|
|
||||||
|
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
|
||||||
|
sparse.first().resize(sz);
|
||||||
|
|
||||||
|
for(auto &&elem: sparse.first()) {
|
||||||
|
elem = (std::numeric_limits<size_type>::max)();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||||
|
const auto index = value_to_bucket(packed.first()[pos].second);
|
||||||
|
packed.first()[pos].first = std::exchange(sparse.first()[index], pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reserves space for at least the specified number of elements and
|
||||||
|
* regenerates the hash table.
|
||||||
|
* @param cnt New number of elements.
|
||||||
|
*/
|
||||||
|
void reserve(const size_type cnt) {
|
||||||
|
packed.first().reserve(cnt);
|
||||||
|
rehash(static_cast<size_type>(std::ceil(static_cast<float>(cnt) / max_load_factor())));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the function used to hash the elements.
|
||||||
|
* @return The function used to hash the elements.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] hasher hash_function() const {
|
||||||
|
return sparse.second();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the function used to compare elements for equality.
|
||||||
|
* @return The function used to compare elements for equality.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] key_equal key_eq() const {
|
||||||
|
return packed.second();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
compressed_pair<sparse_container_type, hasher> sparse;
|
||||||
|
compressed_pair<packed_container_type, key_equal> packed;
|
||||||
|
float threshold{default_threshold};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+38
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef ENTT_CONTAINER_FWD_HPP
|
||||||
|
#define ENTT_CONTAINER_FWD_HPP
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename Key,
|
||||||
|
typename Type,
|
||||||
|
typename = std::hash<Key>,
|
||||||
|
typename = std::equal_to<>,
|
||||||
|
typename = std::allocator<std::pair<const Key, Type>>>
|
||||||
|
class dense_map;
|
||||||
|
|
||||||
|
template<
|
||||||
|
typename Type,
|
||||||
|
typename = std::hash<Type>,
|
||||||
|
typename = std::equal_to<>,
|
||||||
|
typename = std::allocator<Type>>
|
||||||
|
class dense_set;
|
||||||
|
|
||||||
|
template<typename...>
|
||||||
|
class basic_table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Alias declaration for the most common use case.
|
||||||
|
* @tparam Type Element types.
|
||||||
|
*/
|
||||||
|
template<typename... Type>
|
||||||
|
using table = basic_table<std::vector<Type>...>;
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+462
@@ -0,0 +1,462 @@
|
|||||||
|
#ifndef ENTT_CONTAINER_TABLE_HPP
|
||||||
|
#define ENTT_CONTAINER_TABLE_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include "../config/config.h"
|
||||||
|
#include "../core/iterator.hpp"
|
||||||
|
#include "fwd.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/*! @cond TURN_OFF_DOXYGEN */
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template<typename... It>
|
||||||
|
class table_iterator {
|
||||||
|
template<typename...>
|
||||||
|
friend class table_iterator;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...));
|
||||||
|
using pointer = input_iterator_pointer<value_type>;
|
||||||
|
using reference = value_type;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using iterator_concept = std::random_access_iterator_tag;
|
||||||
|
|
||||||
|
constexpr table_iterator() noexcept
|
||||||
|
: it{} {}
|
||||||
|
|
||||||
|
constexpr table_iterator(It... from) noexcept
|
||||||
|
: it{from...} {}
|
||||||
|
|
||||||
|
template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
|
||||||
|
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
|
||||||
|
: table_iterator{std::get<Other>(other.it)...} {}
|
||||||
|
|
||||||
|
constexpr table_iterator &operator++() noexcept {
|
||||||
|
return (++std::get<It>(it), ...), *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator operator++(int) noexcept {
|
||||||
|
const table_iterator orig = *this;
|
||||||
|
return ++(*this), orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator &operator--() noexcept {
|
||||||
|
return (--std::get<It>(it), ...), *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator operator--(int) noexcept {
|
||||||
|
const table_iterator orig = *this;
|
||||||
|
return operator--(), orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator &operator+=(const difference_type value) noexcept {
|
||||||
|
return ((std::get<It>(it) += value), ...), *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator operator+(const difference_type value) const noexcept {
|
||||||
|
table_iterator copy = *this;
|
||||||
|
return (copy += value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator &operator-=(const difference_type value) noexcept {
|
||||||
|
return (*this += -value);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr table_iterator operator-(const difference_type value) const noexcept {
|
||||||
|
return (*this + -value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||||
|
return std::forward_as_tuple(std::get<It>(it)[value]...);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||||
|
return {operator[](0)};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||||
|
return operator[](0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::tuple<It...> it;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return std::get<0>(lhs.it) - std::get<0>(rhs.it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return std::get<0>(lhs.it) < std::get<0>(rhs.it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return rhs < lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return !(lhs > rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Lhs, typename... Rhs>
|
||||||
|
[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
|
||||||
|
return !(lhs < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
/*! @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Basic table implementation.
|
||||||
|
*
|
||||||
|
* Internal data structures arrange elements to maximize performance. There are
|
||||||
|
* no guarantees that objects are returned in the insertion order when iterate
|
||||||
|
* a table. Do not make assumption on the order in any case.
|
||||||
|
*
|
||||||
|
* @tparam Container Sequence container row types.
|
||||||
|
*/
|
||||||
|
template<typename... Container>
|
||||||
|
class basic_table {
|
||||||
|
using container_type = std::tuple<Container...>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Unsigned integer type. */
|
||||||
|
using size_type = std::size_t;
|
||||||
|
/*! @brief Signed integer type. */
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
/*! @brief Input iterator type. */
|
||||||
|
using iterator = internal::table_iterator<typename Container::iterator...>;
|
||||||
|
/*! @brief Constant input iterator type. */
|
||||||
|
using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
|
||||||
|
/*! @brief Reverse iterator type. */
|
||||||
|
using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
|
||||||
|
/*! @brief Constant reverse iterator type. */
|
||||||
|
using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>;
|
||||||
|
|
||||||
|
/*! @brief Default constructor. */
|
||||||
|
basic_table()
|
||||||
|
: payload{} {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructs the underlying containers.
|
||||||
|
* @param container The containers to copy from.
|
||||||
|
*/
|
||||||
|
explicit basic_table(const Container &...container) noexcept
|
||||||
|
: payload{container...} {
|
||||||
|
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructs the underlying containers.
|
||||||
|
* @param container The containers to move from.
|
||||||
|
*/
|
||||||
|
explicit basic_table(Container &&...container) noexcept
|
||||||
|
: payload{std::move(container)...} {
|
||||||
|
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Default copy constructor, deleted on purpose. */
|
||||||
|
basic_table(const basic_table &) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
*/
|
||||||
|
basic_table(basic_table &&other) noexcept
|
||||||
|
: payload{std::move(other.payload)} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs the underlying containers using a given allocator.
|
||||||
|
* @tparam Allocator Type of allocator.
|
||||||
|
* @param allocator A valid allocator.
|
||||||
|
*/
|
||||||
|
template<typename Allocator>
|
||||||
|
explicit basic_table(const Allocator &allocator)
|
||||||
|
: payload{Container{allocator}...} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructs the underlying containers using a given allocator.
|
||||||
|
* @tparam Allocator Type of allocator.
|
||||||
|
* @param container The containers to copy from.
|
||||||
|
* @param allocator A valid allocator.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
basic_table(const Container &...container, const Allocator &allocator) noexcept
|
||||||
|
: payload{Container{container, allocator}...} {
|
||||||
|
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructs the underlying containers using a given allocator.
|
||||||
|
* @tparam Allocator Type of allocator.
|
||||||
|
* @param container The containers to move from.
|
||||||
|
* @param allocator A valid allocator.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
basic_table(Container &&...container, const Allocator &allocator) noexcept
|
||||||
|
: payload{Container{std::move(container), allocator}...} {
|
||||||
|
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Allocator-extended move constructor.
|
||||||
|
* @tparam Allocator Type of allocator.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
* @param allocator The allocator to use.
|
||||||
|
*/
|
||||||
|
template<class Allocator>
|
||||||
|
basic_table(basic_table &&other, const Allocator &allocator)
|
||||||
|
: payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {}
|
||||||
|
|
||||||
|
/*! @brief Default destructor. */
|
||||||
|
~basic_table() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default copy assignment operator, deleted on purpose.
|
||||||
|
* @return This container.
|
||||||
|
*/
|
||||||
|
basic_table &operator=(const basic_table &) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move assignment operator.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
* @return This container.
|
||||||
|
*/
|
||||||
|
basic_table &operator=(basic_table &&other) noexcept {
|
||||||
|
swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Exchanges the contents with those of a given table.
|
||||||
|
* @param other Table to exchange the content with.
|
||||||
|
*/
|
||||||
|
void swap(basic_table &other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
swap(payload, other.payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Increases the capacity of a table.
|
||||||
|
*
|
||||||
|
* If the new capacity is greater than the current capacity, new storage is
|
||||||
|
* allocated, otherwise the method does nothing.
|
||||||
|
*
|
||||||
|
* @param cap Desired capacity.
|
||||||
|
*/
|
||||||
|
void reserve(const size_type cap) {
|
||||||
|
(std::get<Container>(payload).reserve(cap), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of rows that a table has currently allocated
|
||||||
|
* space for.
|
||||||
|
* @return Capacity of the table.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type capacity() const noexcept {
|
||||||
|
return std::get<0>(payload).capacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Requests the removal of unused capacity. */
|
||||||
|
void shrink_to_fit() {
|
||||||
|
(std::get<Container>(payload).shrink_to_fit(), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of rows in a table.
|
||||||
|
* @return Number of rows.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] size_type size() const noexcept {
|
||||||
|
return std::get<0>(payload).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether a table is empty.
|
||||||
|
* @return True if the table is empty, false otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool empty() const noexcept {
|
||||||
|
return std::get<0>(payload).empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning.
|
||||||
|
*
|
||||||
|
* If the table is empty, the returned iterator will be equal to `end()`.
|
||||||
|
*
|
||||||
|
* @return An iterator to the first row of the table.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_iterator cbegin() const noexcept {
|
||||||
|
return {std::get<Container>(payload).cbegin()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc cbegin */
|
||||||
|
[[nodiscard]] const_iterator begin() const noexcept {
|
||||||
|
return cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc begin */
|
||||||
|
[[nodiscard]] iterator begin() noexcept {
|
||||||
|
return {std::get<Container>(payload).begin()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end.
|
||||||
|
* @return An iterator to the element following the last row of the table.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_iterator cend() const noexcept {
|
||||||
|
return {std::get<Container>(payload).cend()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc cend */
|
||||||
|
[[nodiscard]] const_iterator end() const noexcept {
|
||||||
|
return cend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc end */
|
||||||
|
[[nodiscard]] iterator end() noexcept {
|
||||||
|
return {std::get<Container>(payload).end()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a reverse iterator to the beginning.
|
||||||
|
*
|
||||||
|
* If the table is empty, the returned iterator will be equal to `rend()`.
|
||||||
|
*
|
||||||
|
* @return An iterator to the first row of the reversed table.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
|
||||||
|
return {std::get<Container>(payload).crbegin()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc crbegin */
|
||||||
|
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||||
|
return crbegin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc rbegin */
|
||||||
|
[[nodiscard]] reverse_iterator rbegin() noexcept {
|
||||||
|
return {std::get<Container>(payload).rbegin()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns a reverse iterator to the end.
|
||||||
|
* @return An iterator to the element following the last row of the reversed
|
||||||
|
* table.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const_reverse_iterator crend() const noexcept {
|
||||||
|
return {std::get<Container>(payload).crend()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc crend */
|
||||||
|
[[nodiscard]] const_reverse_iterator rend() const noexcept {
|
||||||
|
return crend();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc rend */
|
||||||
|
[[nodiscard]] reverse_iterator rend() noexcept {
|
||||||
|
return {std::get<Container>(payload).rend()...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Appends a row to the end of a table.
|
||||||
|
* @tparam Args Types of arguments to use to construct the row data.
|
||||||
|
* @param args Parameters to use to construct the row data.
|
||||||
|
* @return A reference to the newly created row data.
|
||||||
|
*/
|
||||||
|
template<typename... Args>
|
||||||
|
std::tuple<typename Container::value_type &...> emplace(Args &&...args) {
|
||||||
|
if constexpr(sizeof...(Args) == 0u) {
|
||||||
|
return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...);
|
||||||
|
} else {
|
||||||
|
return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes a row from a table.
|
||||||
|
* @param pos An iterator to the row to remove.
|
||||||
|
* @return An iterator following the removed row.
|
||||||
|
*/
|
||||||
|
iterator erase(const_iterator pos) {
|
||||||
|
const auto diff = pos - begin();
|
||||||
|
return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Removes a row from a table.
|
||||||
|
* @param pos Index of the row to remove.
|
||||||
|
*/
|
||||||
|
void erase(const size_type pos) {
|
||||||
|
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||||
|
erase(begin() + static_cast<difference_type>(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the row data at specified location.
|
||||||
|
* @param pos The row for which to return the data.
|
||||||
|
* @return The row data at specified location.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const {
|
||||||
|
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||||
|
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator[] */
|
||||||
|
[[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) {
|
||||||
|
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||||
|
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Clears a table. */
|
||||||
|
void clear() {
|
||||||
|
(std::get<Container>(payload).clear(), ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
container_type payload;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
/*! @cond TURN_OFF_DOXYGEN */
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template<typename... Container, typename Allocator>
|
||||||
|
struct uses_allocator<entt::basic_table<Container...>, Allocator>
|
||||||
|
: std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
/*! @endcond */
|
||||||
|
|
||||||
|
#endif
|
||||||
+145
@@ -0,0 +1,145 @@
|
|||||||
|
#ifndef ENTT_CORE_ALGORITHM_HPP
|
||||||
|
#define ENTT_CORE_ALGORITHM_HPP
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <functional>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function object to wrap `std::sort` in a class type.
|
||||||
|
*
|
||||||
|
* Unfortunately, `std::sort` cannot be passed as template argument to a class
|
||||||
|
* template or a function template.<br/>
|
||||||
|
* This class fills the gap by wrapping some flavors of `std::sort` in a
|
||||||
|
* function object.
|
||||||
|
*/
|
||||||
|
struct std_sort {
|
||||||
|
/**
|
||||||
|
* @brief Sorts the elements in a range.
|
||||||
|
*
|
||||||
|
* 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<>, 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 insertion_sort {
|
||||||
|
/**
|
||||||
|
* @brief Sorts the elements in a range.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
* @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) {
|
||||||
|
for(auto it = first + 1; it < last; ++it) {
|
||||||
|
auto value = std::move(*it);
|
||||||
|
auto pre = it;
|
||||||
|
|
||||||
|
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||||
|
for(; pre > first && compare(value, *(pre - 1)); --pre) {
|
||||||
|
*pre = std::move(*(pre - 1));
|
||||||
|
}
|
||||||
|
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||||
|
|
||||||
|
*pre = std::move(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function object for performing LSD radix sort.
|
||||||
|
* @tparam Bit Number of bits processed per pass.
|
||||||
|
* @tparam N Maximum number of bits to sort.
|
||||||
|
*/
|
||||||
|
template<std::size_t Bit, std::size_t N>
|
||||||
|
struct radix_sort {
|
||||||
|
static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sorts the elements in a range.
|
||||||
|
*
|
||||||
|
* Sorts the elements in a range using the given _getter_ to access the
|
||||||
|
* actual data to be sorted.
|
||||||
|
*
|
||||||
|
* This implementation is inspired by the online book
|
||||||
|
* [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort).
|
||||||
|
*
|
||||||
|
* @tparam It Type of random access iterator.
|
||||||
|
* @tparam Getter Type of _getter_ 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 getter A valid _getter_ function object.
|
||||||
|
*/
|
||||||
|
template<typename It, typename Getter = identity>
|
||||||
|
void operator()(It first, It last, Getter getter = Getter{}) const {
|
||||||
|
if(first < last) {
|
||||||
|
constexpr auto passes = N / Bit;
|
||||||
|
|
||||||
|
using value_type = typename std::iterator_traits<It>::value_type;
|
||||||
|
using difference_type = typename std::iterator_traits<It>::difference_type;
|
||||||
|
std::vector<value_type> aux(static_cast<std::size_t>(std::distance(first, last)));
|
||||||
|
|
||||||
|
auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) {
|
||||||
|
constexpr auto mask = (1 << Bit) - 1;
|
||||||
|
constexpr auto buckets = 1 << Bit;
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays, misc-const-correctness)
|
||||||
|
std::size_t count[buckets]{};
|
||||||
|
|
||||||
|
for(auto it = from; it != to; ++it) {
|
||||||
|
++count[(getter(*it) >> start) & mask];
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||||
|
std::size_t index[buckets]{};
|
||||||
|
|
||||||
|
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
|
||||||
|
index[pos + 1u] = index[pos] + count[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto it = from; it != to; ++it) {
|
||||||
|
const auto pos = index[(getter(*it) >> start) & mask]++;
|
||||||
|
out[static_cast<difference_type>(pos)] = std::move(*it);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for(std::size_t pass = 0; pass < (passes & ~1u); pass += 2) {
|
||||||
|
part(first, last, aux.begin(), pass * Bit);
|
||||||
|
part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if constexpr(passes & 1) {
|
||||||
|
part(first, last, aux.begin(), (passes - 1) * Bit);
|
||||||
|
std::move(aux.begin(), aux.end(), first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
Vendored
+544
@@ -0,0 +1,544 @@
|
|||||||
|
#ifndef ENTT_CORE_ANY_HPP
|
||||||
|
#define ENTT_CORE_ANY_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include "../config/config.h"
|
||||||
|
#include "../core/utility.hpp"
|
||||||
|
#include "fwd.hpp"
|
||||||
|
#include "type_info.hpp"
|
||||||
|
#include "type_traits.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/*! @cond TURN_OFF_DOXYGEN */
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
enum class any_request : std::uint8_t {
|
||||||
|
transfer,
|
||||||
|
assign,
|
||||||
|
destroy,
|
||||||
|
compare,
|
||||||
|
copy,
|
||||||
|
move,
|
||||||
|
get
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
/*! @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||||
|
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||||
|
* @tparam Align Optional alignment requirement.
|
||||||
|
*/
|
||||||
|
template<std::size_t Len, std::size_t Align>
|
||||||
|
class basic_any {
|
||||||
|
using request = internal::any_request;
|
||||||
|
using vtable_type = const void *(const request, const basic_any &, const void *);
|
||||||
|
|
||||||
|
struct storage_type {
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||||
|
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||||
|
static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
|
||||||
|
|
||||||
|
template<typename Type>
|
||||||
|
static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
|
||||||
|
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||||
|
const Type *elem = nullptr;
|
||||||
|
|
||||||
|
if constexpr(in_situ<Type>) {
|
||||||
|
elem = (value.mode == any_policy::embedded) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||||
|
} else {
|
||||||
|
elem = static_cast<const Type *>(value.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(req) {
|
||||||
|
case request::transfer:
|
||||||
|
if constexpr(std::is_move_assignable_v<Type>) {
|
||||||
|
// NOLINTNEXTLINE(bugprone-casting-through-void)
|
||||||
|
*const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case request::assign:
|
||||||
|
if constexpr(std::is_copy_assignable_v<Type>) {
|
||||||
|
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case request::destroy:
|
||||||
|
if constexpr(in_situ<Type>) {
|
||||||
|
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
|
||||||
|
} else if constexpr(std::is_array_v<Type>) {
|
||||||
|
delete[] elem;
|
||||||
|
} else {
|
||||||
|
delete elem;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case request::compare:
|
||||||
|
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||||
|
return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
|
||||||
|
} else {
|
||||||
|
return (elem == other) ? other : nullptr;
|
||||||
|
}
|
||||||
|
case request::copy:
|
||||||
|
if constexpr(std::is_copy_constructible_v<Type>) {
|
||||||
|
// NOLINTNEXTLINE(bugprone-casting-through-void)
|
||||||
|
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case request::move:
|
||||||
|
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
|
||||||
|
if constexpr(in_situ<Type>) {
|
||||||
|
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
|
||||||
|
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
|
||||||
|
}
|
||||||
|
[[fallthrough]];
|
||||||
|
case request::get:
|
||||||
|
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
|
||||||
|
if constexpr(in_situ<Type>) {
|
||||||
|
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||||
|
return elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Type, typename... Args>
|
||||||
|
void initialize([[maybe_unused]] Args &&...args) {
|
||||||
|
if constexpr(!std::is_void_v<Type>) {
|
||||||
|
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
|
||||||
|
|
||||||
|
info = &type_id<plain_type>();
|
||||||
|
vtable = basic_vtable<plain_type>;
|
||||||
|
|
||||||
|
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||||
|
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||||
|
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||||
|
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||||
|
instance = (std::addressof(args), ...);
|
||||||
|
} else if constexpr(in_situ<plain_type>) {
|
||||||
|
mode = any_policy::embedded;
|
||||||
|
|
||||||
|
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||||
|
::new(&storage) plain_type{std::forward<Args>(args)...};
|
||||||
|
} else {
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||||
|
::new(&storage) plain_type(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mode = any_policy::dynamic;
|
||||||
|
|
||||||
|
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||||
|
instance = new plain_type{std::forward<Args>(args)...};
|
||||||
|
} else if constexpr(std::is_array_v<plain_type>) {
|
||||||
|
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
|
||||||
|
instance = new plain_type[std::extent_v<plain_type>]();
|
||||||
|
} else {
|
||||||
|
instance = new plain_type(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_any(const basic_any &other, const any_policy pol) noexcept
|
||||||
|
: instance{other.data()},
|
||||||
|
info{other.info},
|
||||||
|
vtable{other.vtable},
|
||||||
|
mode{pol} {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Size of the internal storage. */
|
||||||
|
static constexpr auto length = Len;
|
||||||
|
/*! @brief Alignment requirement. */
|
||||||
|
static constexpr auto alignment = Align;
|
||||||
|
|
||||||
|
/*! @brief Default constructor. */
|
||||||
|
constexpr basic_any() noexcept
|
||||||
|
: basic_any{std::in_place_type<void>} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a wrapper by directly initializing the new object.
|
||||||
|
* @tparam Type Type of object to use to initialize the wrapper.
|
||||||
|
* @tparam Args Types of arguments to use to construct the new instance.
|
||||||
|
* @param args Parameters to use to construct the instance.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename... Args>
|
||||||
|
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
|
||||||
|
: instance{} {
|
||||||
|
initialize<Type>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a wrapper taking ownership of the passed object.
|
||||||
|
* @tparam Type Type of object to use to initialize the wrapper.
|
||||||
|
* @param value A pointer to an object to take ownership of.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
explicit basic_any(std::in_place_t, Type *value)
|
||||||
|
: instance{} {
|
||||||
|
static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
|
||||||
|
|
||||||
|
if(value != nullptr) {
|
||||||
|
initialize<Type &>(*value);
|
||||||
|
mode = any_policy::dynamic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a wrapper from a given value.
|
||||||
|
* @tparam Type Type of object to use to initialize the wrapper.
|
||||||
|
* @param value An instance of an object to use to initialize the wrapper.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
|
||||||
|
basic_any(Type &&value)
|
||||||
|
: basic_any{std::in_place_type<std::decay_t<Type>>, std::forward<Type>(value)} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructor.
|
||||||
|
* @param other The instance to copy from.
|
||||||
|
*/
|
||||||
|
basic_any(const basic_any &other)
|
||||||
|
: basic_any{} {
|
||||||
|
if(other.vtable) {
|
||||||
|
other.vtable(request::copy, other, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
*/
|
||||||
|
basic_any(basic_any &&other) noexcept
|
||||||
|
: instance{},
|
||||||
|
info{other.info},
|
||||||
|
vtable{other.vtable},
|
||||||
|
mode{other.mode} {
|
||||||
|
if(other.mode == any_policy::embedded) {
|
||||||
|
other.vtable(request::move, other, this);
|
||||||
|
} else if(other.mode != any_policy::empty) {
|
||||||
|
instance = std::exchange(other.instance, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Frees the internal storage, whatever it means. */
|
||||||
|
~basic_any() {
|
||||||
|
if(owner()) {
|
||||||
|
vtable(request::destroy, *this, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy assignment operator.
|
||||||
|
* @param other The instance to copy from.
|
||||||
|
* @return This any object.
|
||||||
|
*/
|
||||||
|
basic_any &operator=(const basic_any &other) {
|
||||||
|
if(this != &other) {
|
||||||
|
reset();
|
||||||
|
|
||||||
|
if(other.vtable) {
|
||||||
|
other.vtable(request::copy, other, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move assignment operator.
|
||||||
|
*
|
||||||
|
* @warning
|
||||||
|
* Self-moving puts objects in a safe but unspecified state.
|
||||||
|
*
|
||||||
|
* @param other The instance to move from.
|
||||||
|
* @return This any object.
|
||||||
|
*/
|
||||||
|
basic_any &operator=(basic_any &&other) noexcept {
|
||||||
|
reset();
|
||||||
|
|
||||||
|
if(other.mode == any_policy::embedded) {
|
||||||
|
other.vtable(request::move, other, this);
|
||||||
|
} else if(other.mode != any_policy::empty) {
|
||||||
|
instance = std::exchange(other.instance, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
info = other.info;
|
||||||
|
vtable = other.vtable;
|
||||||
|
mode = other.mode;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Value assignment operator.
|
||||||
|
* @tparam Type Type of object to use to initialize the wrapper.
|
||||||
|
* @param value An instance of an object to use to initialize the wrapper.
|
||||||
|
* @return This any object.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
|
||||||
|
basic_any &operator=(Type &&value) {
|
||||||
|
emplace<std::decay_t<Type>>(std::forward<Type>(value));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||||
|
* @return The object type if any, `type_id<void>()` otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const type_info &type() const noexcept {
|
||||||
|
return (info == nullptr) ? type_id<void>() : *info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an opaque pointer to the contained instance.
|
||||||
|
* @return An opaque pointer the contained instance, if any.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const void *data() const noexcept {
|
||||||
|
return (mode == any_policy::embedded) ? vtable(request::get, *this, nullptr) : instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an opaque pointer to the contained instance.
|
||||||
|
* @param req Expected type.
|
||||||
|
* @return An opaque pointer the contained instance, if any.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] const void *data(const type_info &req) const noexcept {
|
||||||
|
return (type() == req) ? data() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an opaque pointer to the contained instance.
|
||||||
|
* @return An opaque pointer the contained instance, if any.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] void *data() noexcept {
|
||||||
|
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an opaque pointer to the contained instance.
|
||||||
|
* @param req Expected type.
|
||||||
|
* @return An opaque pointer the contained instance, if any.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] void *data(const type_info &req) noexcept {
|
||||||
|
return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Replaces the contained object by creating a new instance directly.
|
||||||
|
* @tparam Type Type of object to use to initialize the wrapper.
|
||||||
|
* @tparam Args Types of arguments to use to construct the new instance.
|
||||||
|
* @param args Parameters to use to construct the instance.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename... Args>
|
||||||
|
void emplace(Args &&...args) {
|
||||||
|
reset();
|
||||||
|
initialize<Type>(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Assigns a value to the contained object without replacing it.
|
||||||
|
* @param other The value to assign to the contained object.
|
||||||
|
* @return True in case of success, false otherwise.
|
||||||
|
*/
|
||||||
|
bool assign(const basic_any &other) {
|
||||||
|
if(vtable && mode != any_policy::cref && *info == other.type()) {
|
||||||
|
return (vtable(request::assign, *this, other.data()) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc assign */
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||||
|
bool assign(basic_any &&other) {
|
||||||
|
if(vtable && mode != any_policy::cref && *info == other.type()) {
|
||||||
|
if(auto *val = other.data(); val) {
|
||||||
|
return (vtable(request::transfer, *this, val) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (vtable(request::assign, *this, std::as_const(other).data()) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Destroys contained object */
|
||||||
|
void reset() {
|
||||||
|
if(owner()) {
|
||||||
|
vtable(request::destroy, *this, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = nullptr;
|
||||||
|
info = nullptr;
|
||||||
|
vtable = nullptr;
|
||||||
|
mode = any_policy::empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns false if a wrapper is empty, true otherwise.
|
||||||
|
* @return False if the wrapper is empty, true otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] explicit operator bool() const noexcept {
|
||||||
|
return vtable != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if two wrappers differ in their content.
|
||||||
|
* @param other Wrapper with which to compare.
|
||||||
|
* @return False if the two objects differ in their content, true otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool operator==(const basic_any &other) const noexcept {
|
||||||
|
if(vtable && *info == other.type()) {
|
||||||
|
return (vtable(request::compare, *this, other.data()) != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (!vtable && !other.vtable);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks if two wrappers differ in their content.
|
||||||
|
* @param other Wrapper with which to compare.
|
||||||
|
* @return True if the two objects differ in their content, false otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aliasing constructor.
|
||||||
|
* @return A wrapper that shares a reference to an unmanaged object.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] basic_any as_ref() noexcept {
|
||||||
|
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc as_ref */
|
||||||
|
[[nodiscard]] basic_any as_ref() const noexcept {
|
||||||
|
return basic_any{*this, any_policy::cref};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns true if a wrapper owns its object, false otherwise.
|
||||||
|
* @return True if the wrapper owns its object, false otherwise.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] bool owner() const noexcept {
|
||||||
|
return (mode == any_policy::dynamic || mode == any_policy::embedded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the current mode of an any object.
|
||||||
|
* @return The current mode of the any object.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] any_policy policy() const noexcept {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
union {
|
||||||
|
const void *instance;
|
||||||
|
storage_type storage;
|
||||||
|
};
|
||||||
|
const type_info *info{};
|
||||||
|
vtable_type *vtable{};
|
||||||
|
any_policy mode{any_policy::empty};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Performs type-safe access to the contained object.
|
||||||
|
* @tparam Type Type to which conversion is required.
|
||||||
|
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||||
|
* @tparam Align Alignment requirement.
|
||||||
|
* @param data Target any object.
|
||||||
|
* @return The element converted to the requested type.
|
||||||
|
*/
|
||||||
|
template<typename Type, std::size_t Len, std::size_t Align>
|
||||||
|
[[nodiscard]] std::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
|
||||||
|
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||||
|
ENTT_ASSERT(instance, "Invalid instance");
|
||||||
|
return static_cast<Type>(*instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc any_cast */
|
||||||
|
template<typename Type, std::size_t Len, std::size_t Align>
|
||||||
|
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
|
||||||
|
// forces const on non-reference types to make them work also with wrappers for const references
|
||||||
|
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
|
||||||
|
ENTT_ASSERT(instance, "Invalid instance");
|
||||||
|
return static_cast<Type>(*instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc any_cast */
|
||||||
|
template<typename Type, std::size_t Len, std::size_t Align>
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||||
|
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||||
|
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||||
|
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
|
||||||
|
return static_cast<Type>(std::move(*instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
return any_cast<Type>(data);
|
||||||
|
} else {
|
||||||
|
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||||
|
ENTT_ASSERT(instance, "Invalid instance");
|
||||||
|
return static_cast<Type>(std::move(*instance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc any_cast */
|
||||||
|
template<typename Type, std::size_t Len, std::size_t Align>
|
||||||
|
[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
|
||||||
|
const auto &info = type_id<std::remove_cv_t<Type>>();
|
||||||
|
return static_cast<const Type *>(data->data(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc any_cast */
|
||||||
|
template<typename Type, std::size_t Len, std::size_t Align>
|
||||||
|
[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
|
||||||
|
if constexpr(std::is_const_v<Type>) {
|
||||||
|
// last attempt to make wrappers for const references return their values
|
||||||
|
return any_cast<Type>(&std::as_const(*data));
|
||||||
|
} else {
|
||||||
|
const auto &info = type_id<std::remove_cv_t<Type>>();
|
||||||
|
return static_cast<Type *>(data->data(info));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a wrapper from a given type, passing it all arguments.
|
||||||
|
* @tparam Type Type of object to use to initialize the wrapper.
|
||||||
|
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||||
|
* @tparam Align Optional alignment requirement.
|
||||||
|
* @tparam Args Types of arguments to use to construct the new instance.
|
||||||
|
* @param args Parameters to use to construct the instance.
|
||||||
|
* @return A properly initialized wrapper for an object of the given type.
|
||||||
|
*/
|
||||||
|
template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
|
||||||
|
[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
|
||||||
|
return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Forwards its argument and avoids copies for lvalue references.
|
||||||
|
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||||
|
* @tparam Align Optional alignment requirement.
|
||||||
|
* @tparam Type Type of argument to use to construct the new instance.
|
||||||
|
* @param value Parameter to use to construct the instance.
|
||||||
|
* @return A properly initialized and not necessarily owning wrapper.
|
||||||
|
*/
|
||||||
|
template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
|
||||||
|
[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
|
||||||
|
return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
#ifndef ENTT_CORE_ATTRIBUTE_H
|
||||||
|
#define ENTT_CORE_ATTRIBUTE_H
|
||||||
|
|
||||||
|
#ifndef ENTT_EXPORT
|
||||||
|
# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER
|
||||||
|
# define ENTT_EXPORT __declspec(dllexport)
|
||||||
|
# define ENTT_IMPORT __declspec(dllimport)
|
||||||
|
# define ENTT_HIDDEN
|
||||||
|
# elif defined __GNUC__ && __GNUC__ >= 4
|
||||||
|
# define ENTT_EXPORT __attribute__((visibility("default")))
|
||||||
|
# define ENTT_IMPORT __attribute__((visibility("default")))
|
||||||
|
# define ENTT_HIDDEN __attribute__((visibility("hidden")))
|
||||||
|
# else /* Unsupported compiler */
|
||||||
|
# define ENTT_EXPORT
|
||||||
|
# define ENTT_IMPORT
|
||||||
|
# define ENTT_HIDDEN
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENTT_API
|
||||||
|
# if defined ENTT_API_EXPORT
|
||||||
|
# define ENTT_API ENTT_EXPORT
|
||||||
|
# elif defined ENTT_API_IMPORT
|
||||||
|
# define ENTT_API ENTT_IMPORT
|
||||||
|
# else /* No API */
|
||||||
|
# define ENTT_API
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Vendored
+70
@@ -0,0 +1,70 @@
|
|||||||
|
#ifndef ENTT_CORE_BIT_HPP
|
||||||
|
#define ENTT_CORE_BIT_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
#include "../config/config.h"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the number of set bits in a value (waiting for C++20 and
|
||||||
|
* `std::popcount`).
|
||||||
|
* @tparam Type Unsigned integer type.
|
||||||
|
* @param value A value of unsigned integer type.
|
||||||
|
* @return The number of set bits in the value.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
|
||||||
|
return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
|
||||||
|
* `std::has_single_bit`).
|
||||||
|
* @tparam Type Unsigned integer type.
|
||||||
|
* @param value A value of unsigned integer type.
|
||||||
|
* @return True if the value is a power of two, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
|
||||||
|
return value && ((value & (value - 1)) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Computes the smallest power of two greater than or equal to a value
|
||||||
|
* (waiting for C++20 and `std::bit_ceil`).
|
||||||
|
* @tparam Type Unsigned integer type.
|
||||||
|
* @param value A value of unsigned integer type.
|
||||||
|
* @return The smallest power of two greater than or equal to the given value.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept {
|
||||||
|
// NOLINTNEXTLINE(bugprone-assert-side-effect)
|
||||||
|
ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded");
|
||||||
|
Type curr = value - (value != 0u);
|
||||||
|
|
||||||
|
for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) {
|
||||||
|
curr |= (curr >> next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ++curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fast module utility function (powers of two only).
|
||||||
|
* @tparam Type Unsigned integer type.
|
||||||
|
* @param value A value of unsigned integer type.
|
||||||
|
* @param mod _Modulus_, it must be a power of two.
|
||||||
|
* @return The common remainder.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept {
|
||||||
|
ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
|
||||||
|
return static_cast<Type>(value & (mod - 1u));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+272
@@ -0,0 +1,272 @@
|
|||||||
|
#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP
|
||||||
|
#define ENTT_CORE_COMPRESSED_PAIR_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include "fwd.hpp"
|
||||||
|
#include "type_traits.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/*! @cond TURN_OFF_DOXYGEN */
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template<typename Type, std::size_t, typename = void>
|
||||||
|
struct compressed_pair_element {
|
||||||
|
using reference = Type &;
|
||||||
|
using const_reference = const Type &;
|
||||||
|
|
||||||
|
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
|
||||||
|
// NOLINTNEXTLINE(modernize-use-equals-default)
|
||||||
|
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {}
|
||||||
|
|
||||||
|
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||||
|
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
|
||||||
|
: value{std::forward<Arg>(arg)} {}
|
||||||
|
|
||||||
|
template<typename... Args, std::size_t... Index>
|
||||||
|
constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<Type, Args...>)
|
||||||
|
: value{std::forward<Args>(std::get<Index>(args))...} {}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference get() noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type value{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Type, std::size_t Tag>
|
||||||
|
struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Type>>>: Type {
|
||||||
|
using reference = Type &;
|
||||||
|
using const_reference = const Type &;
|
||||||
|
using base_type = Type;
|
||||||
|
|
||||||
|
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
|
||||||
|
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
|
||||||
|
: base_type{} {}
|
||||||
|
|
||||||
|
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
|
||||||
|
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<base_type, Arg>)
|
||||||
|
: base_type{std::forward<Arg>(arg)} {}
|
||||||
|
|
||||||
|
template<typename... Args, std::size_t... Index>
|
||||||
|
constexpr compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>) noexcept(std::is_nothrow_constructible_v<base_type, Args...>)
|
||||||
|
: base_type{std::forward<Args>(std::get<Index>(args))...} {}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr reference get() noexcept {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
/*! @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A compressed pair.
|
||||||
|
*
|
||||||
|
* A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to
|
||||||
|
* reduce its final size to a minimum.
|
||||||
|
*
|
||||||
|
* @tparam First The type of the first element that the pair stores.
|
||||||
|
* @tparam Second The type of the second element that the pair stores.
|
||||||
|
*/
|
||||||
|
template<typename First, typename Second>
|
||||||
|
class compressed_pair final
|
||||||
|
: internal::compressed_pair_element<First, 0u>,
|
||||||
|
internal::compressed_pair_element<Second, 1u> {
|
||||||
|
using first_base = internal::compressed_pair_element<First, 0u>;
|
||||||
|
using second_base = internal::compressed_pair_element<Second, 1u>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief The type of the first element that the pair stores. */
|
||||||
|
using first_type = First;
|
||||||
|
/*! @brief The type of the second element that the pair stores. */
|
||||||
|
using second_type = Second;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default constructor, conditionally enabled.
|
||||||
|
*
|
||||||
|
* This constructor is only available when the types that the pair stores
|
||||||
|
* are both at least default constructible.
|
||||||
|
*
|
||||||
|
* @tparam Dummy Dummy template parameter used for internal purposes.
|
||||||
|
*/
|
||||||
|
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
|
||||||
|
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>)
|
||||||
|
: first_base{},
|
||||||
|
second_base{} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy constructor.
|
||||||
|
* @param other The instance to copy from.
|
||||||
|
*/
|
||||||
|
constexpr compressed_pair(const compressed_pair &other) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move constructor.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
*/
|
||||||
|
constexpr compressed_pair(compressed_pair &&other) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a pair from its values.
|
||||||
|
* @tparam Arg Type of value to use to initialize the first element.
|
||||||
|
* @tparam Other Type of value to use to initialize the second element.
|
||||||
|
* @param arg Value to use to initialize the first element.
|
||||||
|
* @param other Value to use to initialize the second element.
|
||||||
|
*/
|
||||||
|
template<typename Arg, typename Other>
|
||||||
|
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> && std::is_nothrow_constructible_v<second_base, Other>)
|
||||||
|
: first_base{std::forward<Arg>(arg)},
|
||||||
|
second_base{std::forward<Other>(other)} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a pair by forwarding the arguments to its parts.
|
||||||
|
* @tparam Args Types of arguments to use to initialize the first element.
|
||||||
|
* @tparam Other Types of arguments to use to initialize the second element.
|
||||||
|
* @param args Arguments to use to initialize the first element.
|
||||||
|
* @param other Arguments to use to initialize the second element.
|
||||||
|
*/
|
||||||
|
template<typename... Args, typename... Other>
|
||||||
|
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> && std::is_nothrow_constructible_v<second_base, Other...>)
|
||||||
|
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
|
||||||
|
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
|
||||||
|
|
||||||
|
/*! @brief Default destructor. */
|
||||||
|
~compressed_pair() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Copy assignment operator.
|
||||||
|
* @param other The instance to copy from.
|
||||||
|
* @return This compressed pair object.
|
||||||
|
*/
|
||||||
|
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Move assignment operator.
|
||||||
|
* @param other The instance to move from.
|
||||||
|
* @return This compressed pair object.
|
||||||
|
*/
|
||||||
|
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the first element that a pair stores.
|
||||||
|
* @return The first element that a pair stores.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr first_type &first() noexcept {
|
||||||
|
return static_cast<first_base &>(*this).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc first */
|
||||||
|
[[nodiscard]] constexpr const first_type &first() const noexcept {
|
||||||
|
return static_cast<const first_base &>(*this).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the second element that a pair stores.
|
||||||
|
* @return The second element that a pair stores.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr second_type &second() noexcept {
|
||||||
|
return static_cast<second_base &>(*this).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc second */
|
||||||
|
[[nodiscard]] constexpr const second_type &second() const noexcept {
|
||||||
|
return static_cast<const second_base &>(*this).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swaps two compressed pair objects.
|
||||||
|
* @param other The compressed pair to swap with.
|
||||||
|
*/
|
||||||
|
constexpr void swap(compressed_pair &other) noexcept {
|
||||||
|
using std::swap;
|
||||||
|
swap(first(), other.first());
|
||||||
|
swap(second(), other.second());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Extracts an element from the compressed pair.
|
||||||
|
* @tparam Index An integer value that is either 0 or 1.
|
||||||
|
* @return Returns a reference to the first element if `Index` is 0 and a
|
||||||
|
* reference to the second element if `Index` is 1.
|
||||||
|
*/
|
||||||
|
template<std::size_t Index>
|
||||||
|
[[nodiscard]] constexpr decltype(auto) get() noexcept {
|
||||||
|
if constexpr(Index == 0u) {
|
||||||
|
return first();
|
||||||
|
} else {
|
||||||
|
static_assert(Index == 1u, "Index out of bounds");
|
||||||
|
return second();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc get */
|
||||||
|
template<std::size_t Index>
|
||||||
|
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
|
||||||
|
if constexpr(Index == 0u) {
|
||||||
|
return first();
|
||||||
|
} else {
|
||||||
|
static_assert(Index == 1u, "Index out of bounds");
|
||||||
|
return second();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deduction guide.
|
||||||
|
* @tparam Type Type of value to use to initialize the first element.
|
||||||
|
* @tparam Other Type of value to use to initialize the second element.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename Other>
|
||||||
|
compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::decay_t<Other>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Swaps two compressed pair objects.
|
||||||
|
* @tparam First The type of the first element that the pairs store.
|
||||||
|
* @tparam Second The type of the second element that the pairs store.
|
||||||
|
* @param lhs A valid compressed pair object.
|
||||||
|
* @param rhs A valid compressed pair object.
|
||||||
|
*/
|
||||||
|
template<typename First, typename Second>
|
||||||
|
constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) noexcept {
|
||||||
|
lhs.swap(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief `std::tuple_size` specialization for `compressed_pair`s.
|
||||||
|
* @tparam First The type of the first element that the pair stores.
|
||||||
|
* @tparam Second The type of the second element that the pair stores.
|
||||||
|
*/
|
||||||
|
template<typename First, typename Second>
|
||||||
|
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<size_t, 2u> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief `std::tuple_element` specialization for `compressed_pair`s.
|
||||||
|
* @tparam Index The index of the type to return.
|
||||||
|
* @tparam First The type of the first element that the pair stores.
|
||||||
|
* @tparam Second The type of the second element that the pair stores.
|
||||||
|
*/
|
||||||
|
template<size_t Index, typename First, typename Second>
|
||||||
|
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {
|
||||||
|
static_assert(Index < 2u, "Index out of bounds");
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
||||||
|
|
||||||
|
#endif
|
||||||
Vendored
+97
@@ -0,0 +1,97 @@
|
|||||||
|
#ifndef ENTT_CORE_ENUM_HPP
|
||||||
|
#define ENTT_CORE_ENUM_HPP
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable bitmask support for enum classes.
|
||||||
|
* @tparam Type The enum type for which to enable bitmask support.
|
||||||
|
*/
|
||||||
|
template<typename Type, typename = void>
|
||||||
|
struct enum_as_bitmask: std::false_type {};
|
||||||
|
|
||||||
|
/*! @copydoc enum_as_bitmask */
|
||||||
|
template<typename Type>
|
||||||
|
struct enum_as_bitmask<Type, std::void_t<decltype(Type::_entt_enum_as_bitmask)>>: std::is_enum<Type> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper variable template.
|
||||||
|
* @tparam Type The enum class type for which to enable bitmask support.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Operator available for enums for which bitmask support is enabled.
|
||||||
|
* @tparam Type Enum class type.
|
||||||
|
* @param lhs The first value to use.
|
||||||
|
* @param rhs The second value to use.
|
||||||
|
* @return The result of invoking the operator on the underlying types of the
|
||||||
|
* two values provided.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||||
|
operator|(const Type lhs, const Type rhs) noexcept {
|
||||||
|
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) | static_cast<std::underlying_type_t<Type>>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator| */
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||||
|
operator&(const Type lhs, const Type rhs) noexcept {
|
||||||
|
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) & static_cast<std::underlying_type_t<Type>>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator| */
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||||
|
operator^(const Type lhs, const Type rhs) noexcept {
|
||||||
|
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Operator available for enums for which bitmask support is enabled.
|
||||||
|
* @tparam Type Enum class type.
|
||||||
|
* @param value The value to use.
|
||||||
|
* @return The result of invoking the operator on the underlying types of the
|
||||||
|
* value provided.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||||
|
operator~(const Type value) noexcept {
|
||||||
|
return static_cast<Type>(~static_cast<std::underlying_type_t<Type>>(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator~ */
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, bool>
|
||||||
|
operator!(const Type value) noexcept {
|
||||||
|
return !static_cast<std::underlying_type_t<Type>>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator| */
|
||||||
|
template<typename Type>
|
||||||
|
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
|
||||||
|
operator|=(Type &lhs, const Type rhs) noexcept {
|
||||||
|
return (lhs = (lhs | rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator| */
|
||||||
|
template<typename Type>
|
||||||
|
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
|
||||||
|
operator&=(Type &lhs, const Type rhs) noexcept {
|
||||||
|
return (lhs = (lhs & rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc operator| */
|
||||||
|
template<typename Type>
|
||||||
|
constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type &>
|
||||||
|
operator^=(Type &lhs, const Type rhs) noexcept {
|
||||||
|
return (lhs = (lhs ^ rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
+33
@@ -0,0 +1,33 @@
|
|||||||
|
#ifndef ENTT_CORE_FAMILY_HPP
|
||||||
|
#define ENTT_CORE_FAMILY_HPP
|
||||||
|
|
||||||
|
#include "../config/config.h"
|
||||||
|
#include "fwd.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dynamic identifier generator.
|
||||||
|
*
|
||||||
|
* Utility class template that can be used to assign unique identifiers to types
|
||||||
|
* at runtime. Use different specializations to create separate sets of
|
||||||
|
* identifiers.
|
||||||
|
*/
|
||||||
|
template<typename...>
|
||||||
|
class family {
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Unsigned integer type. */
|
||||||
|
using value_type = id_type;
|
||||||
|
|
||||||
|
/*! @brief Statically generated unique identifier for the given type. */
|
||||||
|
template<typename... Type>
|
||||||
|
// at the time I'm writing, clang crashes during compilation if auto is used instead of family_type
|
||||||
|
inline static const value_type value = identifier++;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
Vendored
+51
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef ENTT_CORE_FWD_HPP
|
||||||
|
#define ENTT_CORE_FWD_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include "../config/config.h"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/*! @brief Possible modes of an any object. */
|
||||||
|
enum class any_policy : std::uint8_t {
|
||||||
|
/*! @brief Default mode, the object does not own any elements. */
|
||||||
|
empty,
|
||||||
|
/*! @brief Owning mode, the object owns a dynamically allocated element. */
|
||||||
|
dynamic,
|
||||||
|
/*! @brief Owning mode, the object owns an embedded element. */
|
||||||
|
embedded,
|
||||||
|
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
|
||||||
|
ref,
|
||||||
|
/*! @brief Const aliasing mode, the object _points_ to a const element. */
|
||||||
|
cref
|
||||||
|
};
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||||
|
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
|
||||||
|
class basic_any;
|
||||||
|
|
||||||
|
/*! @brief Alias declaration for type identifiers. */
|
||||||
|
using id_type = ENTT_ID_TYPE;
|
||||||
|
|
||||||
|
/*! @brief Alias declaration for the most common use case. */
|
||||||
|
using any = basic_any<>;
|
||||||
|
|
||||||
|
template<typename, typename>
|
||||||
|
class compressed_pair;
|
||||||
|
|
||||||
|
template<typename>
|
||||||
|
class basic_hashed_string;
|
||||||
|
|
||||||
|
/*! @brief Aliases for common character types. */
|
||||||
|
using hashed_string = basic_hashed_string<char>;
|
||||||
|
|
||||||
|
/*! @brief Aliases for common character types. */
|
||||||
|
using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||||
|
|
||||||
|
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
|
||||||
|
struct type_info;
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+311
@@ -0,0 +1,311 @@
|
|||||||
|
#ifndef ENTT_CORE_HASHED_STRING_HPP
|
||||||
|
#define ENTT_CORE_HASHED_STRING_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string_view>
|
||||||
|
#include "fwd.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/*! @cond TURN_OFF_DOXYGEN */
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template<typename = id_type>
|
||||||
|
struct fnv_1a_params;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct fnv_1a_params<std::uint32_t> {
|
||||||
|
static constexpr auto offset = 2166136261;
|
||||||
|
static constexpr auto prime = 16777619;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct fnv_1a_params<std::uint64_t> {
|
||||||
|
static constexpr auto offset = 14695981039346656037ull;
|
||||||
|
static constexpr auto prime = 1099511628211ull;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Char>
|
||||||
|
struct basic_hashed_string {
|
||||||
|
using value_type = Char;
|
||||||
|
using size_type = std::size_t;
|
||||||
|
using hash_type = id_type;
|
||||||
|
|
||||||
|
const value_type *repr;
|
||||||
|
size_type length;
|
||||||
|
hash_type hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
/*! @endcond */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Zero overhead unique identifier.
|
||||||
|
*
|
||||||
|
* A hashed string is a compile-time tool that allows users to use
|
||||||
|
* human-readable identifiers in the codebase while using their numeric
|
||||||
|
* counterparts at runtime.<br/>
|
||||||
|
* Because of that, a hashed string can also be used in constant expressions if
|
||||||
|
* required.
|
||||||
|
*
|
||||||
|
* @warning
|
||||||
|
* This class doesn't take ownership of user-supplied strings nor does it make a
|
||||||
|
* copy of them.
|
||||||
|
*
|
||||||
|
* @tparam Char Character type.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||||
|
using base_type = internal::basic_hashed_string<Char>;
|
||||||
|
using params = internal::fnv_1a_params<>;
|
||||||
|
|
||||||
|
struct const_wrapper {
|
||||||
|
// non-explicit constructor on purpose
|
||||||
|
constexpr const_wrapper(const Char *str) noexcept
|
||||||
|
: repr{str} {}
|
||||||
|
|
||||||
|
const Char *repr;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||||
|
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
|
||||||
|
base_type base{view.data(), view.size(), params::offset};
|
||||||
|
|
||||||
|
for(auto &&curr: view) {
|
||||||
|
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Character type. */
|
||||||
|
using value_type = typename base_type::value_type;
|
||||||
|
/*! @brief Unsigned integer type. */
|
||||||
|
using size_type = typename base_type::size_type;
|
||||||
|
/*! @brief Unsigned integer type. */
|
||||||
|
using hash_type = typename base_type::hash_type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns directly the numeric representation of a string view.
|
||||||
|
* @param str Human-readable identifier.
|
||||||
|
* @param len Length of the string to hash.
|
||||||
|
* @return The numeric representation of the string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept {
|
||||||
|
return basic_hashed_string{str, len};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns directly the numeric representation of a string.
|
||||||
|
* @tparam N Number of characters of the identifier.
|
||||||
|
* @param str Human-readable identifier.
|
||||||
|
* @return The numeric representation of the string.
|
||||||
|
*/
|
||||||
|
template<std::size_t N>
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||||
|
[[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept {
|
||||||
|
return basic_hashed_string{str};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns directly the numeric representation of a string.
|
||||||
|
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||||
|
* @return The numeric representation of the string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept {
|
||||||
|
return basic_hashed_string{wrapper};
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @brief Constructs an empty hashed string. */
|
||||||
|
constexpr basic_hashed_string() noexcept
|
||||||
|
: basic_hashed_string{nullptr, 0u} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a hashed string from a string view.
|
||||||
|
* @param str Human-readable identifier.
|
||||||
|
* @param len Length of the string to hash.
|
||||||
|
*/
|
||||||
|
constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept
|
||||||
|
: base_type{helper({str, len})} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a hashed string from an array of const characters.
|
||||||
|
* @tparam N Number of characters of the identifier.
|
||||||
|
* @param str Human-readable identifier.
|
||||||
|
*/
|
||||||
|
template<std::size_t N>
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||||
|
ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept
|
||||||
|
: base_type{helper({static_cast<const value_type *>(str)})} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||||
|
* string directly from a `const value_type *`.
|
||||||
|
*
|
||||||
|
* @warning
|
||||||
|
* The lifetime of the string is not extended nor is it copied.
|
||||||
|
*
|
||||||
|
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||||
|
*/
|
||||||
|
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
|
||||||
|
: base_type{helper({wrapper.repr})} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the size a hashed string.
|
||||||
|
* @return The size of the hashed string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||||
|
return base_type::length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the human-readable representation of a hashed string.
|
||||||
|
* @return The string used to initialize the hashed string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr const value_type *data() const noexcept {
|
||||||
|
return base_type::repr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the numeric representation of a hashed string.
|
||||||
|
* @return The numeric representation of the hashed string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr hash_type value() const noexcept {
|
||||||
|
return base_type::hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc data */
|
||||||
|
[[nodiscard]] constexpr operator const value_type *() const noexcept {
|
||||||
|
return data();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns the numeric representation of a hashed string.
|
||||||
|
* @return The numeric representation of the hashed string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr operator hash_type() const noexcept {
|
||||||
|
return value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deduction guide.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param str Human-readable identifier.
|
||||||
|
* @param len Length of the string to hash.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Char>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Deduction guide.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @tparam N Number of characters of the identifier.
|
||||||
|
* @param str Human-readable identifier.
|
||||||
|
*/
|
||||||
|
template<typename Char, std::size_t N>
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||||
|
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two hashed strings.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param lhs A valid hashed string.
|
||||||
|
* @param rhs A valid hashed string.
|
||||||
|
* @return True if the two hashed strings are identical, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||||
|
return lhs.value() == rhs.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two hashed strings.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param lhs A valid hashed string.
|
||||||
|
* @param rhs A valid hashed string.
|
||||||
|
* @return True if the two hashed strings differ, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two hashed strings.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param lhs A valid hashed string.
|
||||||
|
* @param rhs A valid hashed string.
|
||||||
|
* @return True if the first element is less than the second, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||||
|
return lhs.value() < rhs.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two hashed strings.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param lhs A valid hashed string.
|
||||||
|
* @param rhs A valid hashed string.
|
||||||
|
* @return True if the first element is less than or equal to the second, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||||
|
return !(rhs < lhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two hashed strings.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param lhs A valid hashed string.
|
||||||
|
* @param rhs A valid hashed string.
|
||||||
|
* @return True if the first element is greater than the second, false
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||||
|
return rhs < lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Compares two hashed strings.
|
||||||
|
* @tparam Char Character type.
|
||||||
|
* @param lhs A valid hashed string.
|
||||||
|
* @param rhs A valid hashed string.
|
||||||
|
* @return True if the first element is greater than or equal to the second,
|
||||||
|
* false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Char>
|
||||||
|
[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||||
|
return !(lhs < rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline namespace literals {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief User defined literal for hashed strings.
|
||||||
|
* @param str The literal without its suffix.
|
||||||
|
* @return A properly initialized hashed string.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept {
|
||||||
|
return hashed_string{str};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief User defined literal for hashed wstrings.
|
||||||
|
* @param str The literal without its suffix.
|
||||||
|
* @return A properly initialized hashed wstring.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept {
|
||||||
|
return hashed_wstring{str};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace literals
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef ENTT_CORE_IDENT_HPP
|
||||||
|
#define ENTT_CORE_IDENT_HPP
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
#include "fwd.hpp"
|
||||||
|
#include "type_traits.hpp"
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Type integral identifiers.
|
||||||
|
* @tparam Type List of types for which to generate identifiers.
|
||||||
|
*/
|
||||||
|
template<typename... Type>
|
||||||
|
class ident {
|
||||||
|
template<typename Curr, std::size_t... Index>
|
||||||
|
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) noexcept {
|
||||||
|
static_assert((std::is_same_v<Curr, Type> || ...), "Invalid type");
|
||||||
|
return (0 + ... + (std::is_same_v<Curr, type_list_element_t<Index, type_list<std::decay_t<Type>...>>> ? id_type{Index} : id_type{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Unsigned integer type. */
|
||||||
|
using value_type = id_type;
|
||||||
|
|
||||||
|
/*! @brief Statically generated unique identifier for the given type. */
|
||||||
|
template<typename Curr>
|
||||||
|
static constexpr value_type value = get<std::decay_t<Curr>>(std::index_sequence_for<Type...>{});
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
+197
@@ -0,0 +1,197 @@
|
|||||||
|
#ifndef ENTT_CORE_ITERATOR_HPP
|
||||||
|
#define ENTT_CORE_ITERATOR_HPP
|
||||||
|
|
||||||
|
#include <iterator>
|
||||||
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace entt {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper type to use as pointer with input iterators.
|
||||||
|
* @tparam Type of wrapped value.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
struct input_iterator_pointer final {
|
||||||
|
/*! @brief Value type. */
|
||||||
|
using value_type = Type;
|
||||||
|
/*! @brief Pointer type. */
|
||||||
|
using pointer = Type *;
|
||||||
|
/*! @brief Reference type. */
|
||||||
|
using reference = Type &;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs a proxy object by move.
|
||||||
|
* @param val Value to use to initialize the proxy object.
|
||||||
|
*/
|
||||||
|
constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v<value_type>)
|
||||||
|
: value{std::move(val)} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Access operator for accessing wrapped values.
|
||||||
|
* @return A pointer to the wrapped value.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr pointer operator->() noexcept {
|
||||||
|
return std::addressof(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dereference operator for accessing wrapped values.
|
||||||
|
* @return A reference to the wrapped value.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr reference operator*() noexcept {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Type value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Plain iota iterator (waiting for C++20).
|
||||||
|
* @tparam Type Value type.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
class iota_iterator final {
|
||||||
|
static_assert(std::is_integral_v<Type>, "Not an integral type");
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*! @brief Value type, likely an integral one. */
|
||||||
|
using value_type = Type;
|
||||||
|
/*! @brief Invalid pointer type. */
|
||||||
|
using pointer = void;
|
||||||
|
/*! @brief Non-reference type, same as value type. */
|
||||||
|
using reference = value_type;
|
||||||
|
/*! @brief Difference type. */
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
/*! @brief Iterator category. */
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
|
||||||
|
/*! @brief Default constructor. */
|
||||||
|
constexpr iota_iterator() noexcept
|
||||||
|
: current{} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Constructs an iota iterator from a given value.
|
||||||
|
* @param init The initial value assigned to the iota iterator.
|
||||||
|
*/
|
||||||
|
constexpr iota_iterator(const value_type init) noexcept
|
||||||
|
: current{init} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Pre-increment operator.
|
||||||
|
* @return This iota iterator.
|
||||||
|
*/
|
||||||
|
constexpr iota_iterator &operator++() noexcept {
|
||||||
|
return ++current, *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Post-increment operator.
|
||||||
|
* @return This iota iterator.
|
||||||
|
*/
|
||||||
|
constexpr iota_iterator operator++(int) noexcept {
|
||||||
|
const iota_iterator orig = *this;
|
||||||
|
return ++(*this), orig;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dereference operator.
|
||||||
|
* @return The underlying value.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_type current;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Comparison operator.
|
||||||
|
* @tparam Type Value type of the iota iterator.
|
||||||
|
* @param lhs A properly initialized iota iterator.
|
||||||
|
* @param rhs A properly initialized iota iterator.
|
||||||
|
* @return True if the two iterators are identical, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr bool operator==(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
|
||||||
|
return *lhs == *rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Comparison operator.
|
||||||
|
* @tparam Type Value type of the iota iterator.
|
||||||
|
* @param lhs A properly initialized iota iterator.
|
||||||
|
* @param rhs A properly initialized iota iterator.
|
||||||
|
* @return True if the two iterators differ, false otherwise.
|
||||||
|
*/
|
||||||
|
template<typename Type>
|
||||||
|
[[nodiscard]] constexpr bool operator!=(const iota_iterator<Type> &lhs, const iota_iterator<Type> &rhs) noexcept {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Utility class to create an iterable object from a pair of iterators.
|
||||||
|
* @tparam It Type of iterator.
|
||||||
|
* @tparam Sentinel Type of sentinel.
|
||||||
|
*/
|
||||||
|
template<typename It, typename Sentinel = It>
|
||||||
|
struct iterable_adaptor final {
|
||||||
|
/*! @brief Value type. */
|
||||||
|
using value_type = typename std::iterator_traits<It>::value_type;
|
||||||
|
/*! @brief Iterator type. */
|
||||||
|
using iterator = It;
|
||||||
|
/*! @brief Sentinel type. */
|
||||||
|
using sentinel = Sentinel;
|
||||||
|
|
||||||
|
/*! @brief Default constructor. */
|
||||||
|
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> && std::is_nothrow_default_constructible_v<sentinel>)
|
||||||
|
: first{},
|
||||||
|
last{} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates an iterable object from a pair of iterators.
|
||||||
|
* @param from Begin iterator.
|
||||||
|
* @param to End iterator.
|
||||||
|
*/
|
||||||
|
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> && std::is_nothrow_move_constructible_v<sentinel>)
|
||||||
|
: first{std::move(from)},
|
||||||
|
last{std::move(to)} {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the beginning.
|
||||||
|
* @return An iterator to the first element of the range.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr iterator begin() const noexcept {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns an iterator to the end.
|
||||||
|
* @return An iterator to the element following the last element of the
|
||||||
|
* range.
|
||||||
|
*/
|
||||||
|
[[nodiscard]] constexpr sentinel end() const noexcept {
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc begin */
|
||||||
|
[[nodiscard]] constexpr iterator cbegin() const noexcept {
|
||||||
|
return begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! @copydoc end */
|
||||||
|
[[nodiscard]] constexpr sentinel cend() const noexcept {
|
||||||
|
return end();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
It first;
|
||||||
|
Sentinel last;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace entt
|
||||||
|
|
||||||
|
#endif
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user