Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8de85b961 | ||
|
|
90742a72b0 | ||
|
|
7bbd7d82b0 | ||
|
|
965c440d86 | ||
|
|
6b2aff821c | ||
|
|
84d9125ea1 | ||
|
|
9b969e762c | ||
|
|
2695d48ba7 | ||
|
|
aba2a6b17d | ||
|
|
7b87d17d22 |
@@ -1 +0,0 @@
|
||||
test
|
||||
@@ -1 +0,0 @@
|
||||
USE_BAZEL_VERSION=6.x
|
||||
16
.bazelrc
16
.bazelrc
@@ -1,16 +0,0 @@
|
||||
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
|
||||
@@ -2,11 +2,10 @@ BasedOnStyle: llvm
|
||||
---
|
||||
AccessModifierOffset: -4
|
||||
AlignEscapedNewlines: DontAlign
|
||||
AllowShortBlocksOnASingleLine: Always
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBinaryOperators: NonAssignment
|
||||
@@ -24,7 +23,6 @@ IncludeCategories:
|
||||
Priority: 4
|
||||
- Regex: '.*'
|
||||
Priority: 5
|
||||
IncludeIsMainRegex: "^$"
|
||||
IndentPPDirectives: AfterHash
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
|
||||
58
.clang-tidy
58
.clang-tidy
@@ -1,58 +0,0 @@
|
||||
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-else-after-return,
|
||||
-readability-function-cognitive-complexity,
|
||||
-readability-named-parameter,
|
||||
-readability-redundant-member-init,
|
||||
-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
|
||||
84
.github/workflows/analyzer.yml
vendored
84
.github/workflows/analyzer.yml
vendored
@@ -1,84 +0,0 @@
|
||||
name: analyzer
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- analyzer
|
||||
|
||||
jobs:
|
||||
|
||||
iwyu:
|
||||
timeout-minutes: 60
|
||||
|
||||
env:
|
||||
IWYU: "0.24"
|
||||
LLVM: "20"
|
||||
|
||||
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_TESTBED=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_TESTBED=ON \
|
||||
-DENTT_USE_CLANG_TIDY=ON \
|
||||
..
|
||||
make -j4
|
||||
23
.github/workflows/bazel-release-archive.yml
vendored
23
.github/workflows/bazel-release-archive.yml
vendored
@@ -1,23 +0,0 @@
|
||||
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
.github/workflows/bazel.yml
vendored
23
.github/workflows/bazel.yml
vendored
@@ -1,23 +0,0 @@
|
||||
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
|
||||
141
.github/workflows/build.yml
vendored
141
.github/workflows/build.yml
vendored
@@ -5,29 +5,42 @@ on: [push, pull_request]
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 15
|
||||
|
||||
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 }
|
||||
- pkg: g++-7
|
||||
exe: g++-7
|
||||
- pkg: g++-8
|
||||
exe: g++-8
|
||||
- pkg: g++-9
|
||||
exe: g++-9
|
||||
- pkg: g++-10
|
||||
exe: g++-10
|
||||
- pkg: clang-8
|
||||
exe: clang++-8
|
||||
- pkg: clang-9
|
||||
exe: clang++-9
|
||||
- pkg: clang-10
|
||||
exe: clang++-10
|
||||
- pkg: clang-11
|
||||
exe: clang++-11
|
||||
- pkg: clang-12
|
||||
exe: clang++-12
|
||||
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }}
|
||||
sudo apt install -y ${{ matrix.compiler.pkg }}
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }}
|
||||
CXX: ${{ matrix.compiler.exe }}
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
make -j4
|
||||
@@ -35,25 +48,52 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
linux-extra:
|
||||
timeout-minutes: 15
|
||||
|
||||
windows:
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [default, v142, v143, clang-cl]
|
||||
compiler: [g++, 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@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXX: ${{ matrix.compiler }}
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=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 --timeout 30 -C Debug -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
toolset: [default, v141, v142, clang-cl]
|
||||
include:
|
||||
- toolset: v141
|
||||
toolset_option: -T"v141"
|
||||
- toolset: v142
|
||||
toolset_option: -T"v142"
|
||||
- toolset: v143
|
||||
toolset_option: -T"v143"
|
||||
- toolset: clang-cl
|
||||
toolset_option: -T"ClangCl"
|
||||
|
||||
timeout-minutes: 15
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -63,37 +103,20 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
macos:
|
||||
windows-extra:
|
||||
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 }}
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -103,4 +126,44 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
macos:
|
||||
timeout-minutes: 15
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- 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 --timeout 30 -C Debug -j4
|
||||
|
||||
macos-extra:
|
||||
timeout-minutes: 15
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
id_type: ["std::uint32_t", "std::uint64_t"]
|
||||
cxx_std: [cxx_std_17, cxx_std_20]
|
||||
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=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 --timeout 30 -C Debug -j4
|
||||
|
||||
10
.github/workflows/coverage.yml
vendored
10
.github/workflows/coverage.yml
vendored
@@ -9,11 +9,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
CXXFLAGS: "--coverage -fno-elide-constructors -fno-inline -fno-default-inline"
|
||||
CXXFLAGS: "--coverage -fno-inline"
|
||||
CXX: g++
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON ..
|
||||
@@ -22,15 +22,15 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -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 -c -d . -o coverage.info
|
||||
lcov -l coverage.info
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
uses: codecov/codecov-action@v2
|
||||
with:
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
files: build/coverage.info
|
||||
|
||||
4
.github/workflows/deploy.yml
vendored
4
.github/workflows/deploy.yml
vendored
@@ -2,7 +2,7 @@ name: deploy
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
types: published
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
FORMULA: entt.rb
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- name: Clone repository
|
||||
working-directory: build
|
||||
env:
|
||||
|
||||
4
.github/workflows/sanitizer.yml
vendored
4
.github/workflows/sanitizer.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v2
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
@@ -28,4 +28,4 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
79
.github/workflows/testbed.yml
vendored
79
.github/workflows/testbed.yml
vendored
@@ -1,79 +0,0 @@
|
||||
name: testbed
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
|
||||
linux:
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install required packages
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y \
|
||||
build-essential \
|
||||
git \
|
||||
make \
|
||||
pkg-config \
|
||||
cmake \
|
||||
ninja-build \
|
||||
gnome-desktop-testing \
|
||||
libasound2-dev \
|
||||
libpulse-dev \
|
||||
libaudio-dev \
|
||||
libjack-dev \
|
||||
libsndio-dev \
|
||||
libx11-dev \
|
||||
libxext-dev \
|
||||
libxrandr-dev \
|
||||
libxcursor-dev \
|
||||
libxfixes-dev \
|
||||
libxi-dev \
|
||||
libxss-dev \
|
||||
libxtst-dev \
|
||||
libxkbcommon-dev \
|
||||
libdrm-dev \
|
||||
libgbm-dev \
|
||||
libgl1-mesa-dev \
|
||||
libgles2-mesa-dev \
|
||||
libegl1-mesa-dev \
|
||||
libdbus-1-dev \
|
||||
libibus-1.0-dev \
|
||||
libudev-dev \
|
||||
libpipewire-0.3-dev \
|
||||
libwayland-dev \
|
||||
libdecor-0-dev \
|
||||
liburing-dev
|
||||
- name: Compile testbed
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTBED=ON ..
|
||||
make -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 15
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: seanmiddleditch/gha-setup-ninja@master
|
||||
- name: Compile testbed
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTBED=ON .. -G Ninja
|
||||
cmake --build . -j 4
|
||||
|
||||
macos:
|
||||
timeout-minutes: 15
|
||||
runs-on: macOS-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile testbed
|
||||
working-directory: build
|
||||
run: |
|
||||
cmake -DENTT_BUILD_TESTBED=ON ..
|
||||
make -j4
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -11,6 +11,3 @@ cpp.hint
|
||||
|
||||
# Bazel
|
||||
/bazel-*
|
||||
/test/bazel-*
|
||||
/user.bazelrc
|
||||
*.bazel.lock
|
||||
|
||||
2
AUTHORS
2
AUTHORS
@@ -16,7 +16,6 @@ cugone
|
||||
dbacchet
|
||||
dBagrat
|
||||
djarek
|
||||
DNKpp
|
||||
DonKult
|
||||
drglove
|
||||
eliasdaler
|
||||
@@ -42,7 +41,6 @@ Oortonaut
|
||||
Paolo-Oliverio
|
||||
pgruenbacher
|
||||
prowolf
|
||||
Qix-
|
||||
stefanofiorentino
|
||||
suVrik
|
||||
szunhammer
|
||||
|
||||
14
BUILD.bazel
14
BUILD.bazel
@@ -1,6 +1,14 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
_msvc_copts = ["/std:c++17"]
|
||||
_gcc_copts = ["-std=c++17"]
|
||||
|
||||
alias(
|
||||
cc_library(
|
||||
name = "entt",
|
||||
actual = "//src:entt",
|
||||
visibility = ["//visibility:public"],
|
||||
strip_include_prefix = "src",
|
||||
hdrs = glob(["src/**/*.h", "src/**/*.hpp"]),
|
||||
copts = select({
|
||||
"@bazel_tools//src/conditions:windows": _msvc_copts,
|
||||
"@bazel_tools//src/conditions:windows_msvc": _msvc_copts,
|
||||
"//conditions:default": _gcc_copts,
|
||||
}),
|
||||
)
|
||||
|
||||
459
CMakeLists.txt
459
CMakeLists.txt
@@ -1,15 +1,21 @@
|
||||
#
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.15.7)
|
||||
cmake_minimum_required(VERSION 3.12.4)
|
||||
|
||||
#
|
||||
# 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
|
||||
@@ -22,21 +28,18 @@ project(
|
||||
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 "* Copyright (c) 2017-2022 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)
|
||||
@@ -56,7 +59,7 @@ if(ENTT_USE_LIBCPP)
|
||||
endif()
|
||||
|
||||
if(NOT ENTT_HAS_LIBCPP)
|
||||
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available.")
|
||||
message(VERBOSE "The option ENTT_USE_LIBCPP is set but libc++ is not available. The flag will not be added to the target.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -67,19 +70,27 @@ if(ENTT_USE_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.")
|
||||
message(VERBOSE "The option ENTT_USE_SANITIZER is set but sanitizer support is not available. The flags will not be added to the target.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# Add EnTT target
|
||||
#
|
||||
|
||||
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()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
@@ -95,8 +106,98 @@ target_include_directories(
|
||||
|
||||
target_compile_features(EnTT INTERFACE cxx_std_17)
|
||||
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
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/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/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/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/helper.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.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/sigh_storage_mixin.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/utility.hpp>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.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/ctx.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/platform/android-ndk-r17.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/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/locator.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
|
||||
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.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()
|
||||
|
||||
if(ENTT_HAS_SANITIZER)
|
||||
@@ -104,255 +205,125 @@ if(ENTT_HAS_SANITIZER)
|
||||
target_link_libraries(EnTT INTERFACE $<$<CONFIG:Debug>:-fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined>)
|
||||
endif()
|
||||
|
||||
if(ENTT_CLANG_TIDY_EXECUTABLE)
|
||||
set(ENTT_CLANG_TIDY_OPTIONS ";--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*")
|
||||
|
||||
if(MSVC AND NOT (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))
|
||||
set(ENTT_CLANG_TIDY_OPTIONS "${ENTT_CLANG_TIDY_OPTIONS};--extra-arg=/EHsc;--extra-arg=/wd4996")
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE}${ENTT_CLANG_TIDY_OPTIONS}")
|
||||
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_HEADERS)
|
||||
set(
|
||||
HEADERS_FILES
|
||||
config/config.h
|
||||
config/macro.h
|
||||
config/version.h
|
||||
container/dense_map.hpp
|
||||
container/dense_set.hpp
|
||||
container/table.hpp
|
||||
container/fwd.hpp
|
||||
core/algorithm.hpp
|
||||
core/any.hpp
|
||||
core/bit.hpp
|
||||
core/compressed_pair.hpp
|
||||
core/enum.hpp
|
||||
core/family.hpp
|
||||
core/fwd.hpp
|
||||
core/hashed_string.hpp
|
||||
core/ident.hpp
|
||||
core/iterator.hpp
|
||||
core/memory.hpp
|
||||
core/monostate.hpp
|
||||
core/ranges.hpp
|
||||
core/tuple.hpp
|
||||
core/type_info.hpp
|
||||
core/type_traits.hpp
|
||||
core/utility.hpp
|
||||
entity/component.hpp
|
||||
entity/entity.hpp
|
||||
entity/fwd.hpp
|
||||
entity/group.hpp
|
||||
entity/handle.hpp
|
||||
entity/mixin.hpp
|
||||
entity/helper.hpp
|
||||
entity/organizer.hpp
|
||||
entity/ranges.hpp
|
||||
entity/registry.hpp
|
||||
entity/runtime_view.hpp
|
||||
entity/snapshot.hpp
|
||||
entity/sparse_set.hpp
|
||||
entity/storage.hpp
|
||||
entity/view.hpp
|
||||
graph/adjacency_matrix.hpp
|
||||
graph/dot.hpp
|
||||
graph/flow.hpp
|
||||
graph/fwd.hpp
|
||||
locator/locator.hpp
|
||||
meta/adl_pointer.hpp
|
||||
meta/container.hpp
|
||||
meta/context.hpp
|
||||
meta/factory.hpp
|
||||
meta/fwd.hpp
|
||||
meta/meta.hpp
|
||||
meta/node.hpp
|
||||
meta/pointer.hpp
|
||||
meta/policy.hpp
|
||||
meta/range.hpp
|
||||
meta/resolve.hpp
|
||||
meta/template.hpp
|
||||
meta/type_traits.hpp
|
||||
meta/utility.hpp
|
||||
poly/fwd.hpp
|
||||
poly/poly.hpp
|
||||
process/fwd.hpp
|
||||
process/process.hpp
|
||||
process/scheduler.hpp
|
||||
resource/cache.hpp
|
||||
resource/fwd.hpp
|
||||
resource/loader.hpp
|
||||
resource/resource.hpp
|
||||
signal/delegate.hpp
|
||||
signal/dispatcher.hpp
|
||||
signal/emitter.hpp
|
||||
signal/fwd.hpp
|
||||
signal/sigh.hpp
|
||||
tools/davey.hpp
|
||||
entt.hpp
|
||||
fwd.hpp
|
||||
tools.hpp
|
||||
)
|
||||
|
||||
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_BUILD_INTERFACE)
|
||||
list(TRANSFORM HEADERS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/")
|
||||
|
||||
list(TRANSFORM HEADERS_FILES APPEND ">" OUTPUT_VARIABLE HEADERS_INSTALL_INTERFACE)
|
||||
list(TRANSFORM HEADERS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/")
|
||||
|
||||
target_sources(EnTT INTERFACE ${HEADERS_BUILD_INTERFACE} ${HEADERS_INSTALL_INTERFACE})
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
endif()
|
||||
|
||||
if(ENTT_INCLUDE_NATVIS)
|
||||
if(MSVC)
|
||||
set(ENTT_HAS_NATVIS TRUE CACHE BOOL "" FORCE)
|
||||
mark_as_advanced(ENTT_HAS_NATVIS)
|
||||
endif()
|
||||
#
|
||||
# Install pkg-config file
|
||||
#
|
||||
|
||||
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()
|
||||
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
|
||||
|
||||
if(ENTT_HAS_NATVIS)
|
||||
set(
|
||||
NATVIS_FILES
|
||||
config.natvis
|
||||
container.natvis
|
||||
core.natvis
|
||||
entity.natvis
|
||||
graph.natvis
|
||||
locator.natvis
|
||||
meta.natvis
|
||||
poly.natvis
|
||||
process.natvis
|
||||
resource.natvis
|
||||
signal.natvis
|
||||
)
|
||||
configure_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
|
||||
${EnTT_PKGCONFIG}
|
||||
@ONLY
|
||||
)
|
||||
|
||||
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_BUILD_INTERFACE)
|
||||
list(TRANSFORM NATVIS_BUILD_INTERFACE PREPEND "$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/natvis/")
|
||||
install(
|
||||
FILES ${EnTT_PKGCONFIG}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
list(TRANSFORM NATVIS_FILES APPEND ">" OUTPUT_VARIABLE NATVIS_INSTALL_INTERFACE)
|
||||
list(TRANSFORM NATVIS_INSTALL_INTERFACE PREPEND "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/entt/natvis/")
|
||||
#
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
target_sources(EnTT INTERFACE ${NATVIS_BUILD_INTERFACE} ${NATVIS_INSTALL_INTERFACE})
|
||||
endif()
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
# Install EnTT and all related files
|
||||
install(
|
||||
TARGETS EnTT
|
||||
EXPORT EnTTTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
option(ENTT_INSTALL "Install EnTT and all related files." OFF)
|
||||
write_basic_package_version_file(
|
||||
EnTTConfigVersion.cmake
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
if(ENTT_INSTALL)
|
||||
# Install pkg-config file
|
||||
configure_package_config_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/EnTTConfig.cmake.in
|
||||
EnTTConfig.cmake
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
include(JoinPaths)
|
||||
export(
|
||||
EXPORT EnTTTargets
|
||||
FILE ${CMAKE_CURRENT_BINARY_DIR}/EnTTTargets.cmake
|
||||
NAMESPACE EnTT::
|
||||
)
|
||||
|
||||
set(EnTT_PKGCONFIG ${CMAKE_CURRENT_BINARY_DIR}/entt.pc)
|
||||
install(
|
||||
EXPORT EnTTTargets
|
||||
FILE EnTTTargets.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
NAMESPACE EnTT::
|
||||
)
|
||||
|
||||
join_paths(EnTT_PKGCONFIG_INCLUDEDIR "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
install(
|
||||
FILES
|
||||
${PROJECT_BINARY_DIR}/EnTTConfig.cmake
|
||||
${PROJECT_BINARY_DIR}/EnTTConfigVersion.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
|
||||
)
|
||||
|
||||
configure_file(
|
||||
${EnTT_SOURCE_DIR}/cmake/in/entt.pc.in
|
||||
${EnTT_PKGCONFIG}
|
||||
@ONLY
|
||||
)
|
||||
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
install(
|
||||
FILES ${EnTT_PKGCONFIG}
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
export(PACKAGE EnTT)
|
||||
|
||||
# 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"
|
||||
PATTERN "*.natvis"
|
||||
)
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
endif()
|
||||
|
||||
# Tests and testbed
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||
option(ENTT_BUILD_TESTBED "Enable building testbed." OFF)
|
||||
|
||||
if(ENTT_BUILD_TESTING OR ENTT_BUILD_TESTBED)
|
||||
set(ENTT_ID_TYPE std::uint32_t CACHE STRING "Type of identifiers to use for tests and testbed")
|
||||
set(ENTT_CXX_STD cxx_std_17 CACHE STRING "C++ standard revision to use for tests and testbed")
|
||||
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)
|
||||
|
||||
# Tests and tesetbed do not work together because SDL gets confused with EnTT tests
|
||||
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)
|
||||
|
||||
include(CTest)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
elseif(ENTT_BUILD_TESTBED)
|
||||
add_subdirectory(testbed)
|
||||
endif()
|
||||
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)
|
||||
find_package(Doxygen 1.8)
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
add_subdirectory(docs)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
#
|
||||
# AOB
|
||||
#
|
||||
|
||||
add_custom_target(
|
||||
aob
|
||||
SOURCES
|
||||
.github/workflows/build.yml
|
||||
.github/workflows/coverage.yml
|
||||
.github/workflows/deploy.yml
|
||||
.github/workflows/sanitizer.yml
|
||||
.github/FUNDING.yml
|
||||
AUTHORS
|
||||
CONTRIBUTING.md
|
||||
LICENSE
|
||||
README.md
|
||||
TODO
|
||||
)
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2025 Michele Caini, author of EnTT
|
||||
Copyright (c) 2017-2022 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
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
module(name = "entt")
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.0.8")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.4.2")
|
||||
121
README.md
121
README.md
@@ -1,18 +1,18 @@
|
||||

|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
[](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)
|
||||
[](http://entt.docsforge.com/)
|
||||
[](https://gitter.im/skypjack/entt)
|
||||
[](https://discord.gg/5BjPWBd)
|
||||
[](https://www.paypal.me/skypjack)
|
||||
|
||||
> `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/>
|
||||
@@ -21,7 +21,7 @@ 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:
|
||||
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
|
||||
|
||||
---
|
||||
|
||||
@@ -36,8 +36,7 @@ Don't forget to check the
|
||||
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/>
|
||||
[**sponsor**](https://github.com/users/skypjack/sponsorship).
|
||||
Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
**special** thanks to:
|
||||
|
||||
@@ -49,7 +48,7 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
* [Introduction](#introduction)
|
||||
* [Code Example](#code-example)
|
||||
* [Motivation](#motivation)
|
||||
* [Benchmark](#benchmark)
|
||||
* [Performance](#performance)
|
||||
* [Integration](#integration)
|
||||
* [Requirements](#requirements)
|
||||
* [CMake](#cmake)
|
||||
@@ -61,6 +60,9 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
|
||||
* [EnTT in Action](#entt-in-action)
|
||||
* [Contributors](#contributors)
|
||||
* [License](#license)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -76,16 +78,14 @@ 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**.
|
||||
* 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.
|
||||
use_ policy.
|
||||
* 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.
|
||||
@@ -97,7 +97,7 @@ Here is a brief, yet incomplete list of what it offers today:
|
||||
* 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
|
||||
Consider these lists 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.
|
||||
@@ -170,28 +170,37 @@ 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
|
||||
## Performance
|
||||
|
||||
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.
|
||||
The proposed entity-component system is incredibly fast to iterate entities and
|
||||
components, this is a fact. Some compilers make a lot of optimizations because
|
||||
of how `EnTT` works, some others aren't that good. In general, if we consider
|
||||
real world cases, `EnTT` is somewhere between a bit and much faster than many of
|
||||
the other solutions around, although I couldn't check them all for obvious
|
||||
reasons.
|
||||
|
||||
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
|
||||
Honestly I got tired of updating the README file whenever there is an
|
||||
improvement.<br/>
|
||||
There are already 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.
|
||||
when the library they are dealing with is actively developed.
|
||||
|
||||
The choice to use `EnTT` should be based on its carefully designed API, its
|
||||
set of features and the general performance, **not** because some single
|
||||
benchmark shows it to be the fastest tool available.
|
||||
|
||||
In the future I'll likely try to get even better performance while still adding
|
||||
new features, mainly for fun.<br/>
|
||||
If you want to contribute and/or have suggestions, feel free to make a PR or
|
||||
open an issue to discuss your idea.
|
||||
|
||||
# Integration
|
||||
|
||||
@@ -232,26 +241,20 @@ build system of the library.
|
||||
## CMake
|
||||
|
||||
To use `EnTT` from a `CMake` project, just link an existing target to the
|
||||
`EnTT::EnTT` alias.
|
||||
|
||||
`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.
|
||||
|
||||
Note that all `install` calls are guarded by the `ENTT_INSTALL` option to allow
|
||||
using `EnTT` as a submodule without conflicting with user logic.<br/>
|
||||
It is therefore necessary to set the option to true to take advantage of the
|
||||
installation logic provided by this library.
|
||||
Covering all possible cases would require a treaty 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` subdirectory, divided by module.<br/>
|
||||
in the `natvis` directory, divided by module.<br/>
|
||||
If you spot errors or have suggestions, any contribution is welcome!
|
||||
|
||||
## Packaging Tools
|
||||
@@ -319,18 +322,6 @@ If you spot errors or have suggestions, any contribution is welcome!
|
||||
[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
|
||||
@@ -348,22 +339,32 @@ The documentation is based on [doxygen](http://www.doxygen.nl/). To build it:
|
||||
$ 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:
|
||||
The API reference will be created in HTML format within the directory
|
||||
`build/docs/html`. To navigate it with your favorite browser:
|
||||
|
||||
$ cd build
|
||||
$ your_favorite_browser docs/html/index.html
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
The same version is also available [online](https://skypjack.github.io/entt/)
|
||||
for the latest release, that is the last stable tag.<br/>
|
||||
for the latest release, that is the last stable tag. If you are looking for
|
||||
something more pleasing to the eye, consider reading the nice-looking version
|
||||
available on [docsforge](https://entt.docsforge.com/): same documentation, much
|
||||
more pleasant to read.<br/>
|
||||
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
|
||||
to the project where users can find all related documentation pages.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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`.
|
||||
`cmake` will download and compile 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:
|
||||
|
||||
@@ -374,6 +375,9 @@ To build the most basic set of tests:
|
||||
|
||||
Note that benchmarks are not part of this set.
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# EnTT in Action
|
||||
|
||||
`EnTT` is widely used in private and commercial applications. I cannot even
|
||||
@@ -391,7 +395,7 @@ 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.
|
||||
Requests for features, PRs, suggestions ad 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
|
||||
@@ -401,15 +405,18 @@ 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/>
|
||||
[contributing](CONTRIBUTING.md) before to create issues or pull
|
||||
requests.<br/>
|
||||
Take also a look at the
|
||||
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
|
||||
know who has participated so far.
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# License
|
||||
|
||||
Code and documentation Copyright (c) 2017-2025 Michele Caini.<br/>
|
||||
Code and documentation Copyright (c) 2017-2022 Michele Caini.<br/>
|
||||
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
|
||||
|
||||
Code released under
|
||||
|
||||
53
TODO
53
TODO
@@ -1,37 +1,26 @@
|
||||
* debugging tools (#60): the issue online already contains interesting tips on this, look at it
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* add examples (and credits) from @alanjfs :)
|
||||
|
||||
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
|
||||
WIP:
|
||||
* view/group: no storage_traits dependency -> use storage instead of components for the definition
|
||||
* basic_storage::bind for cross-registry setups
|
||||
* uses-allocator construction: any (with allocator support), poly, ...
|
||||
* process scheduler: reviews, use free lists internally
|
||||
* iterator based try_emplace vs try_insert for perf reasons
|
||||
* dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
|
||||
* entity-only and exclude-only views
|
||||
* custom allocators all over (sigh storage mixin, registry, ...)
|
||||
* consider removing ENTT_NOEXCEPT, use ENTT_NOEXCEPT_IF (or noexcept(...)) as appropriate in any case (ie make compressed_pair conditionally noexcept)
|
||||
* add test for maximum number of entities reached
|
||||
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
* 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)
|
||||
* review build process for testbed (i.e. tests first due to SDL)
|
||||
* use unique_ptr or any for meta_custom_node
|
||||
* paged vector as a standalone class
|
||||
* resource: shared_from_this?
|
||||
* finish the imgui viewer/editor!
|
||||
* archetype-like a-là EnTT support (see my own notes)
|
||||
* meta: conversion_helper machinery has lot of room for improvements
|
||||
* organizer: view/storage only based model, no registry
|
||||
WIP:
|
||||
* add user data to type_info
|
||||
* write documentation for custom storages and views!!
|
||||
* make runtime views use opaque storage and therefore return also elements.
|
||||
* entity-aware observer, add observer functions aside observer class
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
# SEE MODULE.bazel
|
||||
@@ -1,13 +0,0 @@
|
||||
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",
|
||||
],
|
||||
})
|
||||
@@ -1,5 +1,5 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=@EnTT_PKGCONFIG_INCLUDEDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: EnTT
|
||||
Description: Gaming meets modern C++
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# 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()
|
||||
@@ -15,7 +15,7 @@ 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 ...
|
||||
// gets only the components that are going to be used ...
|
||||
|
||||
auto &vel = view.get<velocity>(entity);
|
||||
|
||||
@@ -28,7 +28,7 @@ void update(entt::registry ®istry) {
|
||||
|
||||
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 ...
|
||||
// gets all the components of the view at once ...
|
||||
|
||||
pos.x += vel.dx * dt;
|
||||
pos.y += vel.dy * dt;
|
||||
|
||||
@@ -1,59 +1,40 @@
|
||||
#
|
||||
# Doxygen configuration (documentation)
|
||||
#
|
||||
|
||||
find_package(Doxygen 1.13)
|
||||
set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps)
|
||||
set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src)
|
||||
set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if(DOXYGEN_FOUND)
|
||||
include(FetchContent)
|
||||
configure_file(doxy.in doxy.cfg @ONLY)
|
||||
|
||||
FetchContent_Declare(
|
||||
doxygen-awesome-css
|
||||
GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css
|
||||
GIT_TAG main
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
add_custom_target(
|
||||
docs ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxy.cfg
|
||||
WORKING_DIRECTORY ${EnTT_SOURCE_DIR}
|
||||
VERBATIM
|
||||
SOURCES
|
||||
dox/extra.dox
|
||||
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
|
||||
)
|
||||
|
||||
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()
|
||||
install(
|
||||
DIRECTORY ${DOXY_OUTPUT_DIRECTORY}/html
|
||||
DESTINATION share/${PROJECT_NAME}-${PROJECT_VERSION}/
|
||||
)
|
||||
|
||||
5
docs/dox/extra.dox
Normal file
5
docs/dox/extra.dox
Normal file
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @namespace entt
|
||||
*
|
||||
* @brief `EnTT` default namespace.
|
||||
*/
|
||||
1141
docs/doxy.in
1141
docs/doxy.in
File diff suppressed because it is too large
Load Diff
@@ -1,53 +1,57 @@
|
||||
# Crash Course: configuration
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Definitions](#definitions)
|
||||
* [ENTT_NOEXCEPTION](#entt_noexception)
|
||||
* [ENTT_NOEXCEPTION](#entt_noexcept)
|
||||
* [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)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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/>
|
||||
`EnTT` doesn't offer many hooks for customization but it certainly offers
|
||||
some.<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.
|
||||
with which it's 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
|
||||
Each parameter can result in internal library definitions. It's 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.
|
||||
This parameter can be used to switch off exception handling in `EnTT`.<br/>
|
||||
To do this, simply define the variable without assigning any value to it. This
|
||||
is roughly equivalent to setting the compiler flag `-ff-noexceptions`.
|
||||
|
||||
## ENTT_USE_ATOMIC
|
||||
|
||||
In general, `EnTT` does not offer primitives to support multi-threading. Many of
|
||||
In general, `EnTT` doesn't 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 internal static data shared between threads should be atomic when
|
||||
using `EnTT` from multiple threads, even when dealing with local storage. Define
|
||||
this macro without assigning any value to it to get the job done.
|
||||
the user is the only one who knows if and when a synchronization point is
|
||||
required.<br/>
|
||||
However, some features aren't easily accessible to users and can be made
|
||||
thread-safe by means of this definition.
|
||||
|
||||
## ENTT_ID_TYPE
|
||||
|
||||
@@ -58,51 +62,42 @@ 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/>
|
||||
It's 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
|
||||
users can adjust it if appropriate. In all case, 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/>
|
||||
Similar to sparse arrays, packed arrays of components are paginated as well. In
|
||||
However, int this case the aim isn't 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
|
||||
users can adjust it if appropriate. In all case, the chosen value **must** be a
|
||||
power of 2.
|
||||
|
||||
## ENTT_ASSERT
|
||||
|
||||
For performance reasons, `EnTT` does not use exceptions or any other control
|
||||
For performance reasons, `EnTT` doesn't 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`.
|
||||
detecting errors in debug builds. By default, it uses `assert` internally, but
|
||||
users are allowed to overwrite its behavior by setting this variable.
|
||||
|
||||
### 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_ASSERT` is 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
|
||||
`ENTT_ASSERT` 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/>
|
||||
never 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.
|
||||
|
||||
@@ -110,7 +105,7 @@ dedicated storage for them.
|
||||
|
||||
`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/>
|
||||
This definition will prevent the library from using non-standard techniques,
|
||||
that is, functionalities that aren't 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.
|
||||
|
||||
@@ -1,30 +1,33 @@
|
||||
# Crash Course: containers
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Containers](#containers)
|
||||
* [Dense map](#dense-map)
|
||||
* [Dense set](#dense-set)
|
||||
* [Adaptors](#adaptors)
|
||||
* [Table](#table)
|
||||
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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 standard C++ library offers a wide range of containers and it's really
|
||||
difficult to do better (although it's very easy to do worse, as many examples
|
||||
available online demonstrate).<br/>
|
||||
`EnTT` doesn't 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.
|
||||
However, the library also tries to fill a gap in features and functionality by
|
||||
making available some containers 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.
|
||||
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
|
||||
For all containers made available, full test coverage and stability over time is
|
||||
guaranteed as usual.
|
||||
|
||||
# Containers
|
||||
|
||||
@@ -37,7 +40,7 @@ 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/>
|
||||
`std::unordered_map`.<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/>
|
||||
@@ -60,26 +63,5 @@ 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.
|
||||
library, that is, `std::unordered_set`.<br/>
|
||||
Therefore, there is no need to go into the API description.
|
||||
|
||||
550
docs/md/core.md
550
docs/md/core.md
@@ -1,22 +1,21 @@
|
||||
# Crash Course: core functionalities
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# 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)
|
||||
* [Wide characters](wide-characters)
|
||||
* [Conflicts](#conflicts)
|
||||
* [Iterators](#iterators)
|
||||
* [Input iterator pointer](#input-iterator-pointer)
|
||||
* [Iota iterator](#iota-iterator)
|
||||
* [Iterable adaptor](#iterable-adaptor)
|
||||
* [Memory](#memory)
|
||||
* [Power of two and fast modulus](#power-of-two-and-fast-modulus)
|
||||
* [Allocator aware unique pointers](#allocator-aware-unique-pointers)
|
||||
* [Monostate](#monostate)
|
||||
* [Type support](#type-support)
|
||||
@@ -28,7 +27,6 @@
|
||||
* [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)
|
||||
@@ -36,28 +34,32 @@
|
||||
* [Compile-time generator](#compile-time-generator)
|
||||
* [Runtime generator](#runtime-generator)
|
||||
* [Utilities](#utilities)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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.
|
||||
of the library itself.<br/>
|
||||
Hardly users will include these features in their code, but it's worth
|
||||
describing what `EnTT` offers so as not to reinvent the wheel in case of need.
|
||||
|
||||
# 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/>
|
||||
`EnTT` comes with 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.
|
||||
`std::type_info`, an implementation defined class that's not something everyone
|
||||
wants to see in a software. Furthermore, there is no way to connect it with the
|
||||
type system of the library and therefore with its integrated RTTI support.<br/>
|
||||
Note that this class is largely used internally by the library itself.
|
||||
|
||||
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.
|
||||
The 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 of `any` 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:
|
||||
|
||||
@@ -68,29 +70,26 @@ 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()};
|
||||
// in place construction
|
||||
entt::any in_place{std::in_place_type<int>, 42};
|
||||
```
|
||||
|
||||
Alternatively, the `make_any` function serves the same purpose. It requires to
|
||||
always be explicit about the type and does not support taking ownership:
|
||||
Alternatively, the `make_any` function serves the same purpose but requires to
|
||||
always be explicit about the type:
|
||||
|
||||
```cpp
|
||||
entt::any any = entt::make_any<int>(42);
|
||||
```
|
||||
|
||||
In all cases, the `any` class takes the burden of destroying the contained
|
||||
In both 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
|
||||
Furthermore, an instance of `any` isn't tied to an actual type. Therefore, the
|
||||
wrapper is reconfigured when it's 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
|
||||
There exists 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
|
||||
@@ -104,24 +103,24 @@ any.assign(value);
|
||||
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.
|
||||
The `any` class will also perform 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 to indicate the
|
||||
success or failure of the operation.
|
||||
|
||||
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:
|
||||
When in doubt about the type of object contained, the `type` member function of
|
||||
`any` returns a const reference to the `type_info` associated with its element,
|
||||
or `type_id<void>()` if the container is empty. 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/>
|
||||
In this case, before proceeding with a comparison, it's 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.
|
||||
`type_info` works and on 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:
|
||||
@@ -138,9 +137,9 @@ 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
|
||||
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
|
||||
Similarly, it's possible to create non-owning copies of `any` from an existing
|
||||
object:
|
||||
|
||||
```cpp
|
||||
@@ -148,22 +147,25 @@ object:
|
||||
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.
|
||||
In this case, it doesn't matter if the original container actually holds an
|
||||
object or acts already as a reference for unmanaged elements, the new instance
|
||||
thus created won't create copies and will only serve as a reference for the
|
||||
original item.<br/>
|
||||
This means that, starting from the example above, both `ref` and `other` will
|
||||
point to the same object, whether it's initially contained in `other` or already
|
||||
an unmanaged element.
|
||||
|
||||
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/>
|
||||
As a side note, it's 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.
|
||||
`any` that wraps a const reference will return 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.
|
||||
The only difference is that, in the case of `EnTT`, these won't 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
|
||||
|
||||
@@ -181,39 +183,31 @@ 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).
|
||||
In other terms, if the size is 0, `any` avoids the use of any optimization and
|
||||
always dynamically allocates objects (except for aliasing cases).
|
||||
|
||||
Note that the size of the internal storage as well as the alignment requirements
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
|
||||
## 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:
|
||||
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.
|
||||
|
||||
The alignment requirement 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.
|
||||
Note that the alignment requirements as well as the size of the internal storage
|
||||
are directly part of the type and therefore contribute to define different types
|
||||
that won't be able to interoperate with each other.
|
||||
|
||||
# Compressed pair
|
||||
|
||||
@@ -226,29 +220,30 @@ 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:
|
||||
no` entt::make_compressed_pair`), the major difference is that `first` and
|
||||
`second` are functions for implementation needs:
|
||||
|
||||
```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.
|
||||
There isn't much to describe then. It's recommended to rely on documentation and
|
||||
intuition. At the end of the day, it's 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.
|
||||
Sometimes it's useful to be able to use enums as bitmasks. However, enum classes
|
||||
aren't really suitable for the purpose out of the box. Main problem is that they
|
||||
don't convert implicitly to their underlying type.<br/>
|
||||
All that remains is to make a choice between using old-fashioned enums (with all
|
||||
their problems that I don't 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):
|
||||
scope to treat enum classes as bitmask transparently.<br/>
|
||||
The ultimate goal is to be able 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 {
|
||||
@@ -261,11 +256,11 @@ 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/>
|
||||
The problem with adding all operators to the global scope is that these will
|
||||
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
|
||||
library requires users to register all enum classes for which bitmask support
|
||||
should be enabled:
|
||||
|
||||
```cpp
|
||||
@@ -276,7 +271,7 @@ struct entt::enum_as_bitmask<my_flag>
|
||||
```
|
||||
|
||||
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
|
||||
and over which the users have no control. However, it's also verbose and can be
|
||||
avoided by adding a specific value to the enum class itself:
|
||||
|
||||
```cpp
|
||||
@@ -289,21 +284,23 @@ enum class my_flag {
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
`EnTT` will automatically detect the flag and enable the bitmask support.<br/>
|
||||
Once the enum class has been registered (in one way or the other) all the most
|
||||
common operators will be available, such as `&`, `|` but also `&=` and `|=`.
|
||||
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/>
|
||||
A hashed string is a zero overhead unique identifier. Users can use
|
||||
human-readable identifiers in the codebase while using their numeric
|
||||
counterparts 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.
|
||||
characters. Once created, all what one can do with it is getting back the
|
||||
original string through the `data` member function or converting the instance
|
||||
into a number.<br/>
|
||||
The good part is that a hashed string can be used wherever a constant expression
|
||||
is required and no _string-to-number_ conversion will take place at runtime if
|
||||
used carefully.
|
||||
|
||||
Example of use:
|
||||
|
||||
@@ -316,18 +313,19 @@ 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_:
|
||||
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:
|
||||
To use it, remember that all 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/>
|
||||
Finally, in case users need to create hashed strings at runtime, this class also
|
||||
offers the necessary functionalities:
|
||||
|
||||
```cpp
|
||||
std::string orig{"text"};
|
||||
@@ -339,15 +337,17 @@ entt::hashed_string str{orig.c_str()};
|
||||
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
|
||||
This possibility shouldn't be exploited in tight loops, since the computation
|
||||
takes place at runtime and no longer at compile-time and could therefore impact
|
||||
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/>
|
||||
The hashed string has a design that is close to that of an `std::basic_string`.
|
||||
It means that `hashed_string` is nothing more than an alias for
|
||||
`basic_hashed_string<char>`. For those who want to use the C++ type for wide
|
||||
character representation, 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`:
|
||||
|
||||
@@ -355,118 +355,63 @@ fly is `_hws`:
|
||||
constexpr auto str = L"text"_hws;
|
||||
```
|
||||
|
||||
The hash type of `hashed_wstring` is the same as its counterpart.
|
||||
Note that the hash type of the `hashed_wstring` is the same of 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/>
|
||||
The hashed string class uses internally FNV-1a to compute the numeric
|
||||
counterpart of a string. 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
|
||||
hashing functions. In this case, the best solution seemed to be to give up.
|
||||
That's all.<br/>
|
||||
After all, human-readable unique identifiers aren't 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
|
||||
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.
|
||||
allocator aware containers. Others, on the other hand, 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
|
||||
I won't describe them here in detail. Instead, I recommend reading the inline
|
||||
documentation to those interested in the subject.
|
||||
|
||||
## Power of two and fast modulus
|
||||
|
||||
Finding out if a number is a power of two (`is_power_of_two`) or what the next
|
||||
power of two is given a random value (`next_power_of_two`) is very useful at
|
||||
times.<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.
|
||||
|
||||
## 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
|
||||
support allocators while unique pointers don't.<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<allocator_type>> ptr = entt::allocate_unique<my_type>(allocator, arguments);
|
||||
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
|
||||
@@ -476,16 +421,13 @@ 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.
|
||||
configuration system. This is exactly its purpose in `EnTT`. Moreover, this
|
||||
implementation is thread safe by design (hopefully).<br/>
|
||||
Keys are represented by hashed strings, values are basic types like `int`s or
|
||||
`bool`s. Values of different types can be associated to each key, even more than
|
||||
one at a time. Because of this, users must pay attention to use the same type
|
||||
both during an assignment and when they try to read back their data. Otherwise,
|
||||
they will probably incur in unexpected results.
|
||||
|
||||
Example of use:
|
||||
|
||||
@@ -509,9 +451,9 @@ library or that will never be.
|
||||
|
||||
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
|
||||
of the reasons for this, it's 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
|
||||
The library tries to fill this gap by offering a built-in system that doesn't
|
||||
serve as a replacement but comes very close to being one and offers similar
|
||||
information to that provided by its counterpart.
|
||||
|
||||
@@ -523,10 +465,17 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
auto index = entt::type_index<a_type>::value();
|
||||
```
|
||||
|
||||
The returned value is not guaranteed to be stable across different runs.<br/>
|
||||
The returned value isn't guaranteed to be stable across different runs.
|
||||
However, it can be very useful as index in associative and unordered
|
||||
associative containers or for positional accesses in a vector or an array.
|
||||
|
||||
So as not to conflict with the other tools available, the `family` class isn't
|
||||
used to generate these indexes. Therefore, the numeric identifiers returned by
|
||||
the two tools may differ.<br/>
|
||||
On the other hand, this leaves users with full powers over the `family` class
|
||||
and therefore the generation of custom runtime sequences of indices for their
|
||||
own purposes, if necessary.
|
||||
|
||||
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:
|
||||
@@ -534,13 +483,13 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
```cpp
|
||||
template<typename Type>
|
||||
struct entt::type_index<Type, std::void_d<decltype(Type::index())>> {
|
||||
static entt::id_type value() noexcept {
|
||||
static entt::id_type value() ENTT_NOEXCEPT {
|
||||
return Type::index();
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
Indexes **must** be sequentially generated in this case.<br/>
|
||||
Note that indexes **must** still be generated sequentially 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.
|
||||
|
||||
@@ -551,20 +500,20 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
```
|
||||
|
||||
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).
|
||||
but this isn't guaranteed for all compilers and platforms (although it's 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
|
||||
In all cases, 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.
|
||||
As for `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:
|
||||
|
||||
@@ -572,9 +521,10 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
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/>
|
||||
The name associated with a type 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 isn't
|
||||
available.<br/>
|
||||
For example, given the following class:
|
||||
|
||||
```cpp
|
||||
@@ -585,25 +535,26 @@ Basically, the whole system relies on a handful of classes. In particular:
|
||||
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.
|
||||
modify it as needed, for example by removing the word `struct` to standardize
|
||||
the result. `EnTT` won't do this for obvious reasons, since it requires
|
||||
copying and creating a new string potentially at runtime.
|
||||
|
||||
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.
|
||||
purposes. Users can prevent the library from using non-standard features by
|
||||
means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be
|
||||
empty by default.
|
||||
|
||||
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.
|
||||
As for `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.
|
||||
similar to that offered by the language.
|
||||
|
||||
### 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
|
||||
The `type_info` class isn't a drop-in replacement for `std::type_info` but can
|
||||
provide similar information which are not implementation defined and don't
|
||||
require to enable RTTI.<br/>
|
||||
Therefore, they can sometimes be even more reliable than those obtained
|
||||
otherwise.
|
||||
@@ -639,7 +590,7 @@ These are the information made available by `type_info`:
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto idx = entt::type_index<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
auto idx = entt::type_index<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
* The hash value associated with a given type:
|
||||
@@ -651,7 +602,7 @@ These are the information made available by `type_info`:
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto hash = entt::type_hash<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
auto hash = entt::type_hash<std::remove_cv_t<std::remove_reference_t<a_type>>>::value();
|
||||
```
|
||||
|
||||
* The name associated with a given type:
|
||||
@@ -663,7 +614,7 @@ These are the information made available by `type_info`:
|
||||
This is also an alias for the following:
|
||||
|
||||
```cpp
|
||||
auto name = entt::type_name<std::remove_const_t<std::remove_reference_t<a_type>>>::value();
|
||||
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
|
||||
@@ -676,18 +627,17 @@ described above.
|
||||
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.
|
||||
In fact, although this is quite rare, it's 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/>
|
||||
have the same fully qualified name. In this case, also `type_name` will return
|
||||
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.
|
||||
identifiers don't suffer from the same problem in fact. However, this solution
|
||||
doesn't work well with a plugin system, where the libraries aren't 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
|
||||
@@ -696,8 +646,8 @@ Fortunately, there are several easy ways to deal with this:
|
||||
* 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
|
||||
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.
|
||||
@@ -712,11 +662,11 @@ 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/>
|
||||
The standard operator `sizeof` complains when users provide it for example with
|
||||
function or incomplete types. On the other hand, it's guaranteed that its result
|
||||
is always nonzero, 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:
|
||||
works under all circumstances, returning zero if the type isn't supported:
|
||||
|
||||
```cpp
|
||||
const auto size = entt::size_of_v<void>;
|
||||
@@ -743,15 +693,15 @@ 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:
|
||||
An 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.
|
||||
The trait is subject to the rules of the language. Therefore, for example,
|
||||
transferring constness between references won't give the desired effect.
|
||||
|
||||
### Member class type
|
||||
|
||||
@@ -765,18 +715,6 @@ 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
|
||||
@@ -798,8 +736,8 @@ 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/>
|
||||
Since `id_type` is very important and widely used in `EnTT`, there is a more
|
||||
user-friendly shortcut for the creation of integral 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
|
||||
@@ -809,7 +747,7 @@ where types would be required otherwise. As an example:
|
||||
registry.emplace<entt::tag<"enemy"_hs>>(entity);
|
||||
```
|
||||
|
||||
However, this is not the only permitted use. Literally any value convertible to
|
||||
However, this isn't 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
|
||||
@@ -824,14 +762,12 @@ 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
|
||||
I'm 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
|
||||
@@ -839,28 +775,28 @@ lists. We therefore have `value_list_element[_v]` as well as
|
||||
|
||||
# Unique sequential identifiers
|
||||
|
||||
Sometimes it is useful to be able to give unique, sequential numeric identifiers
|
||||
Sometimes it's 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.
|
||||
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 embraces what the modern C++ has to offer.
|
||||
|
||||
## Compile-time generator
|
||||
|
||||
To generate sequential numeric identifiers at compile-time, `EnTT` offers the
|
||||
`ident` class template:
|
||||
`identifier` class template:
|
||||
|
||||
```cpp
|
||||
// defines the identifiers for the given types
|
||||
using id = entt::ident<a_type, another_type>;
|
||||
using id = entt::identifier<a_type, another_type>;
|
||||
|
||||
// ...
|
||||
|
||||
switch(a_type_identifier) {
|
||||
case id::value<a_type>:
|
||||
case id::type<a_type>:
|
||||
// ...
|
||||
break;
|
||||
case id::value<another_type>:
|
||||
case id::type<another_type>:
|
||||
// ...
|
||||
break;
|
||||
default:
|
||||
@@ -868,32 +804,31 @@ default:
|
||||
}
|
||||
```
|
||||
|
||||
This is what this class template has to offer: a `value` inline variable that
|
||||
This is all what this class template has to offer: a `type` 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:
|
||||
stable across different runs. In case they have been used in a production
|
||||
environment and a type has to be removed, one can just use a placeholder to left
|
||||
the other identifiers unchanged:
|
||||
|
||||
```cpp
|
||||
template<typename>
|
||||
struct ignore_type {};
|
||||
template<typename> struct ignore_type {};
|
||||
|
||||
using id = entt::ident<
|
||||
using id = entt::identifier<
|
||||
a_type_still_valid,
|
||||
ignore_type<no_longer_valid_type>,
|
||||
ignore_type<a_type_no_longer_valid>,
|
||||
another_type_still_valid
|
||||
>;
|
||||
```
|
||||
|
||||
Perhaps a bit ugly to see in a codebase, but it gets the job done at least.
|
||||
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:
|
||||
To generate sequential numeric identifiers at runtime, `EnTT` offers the
|
||||
`family` class template:
|
||||
|
||||
```cpp
|
||||
// defines a custom generator
|
||||
@@ -901,26 +836,26 @@ 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>;
|
||||
const auto a_type_id = id::type<a_type>;
|
||||
const auto another_type_id = id::type<another_type>;
|
||||
```
|
||||
|
||||
This is what a _family_ has to offer: a `value` inline variable that contains a
|
||||
numeric identifier for the given type.<br/>
|
||||
This is all what a _family_ has to offer: a `type` 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.
|
||||
Please, note that identifiers aren't 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
|
||||
It's 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
|
||||
C++20. It returns its argument unchanged and nothing more. It's useful as a
|
||||
sort of _do nothing_ function in template programming.
|
||||
|
||||
* `entt::overload`: a tool to disambiguate different overloads from their
|
||||
@@ -966,8 +901,7 @@ life of developers:
|
||||
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/>
|
||||
If it's not clear what it is, there is probably no need for this utility.<br/>
|
||||
Below is a small example to show its use:
|
||||
|
||||
```cpp
|
||||
@@ -978,9 +912,9 @@ life of developers:
|
||||
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.
|
||||
Maybe convoluted at a first glance but certainly effective. Unfortunately,
|
||||
the language doesn't 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.
|
||||
list will probably grow over time but the size of each will remain rather small,
|
||||
as has been the case so far.
|
||||
|
||||
2007
docs/md/entity.md
2007
docs/md/entity.md
File diff suppressed because it is too large
Load Diff
135
docs/md/faq.md
135
docs/md/faq.md
@@ -1,5 +1,8 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -7,20 +10,22 @@
|
||||
* [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)
|
||||
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
|
||||
* [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)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
This is a constantly updated section where I am trying to put the answers to the
|
||||
This is a constantly updated section where I'll try 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/>
|
||||
If you don't find your answer here, there are two cases: nobody has done it yet
|
||||
or this section needs updating. In both cases, try to
|
||||
[open a new issue](https://github.com/skypjack/entt/issues/new) or enter the
|
||||
[gitter channel](https://gitter.im/skypjack/entt) and ask your question.
|
||||
Probably someone already has an answer for you and we can then integrate this
|
||||
part of the documentation.
|
||||
|
||||
@@ -29,50 +34,50 @@ part of the documentation.
|
||||
## 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
|
||||
latest revision of the language and the standard library. For this reason, it's
|
||||
likely that some classes you're working with are using standard containers under
|
||||
the hood.<br/>
|
||||
Unfortunately, it's known that the standard containers aren't 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
|
||||
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
|
||||
option (_Just My Code_ debugging), available starting in 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`:
|
||||
Moreover, the macro `ENTT_ASSERT` should be redefined to disable internal checks
|
||||
made by `EnTT` in debug:
|
||||
|
||||
```cpp
|
||||
#define ENTT_ASSERT(...) ((void)0)
|
||||
```
|
||||
|
||||
These asserts are introduced to help the users, but they require access to the
|
||||
These asserts are introduced to help the users but they require to 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`.
|
||||
With these changes, debug performance should increase enough for most cases. If
|
||||
you want something more, you can 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.
|
||||
There are several approaches to the problem and what’s the best one depends
|
||||
mainly on the real problem one is facing. In all cases, how to do it doesn't
|
||||
strictly depend on the library in use, but the latter can certainly allow 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/>
|
||||
I tried to describe some of the techniques that fit well with the model of
|
||||
`EnTT`. [Here](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 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
|
||||
@@ -83,9 +88,8 @@ what concerns the `component_traits` class for further details.
|
||||
|
||||
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
|
||||
* If `std::uint32_t` isn't 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
|
||||
@@ -98,15 +102,41 @@ enum class entity: std::uint32_t {};
|
||||
|
||||
There is no limit to the number of identifiers that can be defined.
|
||||
|
||||
## Warning C4307: integral constant overflow
|
||||
|
||||
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
|
||||
hashed string under VS could generate a warning.<br/>
|
||||
First of all, I want to reassure you: it's expected and harmless. However, it
|
||||
can be annoying.
|
||||
|
||||
To suppress it and if you don't want to suppress all the other warnings as well,
|
||||
here is a workaround in the form of a macro:
|
||||
|
||||
```cpp
|
||||
#if defined(_MSC_VER)
|
||||
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
|
||||
#else
|
||||
#define HS(str) entt::hashed_string{str}
|
||||
#endif
|
||||
```
|
||||
|
||||
With an example of use included:
|
||||
|
||||
```cpp
|
||||
constexpr auto identifier = HS("my/resource/identifier");
|
||||
```
|
||||
|
||||
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
|
||||
|
||||
## 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
|
||||
It's a pretty big problem but fortunately it's not a problem of `EnTT` and there
|
||||
is a fairly simple solution to it.<br/>
|
||||
It consists in defining the `NOMINMAX` macro before to include any other header
|
||||
so as to get rid of the extra definitions:
|
||||
|
||||
```cpp
|
||||
@@ -119,9 +149,9 @@ 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/>
|
||||
component is actually copyable. However, this trait doesn't 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
|
||||
@@ -161,48 +191,21 @@ to mitigate the problem makes it manageable.
|
||||
|
||||
## Which functions trigger which signals
|
||||
|
||||
Storage classes offer three _signals_ that are emitted following specific
|
||||
The `registry` class offers 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:
|
||||
If this isn't 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
|
||||
`destroy`. However, following the above rules, it's 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.
|
||||
|
||||
367
docs/md/graph.md
367
docs/md/graph.md
@@ -1,367 +0,0 @@
|
||||
# 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.
|
||||
@@ -1,11 +1,17 @@
|
||||
# Push EnTT across boundaries
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# 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)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Working across boundaries
|
||||
|
||||
@@ -13,58 +19,71 @@
|
||||
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.
|
||||
Fortunately, nowadays using `EnTT` across boundaries is much easier.
|
||||
|
||||
## 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/>
|
||||
This isn't a problem on itself (in fact, it's the basis of an API so convenient
|
||||
to use). However, a way is needed to recognize the objects whose type has been
|
||||
erased on the other side of a boundary.<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
|
||||
available to the rest of the library. In general, this class doesn't arouse much
|
||||
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/>
|
||||
(definitely uncommon though) or when the default solution proposed by `EnTT`
|
||||
isn't 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 there to import or export symbols, so as to make
|
||||
everything work nicely across boundaries.<br/>
|
||||
`ENTT_API_IMPORT` can be used where there is a need 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.
|
||||
shared libraries that don't 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.
|
||||
For anyone who needs more details, the test suite contains multiple examples
|
||||
covering the most common cases (see the `lib` directory for all details).<br/>
|
||||
It goes without saying that it's 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
|
||||
Since it's linked already to a static context to which the visible components
|
||||
are attached and different contexts don't 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:
|
||||
Sharing a context is trivial though. First of all, the local one must be
|
||||
acquired in the main space:
|
||||
|
||||
```cpp
|
||||
auto handle = entt::locator<entt::meta_ctx>::handle();
|
||||
entt::meta_ctx ctx{};
|
||||
```
|
||||
|
||||
Then, it is passed to the receiving space that sets it as its default context,
|
||||
thus discarding or storing aside the local one:
|
||||
Then, it must passed to the receiving space that will set it as its global
|
||||
context, thus releasing the local one that remains available but is no longer
|
||||
referred to by the runtime reflection system:
|
||||
|
||||
```cpp
|
||||
entt::locator<entt::meta_ctx>::reset(handle);
|
||||
entt::meta_ctx::bind(ctx);
|
||||
```
|
||||
|
||||
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.
|
||||
From now on, both spaces will refer to the same context and on it will be
|
||||
attached the new visible meta types, no matter where they are created.<br/>
|
||||
A context can also be reset and then associated again locally as:
|
||||
|
||||
```cpp
|
||||
entt::meta_ctx::bind(entt::meta_ctx{});
|
||||
```
|
||||
|
||||
This is allowed because local and global contexts are separated. Therefore, it's
|
||||
always possible to make the local context the current one again.
|
||||
|
||||
Before to release a context, all locally registered types should be reset to
|
||||
avoid dangling references. Otherwise, if a type is accessed from another space
|
||||
by name, there could be an attempt to address its parts that are no longer
|
||||
available.
|
||||
|
||||
## Memory Management
|
||||
|
||||
@@ -81,10 +100,9 @@ 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.
|
||||
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
|
||||
To overcome the risk, it's 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
|
||||
|
||||
165
docs/md/links.md
165
docs/md/links.md
@@ -1,41 +1,22 @@
|
||||
# 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.
|
||||
source projects based on `EnTT` and didn't 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.
|
||||
a reference. Where I put the word _apparently_ means that the use of `EnTT` is
|
||||
documented but the authors didn't 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
|
||||
I hope this list can grow much more in the future:
|
||||
|
||||
* 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.
|
||||
@@ -48,11 +29,11 @@ I hope the following lists can grow much more in the future.
|
||||
* [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.
|
||||
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
|
||||
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) 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
|
||||
@@ -73,7 +54,7 @@ I hope the following lists can grow much more in the future.
|
||||
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.
|
||||
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
|
||||
@@ -97,60 +78,24 @@ I hope the following lists can grow much more in the future.
|
||||
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
|
||||
* [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
|
||||
space ship through a large 2D open world.
|
||||
* [PokeMaster](https://github.com/utilForever/PokeMaster): Pokemon Battle
|
||||
simulator using C++ with some reinforcement learning.
|
||||
* [HomeHearth](https://youtu.be/GrEWl8npL9Y): choose your hero, protect the
|
||||
town, before it is too late.
|
||||
town, before it's 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.
|
||||
* [You Are Circle](https://store.steampowered.com/app/3578190/You_Are_Circle/):
|
||||
a roguelite top-down shooter with a high-contrast vector line aesthetic.
|
||||
* [EnTT Dino](https://github.com/omgitsaheadcrab/entt_dino): a Dinosaur Game
|
||||
clone in C++ using only `SDL2` and `EnTT`.
|
||||
|
||||
## 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.
|
||||
* Engines and the like:
|
||||
* [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
|
||||
@@ -196,7 +141,7 @@ I hope the following lists can grow much more in the future.
|
||||
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
|
||||
* [OverEngine](https://github.com/OverShifted/OverEngine): an over-engineered
|
||||
game engine.
|
||||
* [Electro](https://github.com/Electro-Technologies/Electro): high performance
|
||||
3D game engine with a high emphasis on rendering.
|
||||
@@ -205,51 +150,14 @@ I hope the following lists can grow much more in the future.
|
||||
* [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.
|
||||
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).
|
||||
* [Physecs](https://github.com/thfProjects/Physecs): real-time 3D rigid body
|
||||
physics simulation built on `EnTT`.
|
||||
|
||||
## Articles, videos and blog posts:
|
||||
|
||||
* 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.
|
||||
@@ -273,23 +181,6 @@ I hope the following lists can grow much more in the future.
|
||||
- ... 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
|
||||
@@ -312,24 +203,17 @@ I hope the following lists can grow much more in the future.
|
||||
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:
|
||||
|
||||
* 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
|
||||
cross platform C++ rendering engine. The SDKs are utilized 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).
|
||||
* [OneArc](https://onearc.com/): [licenses](https://onearc.com/third-party-licensors/)
|
||||
do not lie. Their products use EnTT in some way, but it is not known _what_
|
||||
way.
|
||||
* [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
|
||||
@@ -339,7 +223,7 @@ I hope the following lists can grow much more in the future.
|
||||
* [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.
|
||||
wallet and non-custodial 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.
|
||||
@@ -362,3 +246,6 @@ I hope the following lists can grow much more in the future.
|
||||
* 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.
|
||||
|
||||
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 this page.
|
||||
|
||||
@@ -1,16 +1,21 @@
|
||||
# Crash Course: service locator
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [Service locator](#service-locator)
|
||||
* [Opaque handles](#opaque-handles)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
Usually, service locators are tightly bound to the services they expose. It is
|
||||
Usually service locators are tightly bound to the services they expose and it's
|
||||
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
|
||||
This tiny class tries to fill the gap and to get rid of the burden of defining a
|
||||
different specific locator for each application.
|
||||
|
||||
# Service locator
|
||||
@@ -27,7 +32,7 @@ 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:
|
||||
Once a service has been set up, it's retrieved using the value function:
|
||||
|
||||
```cpp
|
||||
interface &service = entt::locator<interface>::value();
|
||||
@@ -45,38 +50,7 @@ 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
|
||||
All arguments are used only if necessary, that is, if a service doesn't 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.
|
||||
|
||||
883
docs/md/meta.md
883
docs/md/meta.md
File diff suppressed because it is too large
Load Diff
128
docs/md/poly.md
128
docs/md/poly.md
@@ -1,5 +1,8 @@
|
||||
# Crash Course: poly
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
@@ -11,6 +14,9 @@
|
||||
* [Inheritance](#inheritance)
|
||||
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
|
||||
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# Introduction
|
||||
|
||||
@@ -20,16 +26,17 @@ 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
|
||||
This is, among others, 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.
|
||||
What users get is an object that can be passed around as such and not through a
|
||||
reference or a pointer, as 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/>
|
||||
supports most of its feature. Among the most important, 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.
|
||||
@@ -37,7 +44,7 @@ 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:
|
||||
Among all, the two that I prefer are:
|
||||
|
||||
* [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right.
|
||||
* [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md):
|
||||
@@ -45,12 +52,12 @@ The ones that I like more are:
|
||||
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,
|
||||
I've some doubts about the usefulness of some feature 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
|
||||
flaw is the API which 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.
|
||||
feature.
|
||||
|
||||
Either way, the authors are gurus of the C++ community, people I only have to
|
||||
learn from.
|
||||
@@ -62,18 +69,18 @@ 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
|
||||
is convenient and allows users to write less code in most cases, this has some
|
||||
limitations and it's 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/>
|
||||
Once the interface is defined, it will be sufficient to provide a generic
|
||||
implementation to fulfill the concept.<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:
|
||||
This is how a concept with a deduced interface is introduced:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
@@ -86,7 +93,7 @@ struct Drawable: entt::type_list<> {
|
||||
};
|
||||
```
|
||||
|
||||
It is recognizable by the fact that it inherits from an empty type list.<br/>
|
||||
It's 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`:
|
||||
|
||||
@@ -101,12 +108,12 @@ struct Drawable: entt::type_list<> {
|
||||
};
|
||||
```
|
||||
|
||||
In this case, all parameters are passed to `invoke` after the reference to
|
||||
In this case, all parameters must be 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
|
||||
`Base`, from which one must necessarily inherit. Since it's 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
|
||||
the language. However, there exists also an alternative that goes through an
|
||||
external call:
|
||||
|
||||
```cpp
|
||||
@@ -158,12 +165,12 @@ struct Drawable: entt::type_list<bool(int) const> {
|
||||
|
||||
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.
|
||||
Because, in fact, this is exactly 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:
|
||||
`void()`, a concept can be satisfied:
|
||||
|
||||
* Either by a class that exposes a member function with the same name and the
|
||||
same signature.
|
||||
@@ -171,9 +178,9 @@ If the concept exposes a member function called `draw` with function type
|
||||
* 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
|
||||
In other words, it's not possible to make use of functions not belonging to the
|
||||
interface, even if they are present in the types that fulfill the concept.<br/>
|
||||
Similarly, it's 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.
|
||||
|
||||
@@ -182,7 +189,7 @@ 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:
|
||||
The `impl` alias template of a concept is used to define how it's fulfilled:
|
||||
|
||||
```cpp
|
||||
struct Drawable: entt::type_list<> {
|
||||
@@ -193,8 +200,8 @@ struct Drawable: entt::type_list<> {
|
||||
};
|
||||
```
|
||||
|
||||
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/>
|
||||
In this case, it's stated that the `draw` method of a generic type will be
|
||||
enough to satisfy the requirements of the `Drawable` concept.<br/>
|
||||
Both member functions and free functions are supported to fulfill concepts:
|
||||
|
||||
```cpp
|
||||
@@ -211,9 +218,9 @@ struct Drawable: entt::type_list<void()> {
|
||||
|
||||
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
|
||||
actual implementation may differ in its function type since it's erased
|
||||
internally.<br/>
|
||||
Moreover, the `self` parameter is not strictly required by the system and can be
|
||||
Moreover, the `self` parameter isn't strictly required by the system and can be
|
||||
left out for free functions if not required.
|
||||
|
||||
Refer to the inline documentation for more details.
|
||||
@@ -221,7 +228,7 @@ 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/>
|
||||
Therefore, it's 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.
|
||||
|
||||
@@ -230,8 +237,8 @@ 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;
|
||||
struct type: typename Drawable::template type<Base> {
|
||||
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
|
||||
void erase() { entt::poly_call<base + 0>(*this); }
|
||||
};
|
||||
|
||||
@@ -244,30 +251,31 @@ struct DrawableAndErasable: entt::type_list<> {
|
||||
```
|
||||
|
||||
The static virtual table is empty and must remain so.<br/>
|
||||
On the other hand, `type` no longer inherits from `Base`. Instead, it forwards
|
||||
On the other hand, `type` no longer inherits from `Base` and instead 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/>
|
||||
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:
|
||||
As for a defined concept instead, also the list of types must be extended, in a
|
||||
similar way to what is shown for the implementation of the above concept.<br/>
|
||||
To do this, it's 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:
|
||||
The definition isn't strictly required, since the function will only be 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()>> {
|
||||
entt::type_list<void()>
|
||||
> {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
@@ -278,8 +286,9 @@ 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:
|
||||
Once the _concept_ and implementation have been introduced, it will be possible
|
||||
to use the `poly` class template to contain instances that meet the
|
||||
requirements:
|
||||
|
||||
```cpp
|
||||
using drawable = entt::poly<Drawable>;
|
||||
@@ -301,9 +310,9 @@ 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/>
|
||||
The `poly` class template offers a wide range of constructors, from the default
|
||||
one (which will return 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):
|
||||
|
||||
@@ -312,23 +321,22 @@ circle shape;
|
||||
drawable instance{std::in_place_type<circle &>, shape};
|
||||
```
|
||||
|
||||
Similarly, it is possible to create non-owning copies of `poly` from an existing
|
||||
Similarly, it's 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.
|
||||
In both cases, although the interface of the `poly` object doesn't change, it
|
||||
won't 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.
|
||||
Therefore, where `instance.data()` will invoke the `data` member function of the
|
||||
poly object, `instance->data()` will map directly to the functionality exposed
|
||||
by the underlying concept.
|
||||
|
||||
# Storage size and alignment requirement
|
||||
|
||||
@@ -343,9 +351,9 @@ 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
|
||||
an integer. The alignment requirement is optional instead and by default such
|
||||
that it's the most stringent (the largest) for any object whose size is at most
|
||||
equal to the one provided.<br/>
|
||||
It's 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.
|
||||
|
||||
@@ -1,68 +1,81 @@
|
||||
# Crash Course: cooperative scheduler
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Introduction](#introduction)
|
||||
* [The process](#the-process)
|
||||
* [Continuation](#continuation)
|
||||
* [Shared process](#shared-process)
|
||||
* [Adaptor](#adaptor)
|
||||
* [The scheduler](#the-scheduler)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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.
|
||||
Sometimes processes are a useful tool to work around the strict definition of a
|
||||
system and introduce logic in a different way, usually without resorting to the
|
||||
introduction of other components.
|
||||
|
||||
`EnTT` offers a minimal support to this paradigm by introducing a few classes
|
||||
that users can use to define and execute cooperative processes.
|
||||
|
||||
# The process
|
||||
|
||||
A typical task inherits from the `process` class template. Derived classes also
|
||||
specify what the intended type for elapsed times is.
|
||||
A typical process must inherit from the `process` class template that stays true
|
||||
to the CRTP idiom. Moreover, derived classes must specify what's the intended
|
||||
type for elapsed times.
|
||||
|
||||
A process should implement 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):
|
||||
A process should expose publicly the following member functions whether needed
|
||||
(note that it isn't 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. 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.
|
||||
It's invoked once per tick until a process is explicitly aborted or it
|
||||
terminates either with or without errors. Even though it's not mandatory to
|
||||
declare this member function, as a rule of thumb 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();`
|
||||
|
||||
It's invoked when the process joins the running queue of a scheduler. This
|
||||
happens as soon as it's attached to the scheduler if the process is a top
|
||||
level one, otherwise when it replaces its parent if the process is a
|
||||
continuation.
|
||||
|
||||
* `void succeeded();`
|
||||
|
||||
This is invoked in case of success, immediately after an update and during the
|
||||
It's 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
|
||||
It's 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.
|
||||
It's invoked only if a process is explicitly aborted. There is no guarantee
|
||||
that it executes in the same tick, this depends solely on whether the
|
||||
process is aborted immediately or not.
|
||||
|
||||
A class can also change the state of a process by invoking `succeed` and `fail`,
|
||||
as well as `pause` and `unpause` the process itself.<br/>
|
||||
All these are public member functions made available to manage the life cycle of
|
||||
a process easily.
|
||||
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. All
|
||||
these are protected member functions made available to be able 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 {
|
||||
using allocator_type = typename entt::process::allocator_type;
|
||||
using delta_type = typename entt::process::delta_type;
|
||||
struct my_process: entt::process<my_process, std::uint32_t> {
|
||||
using delta_type = std::uint32_t;
|
||||
|
||||
my_process(const allocator_type &allocator, delta_type delay)
|
||||
: entt::process{allocator},
|
||||
remaining{delay}
|
||||
my_process(delta_type delay)
|
||||
: remaining{delay}
|
||||
{}
|
||||
|
||||
void update(delta_type delta, void *) {
|
||||
@@ -80,118 +93,96 @@ private:
|
||||
};
|
||||
```
|
||||
|
||||
## Continuation
|
||||
## Adaptor
|
||||
|
||||
A process may be followed by other processes upon successful termination.<br/>
|
||||
This pairing can be set up at creation time, keeping the processes conceptually
|
||||
separate from each other while still combining them at runtime:
|
||||
Lambdas and functors can't be used directly with a scheduler for 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 arguments to
|
||||
call whenever a process is terminated with success or with an error:
|
||||
|
||||
```cpp
|
||||
my_process process{};
|
||||
process.then<my_other_process>();
|
||||
void(Delta delta, void *data, auto succeed, auto fail);
|
||||
```
|
||||
|
||||
This approach allows processes to be developed in isolation and combined to
|
||||
define complex actions.<br/>
|
||||
For example, a delayed operation where a parent process (such as a timer)
|
||||
_schedules_ a child process (the deferred task) once the time is over.
|
||||
Parameters have the following meaning:
|
||||
|
||||
The `then` function also accepts lambdas, which are associated with a dedicated
|
||||
process internally:
|
||||
* `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.
|
||||
|
||||
```cpp
|
||||
process.then([](entt::process &proc, std::uint32_t delta, void *data) {
|
||||
// ...
|
||||
})
|
||||
```
|
||||
Both `succeed` and `fail` accept no parameters at all.
|
||||
|
||||
The lambda function is such that it accepts a reference to the process that
|
||||
manages it (to be able to terminate it, pause it and so on), plus the usual
|
||||
values also passed to the `update` function.
|
||||
|
||||
## Shared process
|
||||
|
||||
All processes inherit from `std::enable_shared_from_this` to allow sharing with
|
||||
the caller.<br/>
|
||||
The returned smart pointer was created using the allocator associated with the
|
||||
scheduler and therefore all its processes. This same allocator is available by
|
||||
invoking `get_allocator` on the process itself.
|
||||
|
||||
As far as possible, sharing a process is not intended to allow the caller to
|
||||
manage it. This could actually compromise the proper functioning of the
|
||||
scheduler and the process itself.<br/>
|
||||
Rather, the purpose is to allow the callers to save a valid reference to the
|
||||
process, allowing them to intervene in its lifecycle through calls like `pause`
|
||||
and the like.
|
||||
Note that usually users shouldn't 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
|
||||
A cooperative scheduler runs different processes and helps managing 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
|
||||
Each process is invoked once per tick. If it terminates, it's removed
|
||||
automatically from the scheduler and it's never invoked again. Otherwise it's
|
||||
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.
|
||||
of errors, both the parent process and its child are discarded. This way, it's
|
||||
easy to create 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;
|
||||
entt::scheduler<std::uint32_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:
|
||||
It 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();
|
||||
entt::scheduler<std::uint32_t>::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, invoke the `attach` function with a process
|
||||
type and the arguments to use to construct it:
|
||||
To attach a process to a scheduler there are mainly two ways:
|
||||
|
||||
```cpp
|
||||
scheduler.attach<my_process>(_1000u);
|
||||
```
|
||||
* If the process inherits from the `process` class template, it's enough to
|
||||
indicate its type and submit all the parameters required to construct it to
|
||||
the `attach` member function:
|
||||
|
||||
The scheduler will also provide the process with its allocator as the first
|
||||
argument.<br>
|
||||
In case of lambdas or functors, the required signature is the one already seen
|
||||
for the `then` function of a process:
|
||||
```cpp
|
||||
scheduler.attach<my_process>(1000u);
|
||||
```
|
||||
|
||||
```cpp
|
||||
scheduler.attach([](entt::process &, std::uint32_t, void *){ /* ... */ });
|
||||
```
|
||||
* Otherwise, in case of a lambda or a functor, it's enough to provide an
|
||||
instance of the class to the `attach` member function:
|
||||
|
||||
In both cases, the newly created process is returned by reference and its `then`
|
||||
member function is used to create chains of processes to run sequentially.<br/>
|
||||
```cpp
|
||||
scheduler.attach([](auto...){ /* ... */ });
|
||||
```
|
||||
|
||||
In both cases, the return value is an opaque object that offers a `then` member
|
||||
function to use 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([](entt::process &, std::uint32_t, void *) {
|
||||
scheduler.attach([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of another lambda function
|
||||
.then([](entt::process &, std::uint32_t, void *) {
|
||||
.then([](auto delta, void *, auto succeed, auto fail) {
|
||||
// ...
|
||||
})
|
||||
// appends a child in the form of a process class
|
||||
@@ -210,7 +201,7 @@ 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:
|
||||
that can be used to discard all the running processes at once:
|
||||
|
||||
```cpp
|
||||
// aborts all the processes abruptly ...
|
||||
@@ -219,6 +210,3 @@ scheduler.abort(true);
|
||||
// ... or gracefully during the next tick
|
||||
scheduler.abort();
|
||||
```
|
||||
|
||||
The argument passed to the `abort` function indicates whether execution should
|
||||
be stopped immediately or processes should be notified on the next tick.
|
||||
|
||||
@@ -1,30 +1,18 @@
|
||||
# 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
|
||||
Below an incomplete list of those that I've come across so far.<br/>
|
||||
If some terms or designs aren't clear, I recommend referring to the
|
||||
[_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the
|
||||
details.
|
||||
|
||||
I hope this list can grow much more in the future:
|
||||
|
||||
* C:
|
||||
* [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based
|
||||
on sparse sets.
|
||||
@@ -35,31 +23,23 @@ details.
|
||||
* [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.
|
||||
* [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
|
||||
* [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.
|
||||
platform agnostic and table based ECS framework.
|
||||
|
||||
* Go:
|
||||
* [gecs](https://github.com/tutumagi/gecs): a sparse sets based ECS inspired
|
||||
@@ -68,7 +48,7 @@ details.
|
||||
* 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
|
||||
* [ecsy](https://github.com/MozillaReality/ecsy): I haven't had the time to
|
||||
investigate the underlying design of `ecsy` but it looks cool anyway.
|
||||
|
||||
* Perl:
|
||||
@@ -80,6 +60,7 @@ details.
|
||||
entity registry for ECS designs inspired by `EnTT`.
|
||||
|
||||
* Rust:
|
||||
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
|
||||
* [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
|
||||
@@ -89,3 +70,6 @@ details.
|
||||
|
||||
* Zig
|
||||
* [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`.
|
||||
|
||||
If you know of other resources out there that can be of interest for the reader,
|
||||
feel free to open an issue or a PR and I'll be glad to add them to this page.
|
||||
|
||||
@@ -1,26 +1,32 @@
|
||||
# Crash Course: resource management
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# 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)
|
||||
* [Loaders](#loader)
|
||||
* [The cache class](#the-cache)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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
|
||||
Resource management is usually one of the most critical part of a software like
|
||||
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
|
||||
`EnTT` doesn't 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.
|
||||
Instead, the library offers a minimal, general purpose resource cache that might
|
||||
be useful in many cases.
|
||||
|
||||
# The resource, the loader and the cache
|
||||
|
||||
@@ -31,8 +37,7 @@ The _resource_ is an image, an audio, a video or any other type:
|
||||
struct my_resource { const int value; };
|
||||
```
|
||||
|
||||
The _loader_ is a callable type, the aim of which is to load a specific
|
||||
resource:
|
||||
The _loader_ is a callable type the aim of which is to load a specific resource:
|
||||
|
||||
```cpp
|
||||
struct my_loader final {
|
||||
@@ -61,46 +66,46 @@ 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/>
|
||||
The cache is meant to be used to create different caches for different types of
|
||||
resources 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
|
||||
Resources aren't returned directly to the caller. Instead, they are wrapped in a
|
||||
_resource handle_ identified by the `entt::resource` class template.<br/>
|
||||
For those who know the _flyweight design pattern_ already, that's 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.
|
||||
handle mostly maps the interface of its standard counterpart and only adds a few
|
||||
things to it.<br/>
|
||||
However, the handle in `EnTT` is designed as a standalone class template named
|
||||
`resource`. It boils down to the fact that specializing a class in the standard
|
||||
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
|
||||
A loader is a class that is responsible for _loading_ the resources.<br/>
|
||||
By default, it's just a callable object which 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.
|
||||
The loader isn't required to return a resource handle. As long as `return_type`
|
||||
is suitable for constructing a handle, that's 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.
|
||||
type. However, it isn't 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:
|
||||
@@ -112,13 +117,13 @@ struct my_loader {
|
||||
struct from_disk_tag{};
|
||||
struct from_network_tag{};
|
||||
|
||||
template<typename... Args>
|
||||
template<typename Args>
|
||||
result_type operator()(from_disk_tag, Args&&... args) {
|
||||
// ...
|
||||
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
template<typename Args>
|
||||
result_type operator()(from_network_tag, Args&&... args) {
|
||||
// ...
|
||||
return std::make_shared<my_resource>(std::forward<Args>(args)...);
|
||||
@@ -131,7 +136,7 @@ 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:
|
||||
It loads the resources, store them aside and returns handles as needed:
|
||||
|
||||
```cpp
|
||||
entt::resource_cache<my_resource, my_loader> cache{};
|
||||
@@ -139,12 +144,12 @@ 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:
|
||||
For this reason, it offers most of the functionality a user would expect from a
|
||||
map, such as `empty` or `size` and so on. Similarly, it's an iterable type that
|
||||
also supports indexing by resource id:
|
||||
|
||||
```cpp
|
||||
for(auto [id, res]: cache) {
|
||||
for(entt::resource<my_resource> curr: cache) {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -154,14 +159,14 @@ 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`).
|
||||
functions (for example `contains` or `erase`).
|
||||
|
||||
Set aside the part of the API that this class _shares_ with a map, it also adds
|
||||
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):
|
||||
In particular, it doesn't 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);
|
||||
@@ -170,17 +175,18 @@ auto ret = cache.load("resource/id"_hs);
|
||||
const bool loaded = ret.second;
|
||||
|
||||
// takes the resource handle pointed to by the returned iterator
|
||||
entt::resource<my_resource> res = ret.first->second;
|
||||
entt::resource<my_resource> res = *ret.first;
|
||||
```
|
||||
|
||||
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
|
||||
Moreover, it's worth mentioning that both the iterators of a cache and 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 isn't 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.
|
||||
error in the user logic but it may also be an _expected_ event.<br/>
|
||||
It's therefore recommended to verify handles validity with a check in debug (for
|
||||
example, when loading) or an appropriate logic in retail.
|
||||
|
||||
@@ -1,42 +1,47 @@
|
||||
# Crash Course: events, signals and everything in between
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# 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)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
# 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.
|
||||
Signals are usually a core part of games and software architectures in
|
||||
general.<br/>
|
||||
Roughly speaking, 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 so called _modern C++_ comes with a tool that can be useful in these terms,
|
||||
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
|
||||
In case that the flexibility and power of an `std::function` isn't 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
|
||||
free functions and members provided along with an instance on which to invoke
|
||||
them.<br/>
|
||||
It doesn't claim to be a drop-in replacement for an `std::function`, so don't
|
||||
expect to use it whenever an `std::function` fits well. That said, it's most
|
||||
likely even a better fit than an `std::function` in a lot of cases, so expect to
|
||||
use it quite a lot anyway.
|
||||
|
||||
@@ -47,13 +52,15 @@ delegates:
|
||||
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.
|
||||
All what is needed to create an instance is to specify the type of the function
|
||||
the delegate will _contain_, that is the signature of the free function or the
|
||||
member one wants to assign to it.
|
||||
|
||||
There exist a few overloads of the `connect` member function to initialize a
|
||||
delegate:
|
||||
Attempting to use an empty delegate by invoking its function call operator
|
||||
results in undefined behavior or most likely a crash. Before to use a delegate,
|
||||
it must be initialized.<br/>
|
||||
There exists a bunch of overloads of the `connect` member function to do that.
|
||||
As an example of use:
|
||||
|
||||
```cpp
|
||||
int f(int i) { return i; }
|
||||
@@ -70,13 +77,13 @@ my_struct instance;
|
||||
delegate.connect<&my_struct::f>(instance);
|
||||
```
|
||||
|
||||
The delegate class also accepts data members, if needed. In this case, the
|
||||
The delegate class accepts also 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
|
||||
well. The first argument `T &` is considered a payload and the function will
|
||||
receive it back every time it's invoked. In other terms, this works just fine
|
||||
with the above definition:
|
||||
|
||||
```cpp
|
||||
@@ -87,11 +94,14 @@ 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:
|
||||
The function `g` will be 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.
|
||||
|
||||
Another interesting aspect of the delegate class is that it accepts also
|
||||
functions with a list of parameters that is shorter than that of the function
|
||||
type used to specialize the delegate itself.<br/>
|
||||
The following code is therefore perfectly valid:
|
||||
|
||||
```cpp
|
||||
void g() { /* ... */ }
|
||||
@@ -100,15 +110,9 @@ 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.
|
||||
saying that the extra arguments are silently discarded internally.<br/>
|
||||
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.
|
||||
|
||||
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
|
||||
@@ -118,7 +122,7 @@ means of the `entt::connect_arg` variable template:
|
||||
entt::delegate<int(int)> func{entt::connect_arg<&f>};
|
||||
```
|
||||
|
||||
Aside `connect`, a `disconnect` counterpart is not provided. Instead, there
|
||||
Aside `connect`, a `disconnect` counterpart isn't 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:
|
||||
@@ -136,14 +140,14 @@ already shown in the examples above:
|
||||
auto ret = delegate(42);
|
||||
```
|
||||
|
||||
In all cases, listeners do not have to strictly follow the signature of the
|
||||
In all cases, the listeners don't 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
|
||||
class on which the members operate and an instance of this class must obviously
|
||||
be passed when invoking the delegate:
|
||||
|
||||
```cpp
|
||||
@@ -154,23 +158,23 @@ 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
|
||||
In this case, it's not possible to deduce the function type since the first
|
||||
argument doesn't 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
|
||||
However, as a consequence of its design, it can also offer minimal support for
|
||||
runtime arguments.<br/>
|
||||
When used like this, some features are not supported though. In particular:
|
||||
When used in this modality, some feature aren't supported though. In particular:
|
||||
|
||||
* Curried functions are not accepted.
|
||||
* Functions with an argument list that differs from that of the delegate are not
|
||||
* Curried functions aren't accepted.
|
||||
* Functions with an argument list that differs from that of the delegate aren't
|
||||
supported.
|
||||
* Return type and types of arguments **must** coincide with those of the
|
||||
delegate and _being at least convertible_ is not enough anymore.
|
||||
delegate and _being at least convertible_ isn't 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...)`.
|
||||
@@ -200,12 +204,12 @@ 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
|
||||
In general, the `delegate` class doesn't fully support lambda functions in all
|
||||
their nuances. The reason is pretty simple: a `delegate` isn't 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.
|
||||
feature aren't 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
|
||||
@@ -228,48 +232,29 @@ delegate.connect([](const void *ptr, int value) {
|
||||
}, &instance);
|
||||
```
|
||||
|
||||
As above, the first parameter (`const void *`) is not part of the function type
|
||||
As above, the first parameter (`const void *`) isn't 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
|
||||
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
|
||||
different lifetimes. On the other side, performance shouldn't 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
|
||||
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.
|
||||
A signal handler can be used as a private data member without exposing any
|
||||
_publish_ functionality to the clients of a class. 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
|
||||
the signal when something is published, all the values returned by the listeners
|
||||
can be 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:
|
||||
@@ -309,31 +294,41 @@ sink.disconnect<&foo>();
|
||||
sink.disconnect<&listener::bar>(instance);
|
||||
|
||||
// disconnect all member functions of an instance, if any
|
||||
sink.disconnect(&instance);
|
||||
sink.disconnect(instance);
|
||||
|
||||
// discards all listeners at once
|
||||
sink.disconnect();
|
||||
```
|
||||
|
||||
As shown above, listeners do not have to strictly follow the signature of the
|
||||
As shown above, the listeners don't 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/>
|
||||
It's also possible to connect a listener before other listeners already
|
||||
contained by the signal. The `before` function returns a `sink` object correctly
|
||||
initialized for the purpose that can be used to connect one or more listeners in
|
||||
order and in the desired position:
|
||||
|
||||
```cpp
|
||||
sink.before<&foo>().connect<&listener::bar>(instance);
|
||||
```
|
||||
|
||||
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.
|
||||
`release` member function. 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`
|
||||
and data in general can be 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:
|
||||
To collect data, the `collect` member function should be used instead. Below is
|
||||
a minimal example to show how to use it:
|
||||
|
||||
```cpp
|
||||
int f() { return 0; }
|
||||
@@ -357,7 +352,7 @@ 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
|
||||
otherwise. This way one can avoid calling all the listeners in case it isn't
|
||||
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
|
||||
@@ -383,7 +378,7 @@ signal.collect(std::ref(collector));
|
||||
|
||||
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
|
||||
This class lazily instantiates its queues. Therefore, it's not necessary to
|
||||
_announce_ the event types in advance:
|
||||
|
||||
```cpp
|
||||
@@ -412,14 +407,12 @@ 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);
|
||||
dispatcher.sink<another_event>().disconnect(listener);
|
||||
```
|
||||
|
||||
The `trigger` member function serves the purpose of sending an immediate event
|
||||
@@ -430,7 +423,7 @@ dispatcher.trigger(an_event{42});
|
||||
dispatcher.trigger<another_event>();
|
||||
```
|
||||
|
||||
Listeners are invoked immediately, order of execution is not guaranteed. This
|
||||
Listeners are invoked immediately, order of execution isn't guaranteed. This
|
||||
method can be used to push around urgent messages like an _is terminating_
|
||||
notification on a mobile app.
|
||||
|
||||
@@ -459,7 +452,7 @@ once per tick to their systems.
|
||||
|
||||
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
|
||||
However, it's 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:
|
||||
|
||||
@@ -476,8 +469,8 @@ parameter for it but rather a different function:
|
||||
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.
|
||||
This is mainly due to the template argument deduction rules and unfortunately
|
||||
there is no real (elegant) way to avoid it.
|
||||
|
||||
# Event emitter
|
||||
|
||||
@@ -487,7 +480,8 @@ 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:
|
||||
To create a custom emitter type, derived classes must inherit directly from the
|
||||
base class as:
|
||||
|
||||
```cpp
|
||||
struct my_emitter: emitter<my_emitter> {
|
||||
@@ -495,10 +489,18 @@ 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.
|
||||
The full list of accepted types of events isn't required. Handlers are created
|
||||
internally on the fly and thus each type of event is accepted by default.
|
||||
|
||||
Whenever an event is published, an emitter provides the listeners with a
|
||||
reference to itself along with a reference to the event. Therefore listeners
|
||||
have an handy way to work with it without incurring in the need of capturing a
|
||||
reference to the emitter itself.<br/>
|
||||
In addition, an opaque object is returned each time a connection is established
|
||||
between an emitter and a listener, allowing the caller to disconnect them at a
|
||||
later time.<br/>
|
||||
The opaque object used to handle connections is both movable and copyable. On
|
||||
the other side, an event emitter is movable but not copyable by default.
|
||||
|
||||
To create new instances of an emitter, no arguments are required:
|
||||
|
||||
@@ -506,54 +508,90 @@ To create new instances of an emitter, no arguments are required:
|
||||
my_emitter emitter{};
|
||||
```
|
||||
|
||||
Listeners are movable and callable objects (free functions, lambdas, functors,
|
||||
`std::function`s, whatever) whose function type is compatible with:
|
||||
Listeners must be movable and callable objects (free functions, lambdas,
|
||||
functors, `std::function`s, whatever) whose function type is compatible with:
|
||||
|
||||
```cpp
|
||||
void(Type &, my_emitter &)
|
||||
void(Event &, 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:
|
||||
Where `Event` is the type of event they want to listen.<br/>
|
||||
There are two ways to attach a listener to an event emitter that differ
|
||||
slightly from each other:
|
||||
|
||||
* To register a long-lived listener, use the `on` member function. It is meant
|
||||
to register a listener designed to be invoked more than once for the given
|
||||
event type.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
auto conn = emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
The connection object can be freely discarded. Otherwise, it can be used later
|
||||
to disconnect the listener if required.
|
||||
|
||||
* To register a short-lived listener, use the `once` member function. It is
|
||||
meant to register a listener designed to be invoked only once for the given
|
||||
event type. The listener is automatically disconnected after the first
|
||||
invocation.<br/>
|
||||
As an example:
|
||||
|
||||
```cpp
|
||||
auto conn = emitter.once<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
The connection object can be freely discarded. Otherwise, it can be used later
|
||||
to disconnect the listener if required.
|
||||
|
||||
In both cases, the connection object can be used with the `erase` member
|
||||
function:
|
||||
|
||||
```cpp
|
||||
emitter.on<my_event>([](const my_event &event, my_emitter &emitter) {
|
||||
// ...
|
||||
});
|
||||
emitter.erase(conn);
|
||||
```
|
||||
|
||||
Similarly, the `reset` member function is used to disconnect listeners given a
|
||||
type while `clear` is used to disconnect all listeners at once:
|
||||
There are also two member functions to use either to disconnect all the
|
||||
listeners for a given type of event or to clear the emitter:
|
||||
|
||||
```cpp
|
||||
// resets the listener for my_event
|
||||
emitter.erase<my_event>();
|
||||
// removes all the listener for the specific event
|
||||
emitter.clear<my_event>();
|
||||
|
||||
// resets all listeners
|
||||
emitter.clear()
|
||||
// removes all the listeners registered so far
|
||||
emitter.clear();
|
||||
```
|
||||
|
||||
To send an event to the listener registered on a given type, the `publish`
|
||||
function is the way to go:
|
||||
To send an event to all the listeners that are interested in it, the `publish`
|
||||
member function offers a convenient approach that relieves users from having to
|
||||
create the event:
|
||||
|
||||
```cpp
|
||||
struct my_event { int i; };
|
||||
|
||||
// ...
|
||||
|
||||
emitter.publish(my_event{42});
|
||||
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:
|
||||
Finally, the `empty` member function tests if there exists at least either a
|
||||
listener registered with the event emitter or to a given type of event:
|
||||
|
||||
```cpp
|
||||
if(emitter.contains<my_event>()) {
|
||||
// ...
|
||||
}
|
||||
bool empty;
|
||||
|
||||
// checks if there is any listener registered for the specific event
|
||||
empty = emitter.empty<my_event>();
|
||||
|
||||
// checks it there are listeners registered with the event emitter
|
||||
empty = emitter.empty();
|
||||
```
|
||||
|
||||
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.
|
||||
In general, the event emitter is a handy tool when the derived classes _wrap_
|
||||
asynchronous operations, because it introduces a _nice-to-have_ model based on
|
||||
events and listeners that kindly hides the complexity behind the scenes. However
|
||||
it is not limited to such uses.
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
# EnTT and Unreal Engine
|
||||
|
||||
<!--
|
||||
@cond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
# Table of Contents
|
||||
|
||||
* [Enable Cpp17](#enable-cpp17)
|
||||
* [EnTT as a third party module](#entt-as-a-third-party-module)
|
||||
* [Include EnTT](#include-entt)
|
||||
<!--
|
||||
@endcond TURN_OFF_DOXYGEN
|
||||
-->
|
||||
|
||||
## 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
|
||||
@@ -24,7 +28,7 @@ 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
|
||||
In case the project doesn't already contain a file of this type, it's possible
|
||||
to create one with the following content:
|
||||
|
||||
```cpp
|
||||
@@ -37,8 +41,8 @@ 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/>
|
||||
Note that updating a *project* to C++17 doesn't 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.
|
||||
|
||||
@@ -62,8 +66,8 @@ Source
|
||||
\---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/>
|
||||
To make this happen, create the folder `ThirdParty` under `Source` if it doesn't
|
||||
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
|
||||
|
||||
52
entt.imp
52
entt.imp
@@ -1,52 +0,0 @@
|
||||
[
|
||||
# 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 ] }
|
||||
]
|
||||
@@ -30,10 +30,4 @@
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_table<*>">
|
||||
<DisplayString>{ payload }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>payload</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_any<*>">
|
||||
<DisplayString>{{ policy={ mode,en } }}</DisplayString>
|
||||
<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>
|
||||
<DisplayString >({ first() }, { second() })</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[first]">first()</Item>
|
||||
<Item Name="[second]">second()</Item>
|
||||
@@ -23,7 +23,8 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::type_info">
|
||||
<DisplayString>{{ name={ alias,na } }}</DisplayString>
|
||||
<DisplayString Condition="seq != 0u">{{ name={ alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[hash]">identifier</Item>
|
||||
<Item Name="[index]">seq</Item>
|
||||
145
natvis/entt/entity.natvis
Normal file
145
natvis/entt/entity.natvis
Normal file
@@ -0,0 +1,145 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_registry<*>">
|
||||
<Intrinsic Name="pools_size" Expression="pools.packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="vars_size" Expression="vars.data.packed.first_base::value.size()"/>
|
||||
<Intrinsic Name="to_entity" Expression="*((entity_traits::entity_type *)&entity) & entity_traits::entity_mask">
|
||||
<Parameter Name="entity" Type="entity_traits::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ size={ entities.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item IncludeView="simple" Name="[entities]">entities,view(simple)nr</Item>
|
||||
<Synthetic Name="[entities]" ExcludeView="simple">
|
||||
<DisplayString>{ entities.size() }</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="entities.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="to_entity(entities[pos]) == pos">
|
||||
<Item Name="[{ pos }]">entities[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[destroyed]" ExcludeView="simple">
|
||||
<DisplayString>{ to_entity(free_list) != entity_traits::entity_mask }</DisplayString>
|
||||
<Expand>
|
||||
<CustomListItems>
|
||||
<Variable Name="it" InitialValue="to_entity(free_list)" />
|
||||
<Loop>
|
||||
<Break Condition="it == entity_traits::entity_mask"/>
|
||||
<Item Name="[{ it }]">entities[it]</Item>
|
||||
<Exec>it = to_entity(entities[it])</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools_size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems ExcludeView="simple">
|
||||
<Size>pools_size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
<IndexListItems IncludeView="simple">
|
||||
<Size>pools_size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
|
||||
<Synthetic Name="[vars]">
|
||||
<DisplayString>{ vars_size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>vars_size()</Size>
|
||||
<ValueNode>vars.data.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_sparse_set<*>">
|
||||
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||
<Item Name="[policy]">mode,en</Item>
|
||||
<Synthetic Name="[sparse]">
|
||||
<DisplayString>{ sparse.size() * entity_traits::page_size }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="page" InitialValue="0"/>
|
||||
<Variable Name="offset" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="sparse.size() * entity_traits::page_size"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<Exec>page = pos / entity_traits::page_size</Exec>
|
||||
<Exec>offset = pos & (entity_traits::page_size - 1)</Exec>
|
||||
<If Condition="sparse[page] && (*((entity_traits::entity_type *)&sparse[page][offset]) < ~entity_traits::entity_mask)">
|
||||
<Item Name="[{ pos }]">*((entity_traits::entity_type *)&sparse[page][offset]) & entity_traits::entity_mask</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[packed]">
|
||||
<DisplayString>{ packed.size() }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
|
||||
<CustomListItems ExcludeView="simple">
|
||||
<Variable Name="pos" InitialValue="0"/>
|
||||
<Variable Name="last" InitialValue="packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((entity_traits::entity_type *)&packed[pos]) < ~entity_traits::entity_mask">
|
||||
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_storage<*>">
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" Optional="true" ExcludeView="simple">packed.first_base::value.capacity() * comp_traits::page_size</Item>
|
||||
<Item Name="[page size]" Optional="true" ExcludeView="simple">comp_traits::page_size</Item>
|
||||
<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item>
|
||||
<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item>
|
||||
<!-- having SFINAE-like techniques in natvis is priceless :) -->
|
||||
<CustomListItems Condition="packed.first_base::value.size() != 0" Optional="true">
|
||||
<Variable Name="pos" InitialValue="0" />
|
||||
<Variable Name="last" InitialValue="base_type::packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((base_type::entity_traits::entity_type *)&base_type::packed[pos]) < ~base_type::entity_traits::entity_mask">
|
||||
<Item Name="[{ pos }]">packed.first_base::value[pos / comp_traits::page_size][pos & (comp_traits::page_size - 1)]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
</Loop>
|
||||
</CustomListItems>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_view<*>">
|
||||
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[pools]">pools,na</Item>
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::null_t">
|
||||
<DisplayString><null></DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::tombstone_t">
|
||||
<DisplayString><tombstone></DisplayString>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
197
natvis/entt/meta.natvis
Normal file
197
natvis/entt/meta.natvis
Normal file
@@ -0,0 +1,197 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::meta_any">
|
||||
<DisplayString Condition="node != nullptr">{{ type={ node->info->alias,na }, policy={ storage.mode,en } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">*node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_associative_container">
|
||||
<DisplayString Condition="mapped_type_node != nullptr">{{ key_type={ key_type_node->info->alias,na }, mapped_type={ mapped_type_node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString Condition="key_type_node != nullptr">{{ key_type={ key_type_node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_base_node">
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_conv_node">
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_ctor_node">
|
||||
<DisplayString Condition="arg != nullptr">{{ arity={ arity } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_data_node">
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
|
||||
<Synthetic Name="[prop]" Condition="prop != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>prop</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_data">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_func_node" >
|
||||
<Intrinsic Name="has_property" Expression="!!(traits & property)">
|
||||
<Parameter Name="property" Type="int"/>
|
||||
</Intrinsic>
|
||||
<DisplayString Condition="ret != nullptr">{{ arity={ arity }, ret={ ret->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[id]">id</Item>
|
||||
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
|
||||
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
|
||||
<Synthetic Name="[prop]" Condition="prop != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>prop</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_func">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_handle">
|
||||
<DisplayString>{ any }</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_prop_node">
|
||||
<DisplayString Condition="value.node != nullptr">{{ key_type={ id.node->info->alias,na }, mapped_type={ value.node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString Condition="id.node != nullptr">{{ key_type={ id.node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[key]">id</Item>
|
||||
<Item Name="[value]">value</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_prop">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_sequence_container">
|
||||
<DisplayString Condition="value_type_node != nullptr">{{ value_type={ value_type_node->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_template_node">
|
||||
<DisplayString Condition="type != nullptr">{{ type={ type->info->alias,na } }}</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[arity]">arity</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::meta_type_node">
|
||||
<Intrinsic Name="has_property" 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_property(entt::internal::meta_traits::is_arithmetic)</Item>
|
||||
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
|
||||
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
|
||||
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
|
||||
<Item Name="[is_pointer]">has_property(entt::internal::meta_traits::is_pointer)</Item>
|
||||
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
|
||||
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
|
||||
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
|
||||
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
|
||||
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
|
||||
<Item Name="[template_info]" Condition="templ != nullptr">*templ</Item>
|
||||
<Synthetic Name="[ctor]" Condition="ctor != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>ctor</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[base]" Condition="base != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>base</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[conv]" Condition="conv != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>conv</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[data]" Condition="data != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>data</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[func]" Condition="func != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>func</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
<Synthetic Name="[prop]" Condition="prop != nullptr">
|
||||
<Expand>
|
||||
<LinkedListItems>
|
||||
<HeadPointer>prop</HeadPointer>
|
||||
<NextPointer>next</NextPointer>
|
||||
<ValueNode>*this</ValueNode>
|
||||
</LinkedListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::meta_type">
|
||||
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
|
||||
<DisplayString>{{}}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
3
natvis/entt/platform.natvis
Normal file
3
natvis/entt/platform.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
3
natvis/entt/process.natvis
Normal file
3
natvis/entt/process.natvis
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
</AutoVisualizer>
|
||||
15
natvis/entt/resource.natvis
Normal file
15
natvis/entt/resource.natvis
Normal file
@@ -0,0 +1,15 @@
|
||||
<?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<*>">
|
||||
<DisplayString>{ pool.first_base::value }</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>pool.first_base::value</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
@@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::connection">
|
||||
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>
|
||||
</Type>
|
||||
<Type Name="entt::delegate<*>">
|
||||
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
@@ -7,14 +10,18 @@
|
||||
<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>
|
||||
<Type Name="entt::dispatcher">
|
||||
<DisplayString>{{ size={ pools.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>size()</Size>
|
||||
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
<Synthetic Name="[pools]">
|
||||
<DisplayString>{ pools.size() }</DisplayString>
|
||||
<Expand>
|
||||
<IndexListItems>
|
||||
<Size>pools.size()</Size>
|
||||
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
|
||||
</IndexListItems>
|
||||
</Expand>
|
||||
</Synthetic>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::internal::dispatcher_handler<*>">
|
||||
@@ -23,12 +30,6 @@
|
||||
<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>
|
||||
@@ -45,6 +46,7 @@
|
||||
<DisplayString>{{ type={ "$T1" } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[signal]">signal,na</Item>
|
||||
<Item Name="[offset]">offset</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
</AutoVisualizer>
|
||||
@@ -1,28 +0,0 @@
|
||||
#!/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"
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +0,0 @@
|
||||
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,
|
||||
)
|
||||
@@ -3,30 +3,21 @@
|
||||
|
||||
#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
|
||||
#ifndef ENTT_NOEXCEPT
|
||||
# define ENTT_NOEXCEPT noexcept
|
||||
# define ENTT_NOEXCEPT_IF(expr) noexcept(expr)
|
||||
# else
|
||||
# define ENTT_NOEXCEPT_IF(...)
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_USE_ATOMIC
|
||||
@@ -39,8 +30,6 @@
|
||||
#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
|
||||
@@ -53,31 +42,16 @@
|
||||
|
||||
#ifdef ENTT_DISABLE_ASSERT
|
||||
# undef ENTT_ASSERT
|
||||
# define ENTT_ASSERT(condition, msg) (void(0))
|
||||
# define ENTT_ASSERT(...) (void(0))
|
||||
#elif !defined ENTT_ASSERT
|
||||
# include <cassert>
|
||||
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
|
||||
# define ENTT_ASSERT(condition, ...) assert(condition)
|
||||
#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
|
||||
# define ENTT_IGNORE_IF_EMPTY false
|
||||
#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__>
|
||||
# define ENTT_IGNORE_IF_EMPTY true
|
||||
#endif
|
||||
|
||||
#ifdef ENTT_STANDARD_CPP
|
||||
@@ -95,32 +69,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#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
|
||||
|
||||
#if defined _MSC_VER
|
||||
# pragma detect_mismatch("entt.version", ENTT_VERSION)
|
||||
# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY))
|
||||
@@ -128,6 +76,4 @@
|
||||
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
|
||||
#endif
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
#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
|
||||
|
||||
@@ -3,16 +3,12 @@
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 16
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_MINOR 10
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_MAP_HPP
|
||||
#define ENTT_CONTAINER_DENSE_MAP_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -12,7 +13,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
@@ -21,10 +21,12 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits<std::size_t>::max)();
|
||||
namespace internal {
|
||||
|
||||
template<typename Key, typename Type>
|
||||
struct dense_map_node final {
|
||||
@@ -68,111 +70,110 @@ public:
|
||||
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 dense_map_iterator() noexcept
|
||||
dense_map_iterator() ENTT_NOEXCEPT
|
||||
: it{} {}
|
||||
|
||||
constexpr dense_map_iterator(const It iter) noexcept
|
||||
dense_map_iterator(const It iter) ENTT_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_map_iterator(const dense_map_iterator<Other> &other) noexcept
|
||||
dense_map_iterator(const dense_map_iterator<Other> &other) ENTT_NOEXCEPT
|
||||
: it{other.it} {}
|
||||
|
||||
constexpr dense_map_iterator &operator++() noexcept {
|
||||
dense_map_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator operator++(int) noexcept {
|
||||
const dense_map_iterator orig = *this;
|
||||
dense_map_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_map_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator &operator--() noexcept {
|
||||
dense_map_iterator &operator--() ENTT_NOEXCEPT {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator operator--(int) noexcept {
|
||||
const dense_map_iterator orig = *this;
|
||||
dense_map_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
dense_map_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator &operator+=(const difference_type value) noexcept {
|
||||
dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator operator+(const difference_type value) const noexcept {
|
||||
dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
dense_map_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator &operator-=(const difference_type value) noexcept {
|
||||
dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
constexpr dense_map_iterator operator-(const difference_type value) const noexcept {
|
||||
dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
return {it[value].element.first, it[value].element.second};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return operator[](0);
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return {it->element.first, it->element.second};
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept;
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it - rhs.it;
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.it < rhs.it;
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs > rhs);
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -190,58 +191,62 @@ public:
|
||||
using reference = value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr dense_map_local_iterator() noexcept = default;
|
||||
dense_map_local_iterator() ENTT_NOEXCEPT
|
||||
: it{},
|
||||
offset{} {}
|
||||
|
||||
constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept
|
||||
dense_map_local_iterator(It iter, const std::size_t pos) ENTT_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_map_local_iterator(const dense_map_local_iterator<Other> &other) noexcept
|
||||
dense_map_local_iterator(const dense_map_local_iterator<Other> &other) ENTT_NOEXCEPT
|
||||
: it{other.it},
|
||||
offset{other.offset} {}
|
||||
|
||||
constexpr dense_map_local_iterator &operator++() noexcept {
|
||||
return (offset = it[static_cast<typename It::difference_type>(offset)].next), *this;
|
||||
dense_map_local_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return offset = it[offset].next, *this;
|
||||
}
|
||||
|
||||
constexpr dense_map_local_iterator operator++(int) noexcept {
|
||||
const dense_map_local_iterator orig = *this;
|
||||
dense_map_local_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_map_local_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
const auto idx = static_cast<typename It::difference_type>(offset);
|
||||
return {it[idx].element.first, it[idx].element.second};
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return {it[offset].element.first, it[offset].element.second};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t index() const noexcept {
|
||||
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
It it{};
|
||||
std::size_t offset{dense_map_placeholder_position};
|
||||
It it;
|
||||
std::size_t offset;
|
||||
};
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.index() == rhs.index();
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Associative container for key-value pairs with unique keys.
|
||||
@@ -260,25 +265,23 @@ template<typename Key, typename Type, typename Hash, typename KeyEqual, typename
|
||||
class dense_map {
|
||||
static constexpr float default_threshold = 0.875f;
|
||||
static constexpr std::size_t minimum_capacity = 8u;
|
||||
static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position;
|
||||
|
||||
using node_type = internal::dense_map_node<Key, Type>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, 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 key_to_bucket(const Other &key) const noexcept {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
|
||||
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const ENTT_NOEXCEPT {
|
||||
return fast_mod(sparse.second()(key), bucket_count());
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
|
||||
if(packed.second()(packed.first()[offset].element.first, key)) {
|
||||
return begin() + static_cast<typename iterator::difference_type>(offset);
|
||||
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) {
|
||||
for(auto it = begin(bucket), last = end(bucket); it != last; ++it) {
|
||||
if(packed.second()(it->first, key)) {
|
||||
return begin() + static_cast<typename iterator::difference_type>(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -286,10 +289,10 @@ class dense_map {
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) {
|
||||
if(packed.second()(packed.first()[offset].element.first, key)) {
|
||||
return cbegin() + static_cast<typename const_iterator::difference_type>(offset);
|
||||
[[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const {
|
||||
for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) {
|
||||
if(packed.second()(it->first, key)) {
|
||||
return cbegin() + static_cast<typename iterator::difference_type>(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,8 +332,8 @@ class dense_map {
|
||||
|
||||
void move_and_pop(const std::size_t pos) {
|
||||
if(const auto last = size() - 1u; pos != last) {
|
||||
size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
|
||||
packed.first()[pos] = std::move(packed.first().back());
|
||||
size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
|
||||
for(; *curr != last; curr = &packed.first()[*curr].next) {}
|
||||
*curr = pos;
|
||||
}
|
||||
@@ -339,14 +342,12 @@ class dense_map {
|
||||
}
|
||||
|
||||
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);
|
||||
if(size() > (bucket_count() * max_load_factor())) {
|
||||
rehash(bucket_count() * 2u);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Key type of the container. */
|
||||
using key_type = Key;
|
||||
/*! @brief Mapped type of the container. */
|
||||
@@ -355,12 +356,12 @@ public:
|
||||
using value_type = std::pair<const Key, 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 keys. */
|
||||
using hasher = Hash;
|
||||
/*! @brief Type of function to use to compare the keys for equality. */
|
||||
using key_equal = KeyEqual;
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Input iterator type. */
|
||||
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
|
||||
/*! @brief Constant input iterator type. */
|
||||
@@ -372,7 +373,7 @@ public:
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
dense_map()
|
||||
: dense_map{minimum_capacity} {}
|
||||
: dense_map(minimum_capacity) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
@@ -384,34 +385,35 @@ public:
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator and user
|
||||
* supplied minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param bucket_count Minimal number of buckets.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_map(const size_type cnt, const allocator_type &allocator)
|
||||
: dense_map{cnt, hasher{}, key_equal{}, allocator} {}
|
||||
dense_map(const size_type bucket_count, const allocator_type &allocator)
|
||||
: dense_map{bucket_count, 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 bucket_count Minimal number of buckets.
|
||||
* @param hash Hash function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_map{cnt, hash, key_equal{}, allocator} {}
|
||||
dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_map{bucket_count, 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 bucket_count Minimal number of buckets.
|
||||
* @param hash Hash function to use.
|
||||
* @param equal Compare function to use.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
explicit dense_map(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal} {
|
||||
rehash(cnt);
|
||||
packed{allocator, equal},
|
||||
threshold{default_threshold} {
|
||||
rehash(bucket_count);
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
@@ -428,7 +430,7 @@ public:
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
dense_map(dense_map &&) noexcept = default;
|
||||
dense_map(dense_map &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -440,9 +442,6 @@ public:
|
||||
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_map() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This container.
|
||||
@@ -453,64 +452,59 @@ public:
|
||||
* @brief Default move assignment operator.
|
||||
* @return This container.
|
||||
*/
|
||||
dense_map &operator=(dense_map &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_map &other) noexcept {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
dense_map &operator=(dense_map &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
|
||||
return sparse.first().get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* 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 {
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
return packed.first().begin();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
return packed.first().begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const noexcept {
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return packed.first().end();
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const noexcept {
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
[[nodiscard]] iterator end() ENTT_NOEXCEPT {
|
||||
return packed.first().end();
|
||||
}
|
||||
|
||||
@@ -518,7 +512,7 @@ public:
|
||||
* @brief Checks whether a container is empty.
|
||||
* @return True if the container is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return packed.first().empty();
|
||||
}
|
||||
|
||||
@@ -526,20 +520,12 @@ public:
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @return Number of elements in a container.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
[[nodiscard]] size_type size() const ENTT_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 {
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(0u);
|
||||
@@ -684,7 +670,7 @@ public:
|
||||
const auto dist = first - cbegin();
|
||||
|
||||
for(auto from = last - cbegin(); from != dist; --from) {
|
||||
erase(packed.first()[static_cast<size_type>(from) - 1u].element.first);
|
||||
erase(packed.first()[from - 1u].element.first);
|
||||
}
|
||||
|
||||
return (begin() + dist);
|
||||
@@ -696,7 +682,7 @@ public:
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
size_type erase(const key_type &key) {
|
||||
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) {
|
||||
for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
|
||||
if(packed.second()(packed.first()[*curr].element.first, key)) {
|
||||
const auto index = *curr;
|
||||
*curr = packed.first()[*curr].next;
|
||||
@@ -708,6 +694,17 @@ public:
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
void swap(dense_map &other) {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses a given element with bounds checking.
|
||||
* @param key A key of an element to find.
|
||||
@@ -726,29 +723,6 @@ public:
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses a given element with bounds checking.
|
||||
* @tparam Other Type of the key of an element to find.
|
||||
* @param key A key of an element to find.
|
||||
* @return A reference to the mapped value of the requested element.
|
||||
*/
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type const &>>
|
||||
at(const Other &key) const {
|
||||
auto it = find(key);
|
||||
ENTT_ASSERT(it != cend(), "Invalid key");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/*! @copydoc at */
|
||||
template<typename Other>
|
||||
[[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, mapped_type &>>
|
||||
at(const Other &key) {
|
||||
auto it = find(key);
|
||||
ENTT_ASSERT(it != end(), "Invalid key");
|
||||
return it->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Accesses or inserts a given element.
|
||||
* @param key A key of an element to find or insert.
|
||||
@@ -767,27 +741,6 @@ public:
|
||||
return insert_or_do_nothing(std::move(key)).first->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements matching a key (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 key_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 key.
|
||||
* @param key Key value of an element to search for.
|
||||
@@ -805,7 +758,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a key that compares _equivalent_ to a given
|
||||
* key.
|
||||
* value.
|
||||
* @tparam Other Type of the key value of an element to search for.
|
||||
* @param key Key value of an element to search for.
|
||||
* @return An iterator to an element with the given key. If no such element
|
||||
@@ -824,46 +777,6 @@ public:
|
||||
return constrained_find(key, key_to_bucket(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range containing all elements with a given key.
|
||||
* @param key Key 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 key_type &key) {
|
||||
const auto it = find(key);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
[[nodiscard]] std::pair<const_iterator, const_iterator> equal_range(const key_type &key) const {
|
||||
const auto it = find(key);
|
||||
return {it, it + !(it == cend())};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range containing all elements that compare _equivalent_
|
||||
* to a given key.
|
||||
* @tparam Other Type of an element to search for.
|
||||
* @param key Key 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 &key) {
|
||||
const auto it = find(key);
|
||||
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 &key) const {
|
||||
const auto it = find(key);
|
||||
return {it, it + !(it == cend())};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if the container contains an element with a given key.
|
||||
* @param key Key value of an element to search for.
|
||||
@@ -919,7 +832,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||
return {};
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -927,7 +840,7 @@ public:
|
||||
* @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 {
|
||||
[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
|
||||
return cend(index);
|
||||
}
|
||||
|
||||
@@ -937,7 +850,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||
return {};
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -979,7 +892,7 @@ public:
|
||||
* @return The average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float load_factor() const {
|
||||
return static_cast<float>(size()) / static_cast<float>(bucket_count());
|
||||
return size() / static_cast<float>(bucket_count());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1003,19 +916,15 @@ public:
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param cnt New number of buckets.
|
||||
* @param count 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;
|
||||
void rehash(const size_type count) {
|
||||
auto value = (std::max)(count, minimum_capacity);
|
||||
value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
|
||||
|
||||
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = placeholder_position;
|
||||
}
|
||||
std::fill(sparse.first().begin(), sparse.first().end(), (std::numeric_limits<size_type>::max)());
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
const auto index = key_to_bucket(packed.first()[pos].element.first);
|
||||
@@ -1027,11 +936,11 @@ public:
|
||||
/**
|
||||
* @brief Reserves space for at least the specified number of elements and
|
||||
* regenerates the hash table.
|
||||
* @param cnt New number of elements.
|
||||
* @param count 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())));
|
||||
void reserve(const size_type count) {
|
||||
packed.first().reserve(count);
|
||||
rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1053,12 +962,16 @@ public:
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold{default_threshold};
|
||||
float threshold;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename Key, typename Value, typename Allocator>
|
||||
@@ -1066,6 +979,10 @@ struct uses_allocator<entt::internal::dense_map_node<Key, Value>, Allocator>
|
||||
: std::true_type {};
|
||||
|
||||
} // namespace std
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
|
||||
#define ENTT_CONTAINER_DENSE_SET_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -12,17 +13,19 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/memory.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits<std::size_t>::max)();
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
class dense_set_iterator final {
|
||||
@@ -36,109 +39,109 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::random_access_iterator_tag;
|
||||
|
||||
constexpr dense_set_iterator() noexcept
|
||||
dense_set_iterator() ENTT_NOEXCEPT
|
||||
: it{} {}
|
||||
|
||||
constexpr dense_set_iterator(const It iter) noexcept
|
||||
dense_set_iterator(const It iter) ENTT_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
|
||||
dense_set_iterator(const dense_set_iterator<Other> &other) ENTT_NOEXCEPT
|
||||
: it{other.it} {}
|
||||
|
||||
constexpr dense_set_iterator &operator++() noexcept {
|
||||
dense_set_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return ++it, *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator++(int) noexcept {
|
||||
const dense_set_iterator orig = *this;
|
||||
dense_set_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
dense_set_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator &operator--() noexcept {
|
||||
dense_set_iterator &operator--() ENTT_NOEXCEPT {
|
||||
return --it, *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator--(int) noexcept {
|
||||
const dense_set_iterator orig = *this;
|
||||
dense_set_iterator operator--(int) ENTT_NOEXCEPT {
|
||||
dense_set_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator &operator+=(const difference_type value) noexcept {
|
||||
dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT {
|
||||
it += value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator+(const difference_type value) const noexcept {
|
||||
dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT {
|
||||
dense_set_iterator copy = *this;
|
||||
return (copy += value);
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator &operator-=(const difference_type value) noexcept {
|
||||
dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT {
|
||||
return (*this += -value);
|
||||
}
|
||||
|
||||
constexpr dense_set_iterator operator-(const difference_type value) const noexcept {
|
||||
dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT {
|
||||
return (*this + -value);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
|
||||
[[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT {
|
||||
return it[value].second;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return std::addressof(operator[](0));
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return std::addressof(it->second);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return operator[](0);
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_NOEXCEPT;
|
||||
|
||||
template<typename Lhs, typename Rhs>
|
||||
friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept;
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -154,55 +157,61 @@ public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
constexpr dense_set_local_iterator() noexcept = default;
|
||||
dense_set_local_iterator() ENTT_NOEXCEPT
|
||||
: it{},
|
||||
offset{} {}
|
||||
|
||||
constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept
|
||||
dense_set_local_iterator(It iter, const std::size_t pos) ENTT_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
|
||||
dense_set_local_iterator(const dense_set_local_iterator<Other> &other) ENTT_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;
|
||||
dense_set_local_iterator &operator++() ENTT_NOEXCEPT {
|
||||
return offset = it[offset].first, *this;
|
||||
}
|
||||
|
||||
constexpr dense_set_local_iterator operator++(int) noexcept {
|
||||
const dense_set_local_iterator orig = *this;
|
||||
dense_set_local_iterator operator++(int) ENTT_NOEXCEPT {
|
||||
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]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return std::addressof(it[offset].second);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::size_t index() const noexcept {
|
||||
[[nodiscard]] std::size_t index() const ENTT_NOEXCEPT {
|
||||
return offset;
|
||||
}
|
||||
|
||||
private:
|
||||
It it{};
|
||||
std::size_t offset{dense_set_placeholder_position};
|
||||
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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_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 {
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Associative container for unique objects of a given type.
|
||||
@@ -220,7 +229,6 @@ 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;
|
||||
static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position;
|
||||
|
||||
using node_type = std::pair<std::size_t, Type>;
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
@@ -229,15 +237,15 @@ class dense_set {
|
||||
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());
|
||||
[[nodiscard]] std::size_t value_to_bucket(const Other &value) const ENTT_NOEXCEPT {
|
||||
return fast_mod(sparse.second()(value), bucket_count());
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
|
||||
if(packed.second()(packed.first()[offset].second, value)) {
|
||||
return begin() + static_cast<typename iterator::difference_type>(offset);
|
||||
[[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<typename iterator::difference_type>(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,10 +253,10 @@ class dense_set {
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const {
|
||||
for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) {
|
||||
if(packed.second()(packed.first()[offset].second, value)) {
|
||||
return cbegin() + static_cast<typename const_iterator::difference_type>(offset);
|
||||
[[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<typename iterator::difference_type>(it.index());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,8 +280,8 @@ class dense_set {
|
||||
|
||||
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());
|
||||
size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
|
||||
for(; *curr != last; curr = &packed.first()[*curr].first) {}
|
||||
*curr = pos;
|
||||
}
|
||||
@@ -282,34 +290,28 @@ class dense_set {
|
||||
}
|
||||
|
||||
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);
|
||||
if(size() > (bucket_count() * max_load_factor())) {
|
||||
rehash(bucket_count() * 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 Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @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. */
|
||||
@@ -317,7 +319,7 @@ public:
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
dense_set()
|
||||
: dense_set{minimum_capacity} {}
|
||||
: dense_set(minimum_capacity) {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator.
|
||||
@@ -329,34 +331,35 @@ public:
|
||||
/**
|
||||
* @brief Constructs an empty container with a given allocator and user
|
||||
* supplied minimal number of buckets.
|
||||
* @param cnt Minimal number of buckets.
|
||||
* @param bucket_count 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} {}
|
||||
dense_set(const size_type bucket_count, const allocator_type &allocator)
|
||||
: dense_set{bucket_count, 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 bucket_count 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} {}
|
||||
dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator)
|
||||
: dense_set{bucket_count, 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 bucket_count 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{})
|
||||
explicit dense_set(const size_type bucket_count, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
|
||||
: sparse{allocator, hash},
|
||||
packed{allocator, equal} {
|
||||
rehash(cnt);
|
||||
packed{allocator, equal},
|
||||
threshold{default_threshold} {
|
||||
rehash(bucket_count);
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
@@ -373,7 +376,7 @@ public:
|
||||
threshold{other.threshold} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
dense_set(dense_set &&) noexcept = default;
|
||||
dense_set(dense_set &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -385,9 +388,6 @@ public:
|
||||
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.
|
||||
@@ -398,112 +398,67 @@ public:
|
||||
* @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);
|
||||
}
|
||||
dense_set &operator=(dense_set &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
|
||||
return sparse.first().get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
*
|
||||
* The returned iterator points to the first instance of the internal array.
|
||||
* 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 {
|
||||
[[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT {
|
||||
return packed.first().begin();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
[[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
[[nodiscard]] iterator begin() ENTT_NOEXCEPT {
|
||||
return packed.first().begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the end.
|
||||
*
|
||||
* The returned iterator points to the element following the last instance
|
||||
* of the internal array. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the element following the last instance of the
|
||||
* internal array.
|
||||
*/
|
||||
[[nodiscard]] const_iterator cend() const noexcept {
|
||||
[[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT {
|
||||
return packed.first().end();
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const noexcept {
|
||||
[[nodiscard]] const_iterator end() const ENTT_NOEXCEPT {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
[[nodiscard]] iterator end() ENTT_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 {
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return packed.first().empty();
|
||||
}
|
||||
|
||||
@@ -511,20 +466,12 @@ public:
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @return Number of elements in a container.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
[[nodiscard]] size_type size() const ENTT_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 {
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
sparse.first().clear();
|
||||
packed.first().clear();
|
||||
rehash(0u);
|
||||
@@ -574,7 +521,7 @@ public:
|
||||
*/
|
||||
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>)) {
|
||||
if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v<std::remove_cv_t<std::remove_reference_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)...));
|
||||
@@ -613,7 +560,7 @@ public:
|
||||
const auto dist = first - cbegin();
|
||||
|
||||
for(auto from = last - cbegin(); from != dist; --from) {
|
||||
erase(packed.first()[static_cast<size_type>(from) - 1u].second);
|
||||
erase(packed.first()[from - 1u].second);
|
||||
}
|
||||
|
||||
return (begin() + dist);
|
||||
@@ -625,7 +572,7 @@ public:
|
||||
* @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 != placeholder_position; curr = &packed.first()[*curr].first) {
|
||||
for(size_type *curr = sparse.first().data() + 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;
|
||||
@@ -638,24 +585,14 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @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).
|
||||
* @brief Exchanges the contents with those of a given container.
|
||||
* @param other Container to exchange the content with.
|
||||
*/
|
||||
[[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();
|
||||
void swap(dense_set &other) {
|
||||
using std::swap;
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(threshold, other.threshold);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,46 +630,6 @@ public:
|
||||
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.
|
||||
@@ -788,7 +685,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const {
|
||||
return {};
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -796,7 +693,7 @@ public:
|
||||
* @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 {
|
||||
[[nodiscard]] const_local_iterator end([[maybe_unused]] const size_type index) const {
|
||||
return cend(index);
|
||||
}
|
||||
|
||||
@@ -806,7 +703,7 @@ public:
|
||||
* @return An iterator to the end of the given bucket.
|
||||
*/
|
||||
[[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) {
|
||||
return {};
|
||||
return {packed.first().begin(), (std::numeric_limits<size_type>::max)()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -848,7 +745,7 @@ public:
|
||||
* @return The average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float load_factor() const {
|
||||
return static_cast<float>(size()) / static_cast<float>(bucket_count());
|
||||
return size() / static_cast<float>(bucket_count());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -872,19 +769,15 @@ public:
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param cnt New number of buckets.
|
||||
* @param count 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;
|
||||
void rehash(const size_type count) {
|
||||
auto value = (std::max)(count, minimum_capacity);
|
||||
value = (std::max)(value, static_cast<size_type>(size() / max_load_factor()));
|
||||
|
||||
if(const auto sz = next_power_of_two(value); sz != bucket_count()) {
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = placeholder_position;
|
||||
}
|
||||
std::fill(sparse.first().begin(), sparse.first().end(), (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);
|
||||
@@ -896,11 +789,11 @@ public:
|
||||
/**
|
||||
* @brief Reserves space for at least the specified number of elements and
|
||||
* regenerates the hash table.
|
||||
* @param cnt New number of elements.
|
||||
* @param count 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())));
|
||||
void reserve(const size_type count) {
|
||||
packed.first().reserve(count);
|
||||
rehash(static_cast<size_type>(std::ceil(count / max_load_factor())));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -922,7 +815,7 @@ public:
|
||||
private:
|
||||
compressed_pair<sparse_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold{default_threshold};
|
||||
float threshold;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -12,27 +10,17 @@ template<
|
||||
typename Key,
|
||||
typename Type,
|
||||
typename = std::hash<Key>,
|
||||
typename = std::equal_to<>,
|
||||
typename = std::equal_to<Key>,
|
||||
typename = std::allocator<std::pair<const Key, Type>>>
|
||||
class dense_map;
|
||||
|
||||
template<
|
||||
typename Type,
|
||||
typename = std::hash<Type>,
|
||||
typename = std::equal_to<>,
|
||||
typename = std::equal_to<Type>,
|
||||
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
|
||||
|
||||
@@ -1,462 +0,0 @@
|
||||
#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
|
||||
@@ -58,11 +58,9 @@ struct insertion_sort {
|
||||
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);
|
||||
}
|
||||
@@ -97,37 +95,31 @@ struct radix_sort {
|
||||
template<typename It, typename Getter = identity>
|
||||
void operator()(It first, It last, Getter getter = Getter{}) const {
|
||||
if(first < last) {
|
||||
constexpr auto passes = N / Bit;
|
||||
static constexpr auto mask = (1 << Bit) - 1;
|
||||
static constexpr auto buckets = 1 << Bit;
|
||||
static 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)));
|
||||
std::vector<value_type> aux(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 index[buckets]{};
|
||||
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);
|
||||
out[index[(getter(*it) >> start) & mask]++] = std::move(*it);
|
||||
}
|
||||
};
|
||||
|
||||
for(std::size_t pass = 0; pass < (passes & ~1u); pass += 2) {
|
||||
for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) {
|
||||
part(first, last, aux.begin(), pass * Bit);
|
||||
part(aux.begin(), aux.end(), first, (pass + 1) * Bit);
|
||||
}
|
||||
|
||||
@@ -6,187 +6,145 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/utility.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "type_info.hpp"
|
||||
#include "type_traits.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
enum class any_request : std::uint8_t {
|
||||
info,
|
||||
transfer,
|
||||
assign,
|
||||
compare,
|
||||
copy,
|
||||
move
|
||||
};
|
||||
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
struct basic_any_storage {
|
||||
static constexpr bool has_buffer = true;
|
||||
union {
|
||||
const void *instance{};
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
alignas(Align) std::byte buffer[Len];
|
||||
};
|
||||
};
|
||||
|
||||
template<std::size_t Align>
|
||||
struct basic_any_storage<0u, Align> {
|
||||
static constexpr bool has_buffer = false;
|
||||
const void *instance{};
|
||||
};
|
||||
|
||||
template<typename Type, std::size_t Len, std::size_t Align>
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>> {};
|
||||
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
struct in_situ<void, Len, Align>: std::false_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||
* @tparam Len Size of the buffer reserved for the small buffer optimization.
|
||||
* @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: private internal::basic_any_storage<Len, Align> {
|
||||
using request = internal::any_request;
|
||||
using base_type = internal::basic_any_storage<Len, Align>;
|
||||
using vtable_type = const void *(const request, const basic_any &, const void *);
|
||||
using deleter_type = void(const basic_any &);
|
||||
class basic_any {
|
||||
enum class operation : std::uint8_t {
|
||||
copy,
|
||||
move,
|
||||
transfer,
|
||||
assign,
|
||||
destroy,
|
||||
compare,
|
||||
get
|
||||
};
|
||||
|
||||
enum class policy : std::uint8_t {
|
||||
owner,
|
||||
ref,
|
||||
cref
|
||||
};
|
||||
|
||||
using storage_type = std::aligned_storage_t<Len + !Len, Align>;
|
||||
using vtable_type = const void *(const operation, const basic_any &, const void *);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
|
||||
static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && 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_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
|
||||
static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
const Type *element = nullptr;
|
||||
|
||||
switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
|
||||
case request::info:
|
||||
return &type_id<Type>();
|
||||
case request::transfer:
|
||||
if constexpr(in_situ<Type>) {
|
||||
element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
element = static_cast<const Type *>(value.instance);
|
||||
}
|
||||
|
||||
switch(op) {
|
||||
case operation::copy:
|
||||
if constexpr(std::is_copy_constructible_v<Type>) {
|
||||
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
|
||||
}
|
||||
break;
|
||||
case operation::move:
|
||||
if constexpr(in_situ<Type>) {
|
||||
if(value.owner()) {
|
||||
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
|
||||
}
|
||||
}
|
||||
|
||||
return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
|
||||
case operation::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)));
|
||||
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||
return other;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case request::assign:
|
||||
case operation::assign:
|
||||
if constexpr(std::is_copy_assignable_v<Type>) {
|
||||
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
|
||||
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
|
||||
return other;
|
||||
}
|
||||
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;
|
||||
case operation::destroy:
|
||||
if constexpr(in_situ<Type>) {
|
||||
element->~Type();
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] element;
|
||||
} 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);
|
||||
delete element;
|
||||
}
|
||||
break;
|
||||
case request::move:
|
||||
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
|
||||
if constexpr(in_situ_v<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
|
||||
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{std::move(*const_cast<Type *>(elem))};
|
||||
case operation::compare:
|
||||
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
|
||||
} else {
|
||||
return (element == other) ? other : nullptr;
|
||||
}
|
||||
case operation::get:
|
||||
return element;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
static void basic_deleter(const basic_any &value) {
|
||||
static_assert(std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v<Type>), "Unexpected policy");
|
||||
|
||||
const auto *elem = static_cast<const Type *>(value.data());
|
||||
|
||||
if constexpr(in_situ_v<Type>) {
|
||||
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
|
||||
} else if constexpr(std::is_array_v<Type>) {
|
||||
delete[] elem;
|
||||
} else {
|
||||
delete elem;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void initialize([[maybe_unused]] Args &&...args) {
|
||||
using plain_type = std::remove_const_t<std::remove_reference_t<Type>>;
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
|
||||
|
||||
vtable = basic_vtable<plain_type>;
|
||||
underlying_type = type_hash<plain_type>::value();
|
||||
|
||||
if constexpr(std::is_void_v<Type>) {
|
||||
deleter = nullptr;
|
||||
mode = any_policy::empty;
|
||||
this->instance = nullptr;
|
||||
} else if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
deleter = nullptr;
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||
this->instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ_v<plain_type>) {
|
||||
if constexpr(std::is_trivially_destructible_v<plain_type>) {
|
||||
deleter = nullptr;
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
|
||||
instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ<Type>) {
|
||||
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
|
||||
new(&storage) Type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
new(&storage) Type(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
deleter = &basic_deleter<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(&this->buffer) plain_type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
::new(&this->buffer) plain_type(std::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
deleter = &basic_deleter<plain_type>;
|
||||
mode = any_policy::dynamic;
|
||||
|
||||
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
|
||||
this->instance = new plain_type{std::forward<Args>(args)...};
|
||||
} else if constexpr(std::is_array_v<plain_type>) {
|
||||
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
|
||||
this->instance = new plain_type[std::extent_v<plain_type>]();
|
||||
} else {
|
||||
this->instance = new plain_type(std::forward<Args>(args)...);
|
||||
if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
|
||||
instance = new Type{std::forward<Args>(args)...};
|
||||
} else {
|
||||
instance = new Type(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void invoke_deleter_if_exists() {
|
||||
if(deleter != nullptr) {
|
||||
deleter(*this);
|
||||
}
|
||||
}
|
||||
basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
|
||||
: instance{other.data()},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
mode{pol} {}
|
||||
|
||||
public:
|
||||
/*! @brief Size of the internal buffer. */
|
||||
/*! @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>} {}
|
||||
constexpr basic_any() ENTT_NOEXCEPT
|
||||
: instance{},
|
||||
info{&type_id<void>()},
|
||||
vtable{},
|
||||
mode{policy::owner} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper by directly initializing the new object.
|
||||
@@ -196,29 +154,10 @@ public:
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_any(std::in_place_type_t<Type>, Args &&...args)
|
||||
: base_type{} {
|
||||
: basic_any{} {
|
||||
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)
|
||||
: base_type{} {
|
||||
static_assert(!std::is_const_v<Type> && !std::is_void_v<Type>, "Non-const non-void pointer required");
|
||||
|
||||
if(value == nullptr) {
|
||||
initialize<void>();
|
||||
} else {
|
||||
initialize<Type &>(*value);
|
||||
deleter = &basic_deleter<Type>;
|
||||
mode = any_policy::dynamic;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper from a given value.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
@@ -226,7 +165,9 @@ public:
|
||||
*/
|
||||
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)} {}
|
||||
: basic_any{} {
|
||||
initialize<std::decay_t<Type>>(std::forward<Type>(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy constructor.
|
||||
@@ -234,29 +175,30 @@ public:
|
||||
*/
|
||||
basic_any(const basic_any &other)
|
||||
: basic_any{} {
|
||||
other.vtable(request::copy, other, this);
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_any(basic_any &&other) noexcept
|
||||
: base_type{},
|
||||
basic_any(basic_any &&other) ENTT_NOEXCEPT
|
||||
: instance{},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
deleter{other.deleter},
|
||||
underlying_type{other.underlying_type},
|
||||
mode{other.mode} {
|
||||
if(other.mode == any_policy::embedded) {
|
||||
other.vtable(request::move, other, this);
|
||||
} else if(other.mode != any_policy::empty) {
|
||||
this->instance = std::exchange(other.instance, nullptr);
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::move, other, this);
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Frees the internal buffer, whatever it means. */
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
~basic_any() {
|
||||
invoke_deleter_if_exists();
|
||||
if(vtable && owner()) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -265,14 +207,10 @@ public:
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(const basic_any &other) {
|
||||
if(this != &other) {
|
||||
invoke_deleter_if_exists();
|
||||
reset();
|
||||
|
||||
if(other) {
|
||||
other.vtable(request::copy, other, this);
|
||||
} else {
|
||||
initialize<void>();
|
||||
}
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::copy, other, this);
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -283,19 +221,13 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @return This any object.
|
||||
*/
|
||||
basic_any &operator=(basic_any &&other) noexcept {
|
||||
if(this != &other) {
|
||||
invoke_deleter_if_exists();
|
||||
|
||||
if(other.mode == any_policy::embedded) {
|
||||
other.vtable(request::move, other, this);
|
||||
} else if(other.mode != any_policy::empty) {
|
||||
this->instance = std::exchange(other.instance, nullptr);
|
||||
}
|
||||
basic_any &operator=(basic_any &&other) ENTT_NOEXCEPT {
|
||||
reset();
|
||||
|
||||
if(other.vtable) {
|
||||
other.vtable(operation::move, other, this);
|
||||
info = other.info;
|
||||
vtable = other.vtable;
|
||||
deleter = other.deleter;
|
||||
underlying_type = other.underlying_type;
|
||||
mode = other.mode;
|
||||
}
|
||||
|
||||
@@ -308,67 +240,27 @@ public:
|
||||
* @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) {
|
||||
template<typename Type>
|
||||
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 false if a wrapper is empty, true otherwise.
|
||||
* @return False if the wrapper is empty, true otherwise.
|
||||
* @brief Returns the object type if any, `type_id<void>()` otherwise.
|
||||
* @return The object type if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool has_value() const noexcept {
|
||||
return (mode != any_policy::empty);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if the wrapper does not contain the expected type,
|
||||
* true otherwise.
|
||||
* @param req Expected type.
|
||||
* @return False if the wrapper does not contain the expected type, true
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool has_value(const type_info &req) const noexcept {
|
||||
return (underlying_type == req.hash());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns false if the wrapper does not contain the expected type,
|
||||
* true otherwise.
|
||||
* @tparam Type Expected type.
|
||||
* @return False if the wrapper does not contain the expected type, true
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool has_value() const noexcept {
|
||||
static_assert(std::is_same_v<std::remove_const_t<Type>, Type>, "Invalid type");
|
||||
return (underlying_type == type_hash<Type>::value());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
|
||||
* @return The object type info if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return *static_cast<const type_info *>(vtable(request::info, *this, nullptr));
|
||||
}
|
||||
|
||||
/*! @copydoc info */
|
||||
[[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept {
|
||||
return info();
|
||||
[[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
|
||||
return *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 {
|
||||
if constexpr(base_type::has_buffer) {
|
||||
return (mode == any_policy::embedded) ? &this->buffer : this->instance;
|
||||
} else {
|
||||
return this->instance;
|
||||
}
|
||||
[[nodiscard]] const void *data() const ENTT_NOEXCEPT {
|
||||
return vtable ? vtable(operation::get, *this, nullptr) : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -376,26 +268,16 @@ public:
|
||||
* @param req Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data(const type_info &req) const noexcept {
|
||||
return has_value(req) ? data() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an opaque pointer to the contained instance.
|
||||
* @tparam Type Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const Type *data() const noexcept {
|
||||
return has_value<std::remove_const_t<Type>>() ? static_cast<const Type *>(data()) : nullptr;
|
||||
[[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
|
||||
return *info == 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());
|
||||
[[nodiscard]] void *data() ENTT_NOEXCEPT {
|
||||
return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -403,22 +285,8 @@ public:
|
||||
* @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 Returns an opaque pointer to the contained instance.
|
||||
* @tparam Type Expected type.
|
||||
* @return An opaque pointer the contained instance, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] Type *data() noexcept {
|
||||
if constexpr(std::is_const_v<Type>) {
|
||||
return std::as_const(*this).template data<std::remove_const_t<Type>>();
|
||||
} else {
|
||||
return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(std::as_const(*this).template data<std::remove_const_t<Type>>());
|
||||
}
|
||||
[[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
|
||||
return *info == req ? data() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -429,7 +297,7 @@ public:
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
invoke_deleter_if_exists();
|
||||
reset();
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -438,19 +306,22 @@ public:
|
||||
* @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(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
|
||||
return (vtable(request::assign, *this, other.data()) != nullptr);
|
||||
bool assign(const any &other) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
return (vtable(operation::assign, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*! @copydoc assign */
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
bool assign(basic_any &&other) {
|
||||
if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) {
|
||||
return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr);
|
||||
bool assign(any &&other) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(operation::transfer, *this, val) != nullptr);
|
||||
} else {
|
||||
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -458,16 +329,21 @@ public:
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
invoke_deleter_if_exists();
|
||||
initialize<void>();
|
||||
if(vtable && owner()) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
|
||||
info = &type_id<void>();
|
||||
vtable = nullptr;
|
||||
mode = policy::owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 has_value();
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return vtable != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,76 +351,68 @@ public:
|
||||
* @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(other && (underlying_type == other.underlying_type)) {
|
||||
return (vtable(request::compare, *this, other.data()) != nullptr);
|
||||
bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
|
||||
if(vtable && *info == *other.info) {
|
||||
return (vtable(operation::compare, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
return (!*this && !other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
return (!vtable && !other.vtable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Aliasing constructor.
|
||||
* @return A wrapper that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_any as_ref() noexcept {
|
||||
basic_any other = std::as_const(*this).as_ref();
|
||||
other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref);
|
||||
return other;
|
||||
[[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
|
||||
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const noexcept {
|
||||
basic_any other{};
|
||||
other.instance = data();
|
||||
other.vtable = vtable;
|
||||
other.underlying_type = underlying_type;
|
||||
other.mode = any_policy::cref;
|
||||
return other;
|
||||
[[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
|
||||
return basic_any{*this, 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;
|
||||
[[nodiscard]] bool owner() const ENTT_NOEXCEPT {
|
||||
return (mode == policy::owner);
|
||||
}
|
||||
|
||||
private:
|
||||
vtable_type *vtable{};
|
||||
deleter_type *deleter{};
|
||||
id_type underlying_type{};
|
||||
any_policy mode{};
|
||||
union {
|
||||
const void *instance;
|
||||
storage_type storage;
|
||||
};
|
||||
const type_info *info;
|
||||
vtable_type *vtable;
|
||||
policy mode;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Checks if two wrappers differ in their content.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
* @tparam Align Alignment requirement.
|
||||
* @param lhs A wrapper, either empty or not.
|
||||
* @param rhs A wrapper, either empty or not.
|
||||
* @return True if the two wrappers differ in their content, false otherwise.
|
||||
*/
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
[[nodiscard]] inline bool operator!=(const basic_any<Len, Align> &lhs, const basic_any<Len, Align> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Performs type-safe access to the contained object.
|
||||
* @tparam Type Type to which conversion is required.
|
||||
* @tparam Len Size of the buffer reserved for the small buffer optimization.
|
||||
* @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 {
|
||||
Type any_cast(const basic_any<Len, Align> &data) ENTT_NOEXCEPT {
|
||||
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(*instance);
|
||||
@@ -552,7 +420,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
|
||||
/*! @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 {
|
||||
Type any_cast(basic_any<Len, Align> &data) ENTT_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");
|
||||
@@ -561,14 +429,13 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
|
||||
/*! @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_const_t<std::remove_reference_t<Type>>>) {
|
||||
Type any_cast(basic_any<Len, Align> &&data) ENTT_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));
|
||||
} else {
|
||||
return any_cast<Type>(data);
|
||||
}
|
||||
|
||||
return any_cast<Type>(data);
|
||||
} else {
|
||||
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
@@ -578,46 +445,44 @@ template<typename Type, std::size_t Len, std::size_t Align>
|
||||
|
||||
/*! @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 {
|
||||
return data->template data<std::remove_const_t<Type>>();
|
||||
const Type *any_cast(const basic_any<Len, Align> *data) ENTT_NOEXCEPT {
|
||||
const auto &info = type_id<std::remove_cv_t<std::remove_reference_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 {
|
||||
return data->template data<Type>();
|
||||
}
|
||||
Type *any_cast(basic_any<Len, Align> *data) ENTT_NOEXCEPT {
|
||||
const auto &info = type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
// last attempt to make wrappers for const references return their values
|
||||
return static_cast<Type *>(static_cast<constness_as_t<basic_any<Len, Align>, 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 buffer reserved for the small buffer optimization.
|
||||
* @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) {
|
||||
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 buffer reserved for the small buffer optimization.
|
||||
* @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)};
|
||||
basic_any<Len, Align> forward_as_any(Type &&value) {
|
||||
return basic_any<Len, Align>{std::in_place_type<std::conditional_t<std::is_rvalue_reference_v<Type>, std::decay_t<Type>, Type>>, std::forward<Type>(value)};
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
30
src/entt/core/attribute.h
Normal file
30
src/entt/core/attribute.h
Normal file
@@ -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
|
||||
@@ -1,70 +0,0 @@
|
||||
#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
|
||||
@@ -5,12 +5,16 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "fwd.hpp"
|
||||
#include "../config/config.h"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, std::size_t, typename = void>
|
||||
@@ -18,28 +22,28 @@ 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<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
|
||||
compressed_pair_element()
|
||||
: value{} {}
|
||||
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_const_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, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
|
||||
compressed_pair_element(Args &&args)
|
||||
: value{std::forward<Args>(args)} {}
|
||||
|
||||
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...>)
|
||||
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
|
||||
: value{std::forward<Args>(std::get<Index>(args))...} {}
|
||||
|
||||
[[nodiscard]] constexpr reference get() noexcept {
|
||||
[[nodiscard]] reference get() ENTT_NOEXCEPT {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type value{};
|
||||
Type value;
|
||||
};
|
||||
|
||||
template<typename Type, std::size_t Tag>
|
||||
@@ -48,29 +52,33 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
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>)
|
||||
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
|
||||
compressed_pair_element()
|
||||
: base_type{} {}
|
||||
|
||||
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_const_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, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Args>>, compressed_pair_element>>>
|
||||
compressed_pair_element(Args &&args)
|
||||
: base_type{std::forward<Args>(args)} {}
|
||||
|
||||
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...>)
|
||||
compressed_pair_element(std::tuple<Args...> args, std::index_sequence<Index...>)
|
||||
: base_type{std::forward<Args>(std::get<Index>(args))...} {}
|
||||
|
||||
[[nodiscard]] constexpr reference get() noexcept {
|
||||
[[nodiscard]] reference get() ENTT_NOEXCEPT {
|
||||
return *this;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||
[[nodiscard]] const_reference get() const ENTT_NOEXCEPT {
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A compressed pair.
|
||||
@@ -103,7 +111,7 @@ public:
|
||||
* @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>)
|
||||
constexpr compressed_pair()
|
||||
: first_base{},
|
||||
second_base{} {}
|
||||
|
||||
@@ -117,7 +125,7 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
constexpr compressed_pair(compressed_pair &&other) noexcept = default;
|
||||
constexpr compressed_pair(compressed_pair &&other) = default;
|
||||
|
||||
/**
|
||||
* @brief Constructs a pair from its values.
|
||||
@@ -127,7 +135,7 @@ public:
|
||||
* @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>)
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other)
|
||||
: first_base{std::forward<Arg>(arg)},
|
||||
second_base{std::forward<Other>(other)} {}
|
||||
|
||||
@@ -139,13 +147,10 @@ public:
|
||||
* @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...>)
|
||||
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> 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.
|
||||
@@ -158,18 +163,18 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @return This compressed pair object.
|
||||
*/
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default;
|
||||
constexpr compressed_pair &operator=(compressed_pair &&other) = default;
|
||||
|
||||
/**
|
||||
* @brief Returns the first element that a pair stores.
|
||||
* @return The first element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] constexpr first_type &first() noexcept {
|
||||
[[nodiscard]] first_type &first() ENTT_NOEXCEPT {
|
||||
return static_cast<first_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc first */
|
||||
[[nodiscard]] constexpr const first_type &first() const noexcept {
|
||||
[[nodiscard]] const first_type &first() const ENTT_NOEXCEPT {
|
||||
return static_cast<const first_base &>(*this).get();
|
||||
}
|
||||
|
||||
@@ -177,12 +182,12 @@ public:
|
||||
* @brief Returns the second element that a pair stores.
|
||||
* @return The second element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] constexpr second_type &second() noexcept {
|
||||
[[nodiscard]] second_type &second() ENTT_NOEXCEPT {
|
||||
return static_cast<second_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc second */
|
||||
[[nodiscard]] constexpr const second_type &second() const noexcept {
|
||||
[[nodiscard]] const second_type &second() const ENTT_NOEXCEPT {
|
||||
return static_cast<const second_base &>(*this).get();
|
||||
}
|
||||
|
||||
@@ -190,7 +195,7 @@ public:
|
||||
* @brief Swaps two compressed pair objects.
|
||||
* @param other The compressed pair to swap with.
|
||||
*/
|
||||
constexpr void swap(compressed_pair &other) noexcept {
|
||||
void swap(compressed_pair &other) {
|
||||
using std::swap;
|
||||
swap(first(), other.first());
|
||||
swap(second(), other.second());
|
||||
@@ -203,7 +208,7 @@ public:
|
||||
* reference to the second element if `Index` is 1.
|
||||
*/
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] constexpr decltype(auto) get() noexcept {
|
||||
decltype(auto) get() ENTT_NOEXCEPT {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
@@ -214,7 +219,7 @@ public:
|
||||
|
||||
/*! @copydoc get */
|
||||
template<std::size_t Index>
|
||||
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
|
||||
decltype(auto) get() const ENTT_NOEXCEPT {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
@@ -240,12 +245,14 @@ compressed_pair(Type &&, Other &&) -> compressed_pair<std::decay_t<Type>, std::d
|
||||
* @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 {
|
||||
inline void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
// disable structured binding support for clang 6, it messes when specializing tuple_size
|
||||
#if !defined __clang_major__ || __clang_major__ > 6
|
||||
namespace std {
|
||||
|
||||
/**
|
||||
@@ -268,5 +275,6 @@ struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<I
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ENTT_CORE_ENUM_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -35,21 +36,21 @@ inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator|(const Type lhs, const Type rhs) noexcept {
|
||||
operator|(const Type lhs, const Type rhs) ENTT_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 {
|
||||
operator&(const Type lhs, const Type rhs) ENTT_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 {
|
||||
operator^(const Type lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return static_cast<Type>(static_cast<std::underlying_type_t<Type>>(lhs) ^ static_cast<std::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
@@ -62,35 +63,35 @@ operator^(const Type lhs, const Type rhs) noexcept {
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<entt::enum_as_bitmask_v<Type>, Type>
|
||||
operator~(const Type value) noexcept {
|
||||
operator~(const Type value) ENTT_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 {
|
||||
operator!(const Type value) ENTT_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 {
|
||||
operator|=(Type &lhs, const Type rhs) ENTT_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 {
|
||||
operator&=(Type &lhs, const Type rhs) ENTT_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 {
|
||||
operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT {
|
||||
return (lhs = (lhs ^ rhs));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,19 +15,16 @@ namespace entt {
|
||||
*/
|
||||
template<typename...>
|
||||
class family {
|
||||
static auto identifier() noexcept {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = id_type;
|
||||
using family_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();
|
||||
inline static const family_type type = identifier++;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -1,28 +1,13 @@
|
||||
#ifndef ENTT_CORE_FWD_HPP
|
||||
#define ENTT_CORE_FWD_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Possible modes of an any object. */
|
||||
enum class any_policy : std::uint8_t {
|
||||
/*! @brief Default mode, no element available. */
|
||||
empty,
|
||||
/*! @brief Owning mode, dynamically allocated element. */
|
||||
dynamic,
|
||||
/*! @brief Owning mode, embedded element. */
|
||||
embedded,
|
||||
/*! @brief Aliasing mode, non-const reference. */
|
||||
ref,
|
||||
/*! @brief Const aliasing mode, const reference. */
|
||||
cref
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
|
||||
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(typename std::aligned_storage_t<Len + !Len>)>
|
||||
class basic_any;
|
||||
|
||||
/*! @brief Alias declaration for type identifiers. */
|
||||
@@ -31,21 +16,6 @@ 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
|
||||
|
||||
@@ -3,26 +3,33 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename = id_type>
|
||||
struct fnv_1a_params;
|
||||
template<typename>
|
||||
struct fnv1a_traits;
|
||||
|
||||
template<>
|
||||
struct fnv_1a_params<std::uint32_t> {
|
||||
static constexpr auto offset = 2166136261;
|
||||
static constexpr auto prime = 16777619;
|
||||
struct fnv1a_traits<std::uint32_t> {
|
||||
using type = std::uint32_t;
|
||||
static constexpr std::uint32_t offset = 2166136261;
|
||||
static constexpr std::uint32_t prime = 16777619;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fnv_1a_params<std::uint64_t> {
|
||||
static constexpr auto offset = 14695981039346656037ull;
|
||||
static constexpr auto prime = 1099511628211ull;
|
||||
struct fnv1a_traits<std::uint64_t> {
|
||||
using type = std::uint64_t;
|
||||
static constexpr std::uint64_t offset = 14695981039346656037ull;
|
||||
static constexpr std::uint64_t prime = 1099511628211ull;
|
||||
};
|
||||
|
||||
template<typename Char>
|
||||
@@ -31,13 +38,17 @@ struct basic_hashed_string {
|
||||
using size_type = std::size_t;
|
||||
using hash_type = id_type;
|
||||
|
||||
const value_type *repr{};
|
||||
hash_type hash{fnv_1a_params<>::offset};
|
||||
size_type length{};
|
||||
const value_type *repr;
|
||||
size_type length;
|
||||
hash_type hash;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Zero overhead unique identifier.
|
||||
@@ -57,16 +68,36 @@ struct basic_hashed_string {
|
||||
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<>;
|
||||
using hs_traits = internal::fnv1a_traits<id_type>;
|
||||
|
||||
struct const_wrapper {
|
||||
// non-explicit constructor on purpose
|
||||
constexpr const_wrapper(const typename base_type::value_type *str) noexcept
|
||||
: repr{str} {}
|
||||
|
||||
const typename base_type::value_type *repr;
|
||||
constexpr const_wrapper(const Char *str) ENTT_NOEXCEPT: repr{str} {}
|
||||
const Char *repr;
|
||||
};
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const Char *str) ENTT_NOEXCEPT {
|
||||
base_type base{str, 0u, hs_traits::offset};
|
||||
|
||||
for(; str[base.length]; ++base.length) {
|
||||
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
// Fowler–Noll–Vo hash function v. 1a - the good
|
||||
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) ENTT_NOEXCEPT {
|
||||
base_type base{str, len, hs_traits::offset};
|
||||
|
||||
for(size_type pos{}; pos < len; ++pos) {
|
||||
base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Character type. */
|
||||
using value_type = typename base_type::value_type;
|
||||
@@ -81,7 +112,7 @@ public:
|
||||
* @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 {
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) ENTT_NOEXCEPT {
|
||||
return basic_hashed_string{str, len};
|
||||
}
|
||||
|
||||
@@ -92,8 +123,7 @@ public:
|
||||
* @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 {
|
||||
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT {
|
||||
return basic_hashed_string{str};
|
||||
}
|
||||
|
||||
@@ -102,28 +132,21 @@ public:
|
||||
* @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 {
|
||||
[[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT {
|
||||
return basic_hashed_string{wrapper};
|
||||
}
|
||||
|
||||
/*! @brief Constructs an empty hashed string. */
|
||||
constexpr basic_hashed_string() noexcept
|
||||
: basic_hashed_string{nullptr, 0u} {}
|
||||
constexpr basic_hashed_string() ENTT_NOEXCEPT
|
||||
: base_type{} {}
|
||||
|
||||
/**
|
||||
* @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
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
: base_type{str} {
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
for(; base_type::length < len; ++base_type::length) {
|
||||
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
constexpr basic_hashed_string(const value_type *str, const size_type len) ENTT_NOEXCEPT
|
||||
: base_type{helper(str, len)} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a hashed string from an array of const characters.
|
||||
@@ -131,14 +154,8 @@ public:
|
||||
* @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
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
: base_type{str} {
|
||||
for(; str[base_type::length]; ++base_type::length) {
|
||||
base_type::hash = (base_type::hash ^ static_cast<id_type>(str[base_type::length])) * params::prime;
|
||||
}
|
||||
}
|
||||
constexpr basic_hashed_string(const value_type (&str)[N]) ENTT_NOEXCEPT
|
||||
: base_type{helper(str)} {}
|
||||
|
||||
/**
|
||||
* @brief Explicit constructor on purpose to avoid constructing a hashed
|
||||
@@ -149,20 +166,14 @@ public:
|
||||
*
|
||||
* @param wrapper Helps achieving the purpose by relying on overloading.
|
||||
*/
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
|
||||
: base_type{wrapper.repr} {
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
for(; wrapper.repr[base_type::length]; ++base_type::length) {
|
||||
base_type::hash = (base_type::hash ^ static_cast<id_type>(wrapper.repr[base_type::length])) * params::prime;
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
}
|
||||
explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT
|
||||
: base_type{helper(wrapper.repr)} {}
|
||||
|
||||
/**
|
||||
* @brief Returns the size of a hashed string.
|
||||
* @brief Returns the size a hashed string.
|
||||
* @return The size of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||
[[nodiscard]] constexpr size_type size() const ENTT_NOEXCEPT {
|
||||
return base_type::length;
|
||||
}
|
||||
|
||||
@@ -170,7 +181,7 @@ public:
|
||||
* @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 {
|
||||
[[nodiscard]] constexpr const value_type *data() const ENTT_NOEXCEPT {
|
||||
return base_type::repr;
|
||||
}
|
||||
|
||||
@@ -178,12 +189,12 @@ public:
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr hash_type value() const noexcept {
|
||||
[[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT {
|
||||
return base_type::hash;
|
||||
}
|
||||
|
||||
/*! @copydoc data */
|
||||
[[nodiscard]] explicit constexpr operator const value_type *() const noexcept {
|
||||
[[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT {
|
||||
return data();
|
||||
}
|
||||
|
||||
@@ -191,7 +202,7 @@ public:
|
||||
* @brief Returns the numeric representation of a hashed string.
|
||||
* @return The numeric representation of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr operator hash_type() const noexcept {
|
||||
[[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
@@ -203,7 +214,7 @@ public:
|
||||
* @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>;
|
||||
basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
@@ -212,7 +223,6 @@ basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Cha
|
||||
* @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>;
|
||||
|
||||
/**
|
||||
@@ -223,7 +233,7 @@ basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||
* @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 {
|
||||
[[nodiscard]] constexpr bool operator==(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.value() == rhs.value();
|
||||
}
|
||||
|
||||
@@ -235,7 +245,7 @@ template<typename Char>
|
||||
* @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 {
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@@ -247,7 +257,7 @@ template<typename Char>
|
||||
* @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 {
|
||||
[[nodiscard]] constexpr bool operator<(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.value() < rhs.value();
|
||||
}
|
||||
|
||||
@@ -260,7 +270,7 @@ template<typename Char>
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator<=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
@@ -273,7 +283,7 @@ template<typename Char>
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator>(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
@@ -286,10 +296,16 @@ template<typename Char>
|
||||
* false otherwise.
|
||||
*/
|
||||
template<typename Char>
|
||||
[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator>=(const basic_hashed_string<Char> &lhs, const basic_hashed_string<Char> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
/*! @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>;
|
||||
|
||||
inline namespace literals {
|
||||
|
||||
/**
|
||||
@@ -297,7 +313,7 @@ inline namespace literals {
|
||||
* @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 {
|
||||
[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return hashed_string{str};
|
||||
}
|
||||
|
||||
@@ -306,7 +322,7 @@ inline namespace literals {
|
||||
* @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 {
|
||||
[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT {
|
||||
return hashed_wstring{str};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,30 +4,54 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
#include "type_traits.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Type integral identifiers.
|
||||
* @tparam Type List of types for which to generate identifiers.
|
||||
* @brief Types identifiers.
|
||||
*
|
||||
* Variable template used to generate identifiers at compile-time for the given
|
||||
* types. Use the `get` member function to know what's the identifier associated
|
||||
* to the specific type.
|
||||
*
|
||||
* @note
|
||||
* Identifiers are constant expression and can be used in any context where such
|
||||
* an expression is required. As an example:
|
||||
* @code{.cpp}
|
||||
* using id = entt::identifier<a_type, another_type>;
|
||||
*
|
||||
* switch(a_type_identifier) {
|
||||
* case id::type<a_type>:
|
||||
* // ...
|
||||
* break;
|
||||
* case id::type<another_type>:
|
||||
* // ...
|
||||
* break;
|
||||
* default:
|
||||
* // ...
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Types 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{}));
|
||||
template<typename... Types>
|
||||
class identifier {
|
||||
template<typename Type, std::size_t... Index>
|
||||
[[nodiscard]] static constexpr id_type get(std::index_sequence<Index...>) ENTT_NOEXCEPT {
|
||||
static_assert((std::is_same_v<Type, Types> || ...), "Invalid type");
|
||||
return (0 + ... + (std::is_same_v<Type, type_list_element_t<Index, type_list<std::decay_t<Types>...>>> ? id_type{Index} : id_type{}));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = id_type;
|
||||
using identifier_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...>{});
|
||||
template<typename Type>
|
||||
static constexpr identifier_type type = get<std::decay_t<Type>>(std::index_sequence_for<Types...>{});
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -14,124 +14,46 @@ namespace entt {
|
||||
*/
|
||||
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 Default copy constructor, deleted on purpose. */
|
||||
input_iterator_pointer(const input_iterator_pointer &) = delete;
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
input_iterator_pointer(input_iterator_pointer &&) = default;
|
||||
|
||||
/**
|
||||
* @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>)
|
||||
input_iterator_pointer(Type &&val)
|
||||
: value{std::move(val)} {}
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This proxy object.
|
||||
*/
|
||||
input_iterator_pointer &operator=(const input_iterator_pointer &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This proxy object.
|
||||
*/
|
||||
input_iterator_pointer &operator=(input_iterator_pointer &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Access operator for accessing wrapped values.
|
||||
* @return A pointer to the wrapped value.
|
||||
*/
|
||||
[[nodiscard]] constexpr pointer operator->() noexcept {
|
||||
[[nodiscard]] pointer operator->() ENTT_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.
|
||||
@@ -147,24 +69,22 @@ struct iterable_adaptor final {
|
||||
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{} {}
|
||||
iterable_adaptor() = default;
|
||||
|
||||
/**
|
||||
* @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)} {}
|
||||
iterable_adaptor(iterator from, sentinel to)
|
||||
: first{from},
|
||||
last{to} {}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the beginning.
|
||||
* @return An iterator to the first element of the range.
|
||||
*/
|
||||
[[nodiscard]] constexpr iterator begin() const noexcept {
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return first;
|
||||
}
|
||||
|
||||
@@ -173,17 +93,17 @@ struct iterable_adaptor final {
|
||||
* @return An iterator to the element following the last element of the
|
||||
* range.
|
||||
*/
|
||||
[[nodiscard]] constexpr sentinel end() const noexcept {
|
||||
[[nodiscard]] sentinel end() const ENTT_NOEXCEPT {
|
||||
return last;
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] constexpr iterator cbegin() const noexcept {
|
||||
[[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT {
|
||||
return begin();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] constexpr sentinel cend() const noexcept {
|
||||
[[nodiscard]] sentinel cend() const ENTT_NOEXCEPT {
|
||||
return end();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define ENTT_CORE_MEMORY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
@@ -17,8 +18,8 @@ namespace entt {
|
||||
* @return A raw pointer that represents the address of the original pointer.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept {
|
||||
if constexpr(std::is_pointer_v<std::decay_t<Type>>) {
|
||||
[[nodiscard]] constexpr auto to_address(Type &&ptr) ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_pointer_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
return ptr;
|
||||
} else {
|
||||
return to_address(std::forward<Type>(ptr).operator->());
|
||||
@@ -32,7 +33,7 @@ template<typename Type>
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
|
||||
lhs = rhs;
|
||||
}
|
||||
@@ -45,7 +46,7 @@ constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_move_assignment::value) {
|
||||
lhs = std::move(rhs);
|
||||
}
|
||||
@@ -58,18 +59,54 @@ constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(std::allocator_traits<Allocator>::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers");
|
||||
|
||||
if constexpr(std::allocator_traits<Allocator>::propagate_on_container_swap::value) {
|
||||
using std::swap;
|
||||
swap(lhs, rhs);
|
||||
} else {
|
||||
ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a value is a power of two or not.
|
||||
* @param value A value that may or may not be a power of two.
|
||||
* @return True if the value is a power of two, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
|
||||
return value && ((value & (value - 1)) == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value.
|
||||
* @param value The value to use.
|
||||
* @return The smallest power of two greater than or equal to the given value.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
|
||||
std::size_t curr = value - (value != 0u);
|
||||
|
||||
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
|
||||
curr |= curr >> next;
|
||||
}
|
||||
|
||||
return ++curr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fast module utility function (powers of two only).
|
||||
* @param value A value for which to calculate the modulus.
|
||||
* @param mod _Modulus_, it must be a power of two.
|
||||
* @return The common remainder.
|
||||
*/
|
||||
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) ENTT_NOEXCEPT {
|
||||
ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two");
|
||||
return value & (mod - 1u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deleter for allocator-aware unique pointers (waiting for C++20).
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
struct allocation_deleter: private Allocator {
|
||||
@@ -82,15 +119,15 @@ struct allocation_deleter: private Allocator {
|
||||
* @brief Inherited constructors.
|
||||
* @param alloc The allocator to use.
|
||||
*/
|
||||
constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v<allocator_type>)
|
||||
allocation_deleter(const allocator_type &alloc)
|
||||
: Allocator{alloc} {}
|
||||
|
||||
/**
|
||||
* @brief Destroys the pointed object and deallocates its memory.
|
||||
* @param ptr A valid pointer to an object of the given type.
|
||||
*/
|
||||
constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
void operator()(pointer ptr) {
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>;
|
||||
alloc_traits::destroy(*this, to_address(ptr));
|
||||
alloc_traits::deallocate(*this, ptr, 1u);
|
||||
}
|
||||
@@ -106,7 +143,7 @@ struct allocation_deleter: private Allocator {
|
||||
* @return A properly initialized unique pointer with a custom deleter.
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
|
||||
auto allocate_unique(Allocator &allocator, Args &&...args) {
|
||||
static_assert(!std::is_array_v<Type>, "Array types are not supported");
|
||||
|
||||
using alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<Type>;
|
||||
@@ -126,20 +163,24 @@ ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) {
|
||||
return std::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
|
||||
}
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
struct uses_allocator_construction {
|
||||
template<typename Allocator, typename... Params>
|
||||
static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
|
||||
static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) ENTT_NOEXCEPT {
|
||||
if constexpr(!std::uses_allocator_v<Type, Allocator> && std::is_constructible_v<Type, Params...>) {
|
||||
return std::forward_as_tuple(std::forward<Params>(params)...);
|
||||
} else {
|
||||
static_assert(std::uses_allocator_v<Type, Allocator>, "Ill-formed request");
|
||||
|
||||
if constexpr(std::is_constructible_v<Type, std::allocator_arg_t, const Allocator &, Params...>) {
|
||||
return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>{std::allocator_arg, allocator, std::forward<Params>(params)...};
|
||||
return std::tuple<std::allocator_arg_t, const Allocator &, Params &&...>(std::allocator_arg, allocator, std::forward<Params>(params)...);
|
||||
} else {
|
||||
static_assert(std::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
|
||||
return std::forward_as_tuple(std::forward<Params>(params)..., allocator);
|
||||
@@ -153,7 +194,7 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
|
||||
using type = std::pair<Type, Other>;
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept {
|
||||
static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) ENTT_NOEXCEPT {
|
||||
return std::make_tuple(
|
||||
std::piecewise_construct,
|
||||
std::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Type>::args(allocator, std::forward<decltype(curr)>(curr)...); }, std::forward<First>(first)),
|
||||
@@ -161,28 +202,32 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
|
||||
}
|
||||
|
||||
template<typename Allocator>
|
||||
static constexpr auto args(const Allocator &allocator) noexcept {
|
||||
static constexpr auto args(const Allocator &allocator) ENTT_NOEXCEPT {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
|
||||
}
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept {
|
||||
static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) ENTT_NOEXCEPT {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward<First>(first)), std::forward_as_tuple(std::forward<Second>(second)));
|
||||
}
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) noexcept {
|
||||
static constexpr auto args(const Allocator &allocator, const std::pair<First, Second> &value) ENTT_NOEXCEPT {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second));
|
||||
}
|
||||
|
||||
template<typename Allocator, typename First, typename Second>
|
||||
static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) noexcept {
|
||||
static constexpr auto args(const Allocator &allocator, std::pair<First, Second> &&value) ENTT_NOEXCEPT {
|
||||
return uses_allocator_construction<type>::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second)));
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
@@ -198,7 +243,7 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
|
||||
* @return The arguments needed to create an object of the given type.
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept {
|
||||
constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) ENTT_NOEXCEPT {
|
||||
return internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -236,7 +281,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
|
||||
*/
|
||||
template<typename Type, typename Allocator, typename... Args>
|
||||
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
|
||||
return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
|
||||
return std::apply([&](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -23,12 +23,10 @@ struct monostate {
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
* @tparam Type Type of the value to assign.
|
||||
* @param val User data to assign to the given key.
|
||||
* @return This monostate object.
|
||||
*/
|
||||
template<typename Type>
|
||||
monostate &operator=(Type val) noexcept {
|
||||
void operator=(Type val) const ENTT_NOEXCEPT {
|
||||
value<Type> = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,13 +35,12 @@ struct monostate {
|
||||
* @return Stored value, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
operator Type() const noexcept {
|
||||
operator Type() const ENTT_NOEXCEPT {
|
||||
return value<Type>;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline static ENTT_MAYBE_ATOMIC(Type) value{};
|
||||
};
|
||||
|
||||
@@ -52,8 +49,7 @@ private:
|
||||
* @tparam Value Value used to differentiate between different variables.
|
||||
*/
|
||||
template<id_type Value>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline monostate<Value> monostate_v{};
|
||||
inline monostate<Value> monostate_v = {};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef ENTT_CORE_RANGES_HPP
|
||||
#define ENTT_CORE_RANGES_HPP
|
||||
|
||||
#if __has_include(<version>)
|
||||
# include <version>
|
||||
#
|
||||
# if defined(__cpp_lib_ranges)
|
||||
# include <ranges>
|
||||
# include "iterator.hpp"
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<entt::iterable_adaptor<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_view<entt::iterable_adaptor<Args...>>{true};
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -4,31 +4,10 @@
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is a
|
||||
* tuple, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_tuple: std::false_type {};
|
||||
|
||||
/**
|
||||
* @copybrief is_tuple
|
||||
* @tparam Args Tuple template arguments.
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct is_tuple<std::tuple<Args...>>: std::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_tuple_v = is_tuple<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Utility function to unwrap tuples of a single element.
|
||||
* @tparam Type Tuple type of any sizes.
|
||||
@@ -37,7 +16,7 @@ inline constexpr bool is_tuple_v = is_tuple<Type>::value;
|
||||
* element otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept {
|
||||
constexpr decltype(auto) unwrap_tuple(Type &&value) ENTT_NOEXCEPT {
|
||||
if constexpr(std::tuple_size_v<std::remove_reference_t<Type>> == 1u) {
|
||||
return std::get<0>(std::forward<Type>(value));
|
||||
} else {
|
||||
@@ -45,46 +24,6 @@ constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility class to forward-and-apply tuple objects.
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
struct forward_apply: private Func {
|
||||
/**
|
||||
* @brief Constructs a forward-and-apply object.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename... Args>
|
||||
constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>)
|
||||
: Func{std::forward<Args>(args)...} {}
|
||||
|
||||
/**
|
||||
* @brief Forwards and applies the arguments with the underlying function.
|
||||
* @tparam Type Tuple-like type to forward to the underlying function.
|
||||
* @param args Parameters to forward to the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) {
|
||||
return std::apply(static_cast<Func &>(*this), std::forward<Type>(args));
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) {
|
||||
return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
forward_apply(Func) -> forward_apply<std::remove_reference_t<std::remove_const_t<Func>>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,63 +5,59 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/attribute.h"
|
||||
#include "fwd.hpp"
|
||||
#include "hashed_string.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct ENTT_API type_index final {
|
||||
[[nodiscard]] static id_type next() noexcept {
|
||||
[[nodiscard]] static id_type next() ENTT_NOEXCEPT {
|
||||
static ENTT_MAYBE_ATOMIC(id_type) value{};
|
||||
return value++;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr const char *pretty_function() noexcept {
|
||||
[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
return static_cast<const char *>(ENTT_PRETTY_FUNCTION);
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
const std::string_view full_name{pretty_function<Type>()};
|
||||
auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
|
||||
auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
|
||||
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
|
||||
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
|
||||
auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
|
||||
return value;
|
||||
#else
|
||||
return std::string_view{};
|
||||
return std::string_view{""};
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] constexpr std::string_view type_name(int) noexcept {
|
||||
[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT {
|
||||
constexpr auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] std::string_view type_name(char) noexcept {
|
||||
[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT {
|
||||
static const auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] constexpr id_type type_hash(int) noexcept {
|
||||
[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT {
|
||||
constexpr auto stripped = stripped_type_name<Type>();
|
||||
constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] id_type type_hash(char) noexcept {
|
||||
[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT {
|
||||
static const auto value = [](const auto stripped) {
|
||||
return hashed_string::value(stripped.data(), stripped.size());
|
||||
}(stripped_type_name<Type>());
|
||||
@@ -69,7 +65,11 @@ template<typename Type>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
@@ -81,13 +81,13 @@ struct ENTT_API type_index final {
|
||||
* @brief Returns the sequential identifier of a given type.
|
||||
* @return The sequential identifier of a given type.
|
||||
*/
|
||||
[[nodiscard]] static id_type value() noexcept {
|
||||
[[nodiscard]] static id_type value() ENTT_NOEXCEPT {
|
||||
static const id_type value = internal::type_index::next();
|
||||
return value;
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const noexcept {
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
@@ -103,16 +103,16 @@ struct type_hash final {
|
||||
* @return The numeric representation of the given type.
|
||||
*/
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
return internal::type_hash<Type>(0);
|
||||
#else
|
||||
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||
[[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT {
|
||||
return type_index<Type>::value();
|
||||
#endif
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator id_type() const noexcept {
|
||||
[[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
@@ -127,12 +127,12 @@ struct type_name final {
|
||||
* @brief Returns the name of a given type.
|
||||
* @return The name of the given type.
|
||||
*/
|
||||
[[nodiscard]] static constexpr std::string_view value() noexcept {
|
||||
[[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT {
|
||||
return internal::type_name<Type>(0);
|
||||
}
|
||||
|
||||
/*! @copydoc value */
|
||||
[[nodiscard]] constexpr operator std::string_view() const noexcept {
|
||||
[[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT {
|
||||
return value();
|
||||
}
|
||||
};
|
||||
@@ -144,18 +144,16 @@ struct type_info final {
|
||||
* @tparam Type Type for which to construct a type info object.
|
||||
*/
|
||||
template<typename Type>
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
constexpr type_info(std::in_place_type_t<Type>) noexcept
|
||||
: seq{type_index<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_const_t<std::remove_reference_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_const_t<std::remove_reference_t<Type>>>::value()} {}
|
||||
// NOLINTEND(modernize-use-transparent-functors)
|
||||
constexpr type_info(std::in_place_type_t<Type>) ENTT_NOEXCEPT
|
||||
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
|
||||
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
|
||||
|
||||
/**
|
||||
* @brief Type index.
|
||||
* @return Type index.
|
||||
*/
|
||||
[[nodiscard]] constexpr id_type index() const noexcept {
|
||||
[[nodiscard]] constexpr id_type index() const ENTT_NOEXCEPT {
|
||||
return seq;
|
||||
}
|
||||
|
||||
@@ -163,7 +161,7 @@ struct type_info final {
|
||||
* @brief Type hash.
|
||||
* @return Type hash.
|
||||
*/
|
||||
[[nodiscard]] constexpr id_type hash() const noexcept {
|
||||
[[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@@ -171,7 +169,7 @@ struct type_info final {
|
||||
* @brief Type name.
|
||||
* @return Type name.
|
||||
*/
|
||||
[[nodiscard]] constexpr std::string_view name() const noexcept {
|
||||
[[nodiscard]] constexpr std::string_view name() const ENTT_NOEXCEPT {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@@ -187,7 +185,7 @@ private:
|
||||
* @param rhs A type info object.
|
||||
* @return True if the two type info objects are identical, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.hash() == rhs.hash();
|
||||
}
|
||||
|
||||
@@ -197,7 +195,7 @@ private:
|
||||
* @param rhs A type info object.
|
||||
* @return True if the two type info objects differ, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
@@ -207,7 +205,7 @@ private:
|
||||
* @param rhs A valid type info object.
|
||||
* @return True if the first element is less than the second, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.index() < rhs.index();
|
||||
}
|
||||
|
||||
@@ -218,7 +216,7 @@ private:
|
||||
* @return True if the first element is less than or equal to the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return !(rhs < lhs);
|
||||
}
|
||||
|
||||
@@ -229,7 +227,7 @@ private:
|
||||
* @return True if the first element is greater than the second, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
@@ -240,7 +238,7 @@ private:
|
||||
* @return True if the first element is greater than or equal to the second,
|
||||
* false otherwise.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
|
||||
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs < rhs);
|
||||
}
|
||||
|
||||
@@ -256,20 +254,19 @@ private:
|
||||
* @return A reference to a properly initialized type info object.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id() noexcept {
|
||||
if constexpr(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>) {
|
||||
static const type_info instance{std::in_place_type<Type>};
|
||||
[[nodiscard]] const type_info &type_id() ENTT_NOEXCEPT {
|
||||
if constexpr(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
static type_info instance{std::in_place_type<Type>};
|
||||
return instance;
|
||||
} else {
|
||||
return type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc type_id */
|
||||
template<typename Type>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
|
||||
[[nodiscard]] const type_info &type_id(Type &&) noexcept {
|
||||
return type_id<std::remove_const_t<std::remove_reference_t<Type>>>();
|
||||
[[nodiscard]] const type_info &type_id(Type &&) ENTT_NOEXCEPT {
|
||||
return type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
@@ -17,7 +16,7 @@ namespace entt {
|
||||
*/
|
||||
template<std::size_t N>
|
||||
struct choice_t
|
||||
// unfortunately, doxygen cannot parse such a construct
|
||||
// Unfortunately, doxygen cannot parse such a construct.
|
||||
: /*! @cond TURN_OFF_DOXYGEN */ choice_t<N - 1> /*! @endcond */
|
||||
{};
|
||||
|
||||
@@ -56,6 +55,7 @@ using type_identity_t = typename type_identity<Type>::type;
|
||||
/**
|
||||
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
|
||||
* @tparam Type The type of which to return the size.
|
||||
* @tparam The size of the type if `sizeof` accepts it, 0 otherwise.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct size_of: std::integral_constant<std::size_t, 0u> {};
|
||||
@@ -63,7 +63,6 @@ struct size_of: std::integral_constant<std::size_t, 0u> {};
|
||||
/*! @copydoc size_of */
|
||||
template<typename Type>
|
||||
struct size_of<Type, std::void_t<decltype(sizeof(Type))>>
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
: std::integral_constant<std::size_t, sizeof(Type)> {};
|
||||
|
||||
/**
|
||||
@@ -122,22 +121,22 @@ struct type_list_element;
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Type First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<std::size_t Index, typename First, typename... Other>
|
||||
struct type_list_element<Index, type_list<First, Other...>>
|
||||
template<std::size_t Index, typename Type, typename... Other>
|
||||
struct type_list_element<Index, type_list<Type, Other...>>
|
||||
: type_list_element<Index - 1u, type_list<Other...>> {};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Type First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename First, typename... Other>
|
||||
struct type_list_element<0u, type_list<First, Other...>> {
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_element<0u, type_list<Type, Other...>> {
|
||||
/*! @brief Searched type. */
|
||||
using type = First;
|
||||
using type = Type;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -148,58 +147,6 @@ struct type_list_element<0u, type_list<First, Other...>> {
|
||||
template<std::size_t Index, typename List>
|
||||
using type_list_element_t = typename type_list_element<Index, List>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, typename>
|
||||
struct type_list_index;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the types of a type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename First, typename... Other>
|
||||
struct type_list_index<Type, type_list<First, Other...>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 1u + type_list_index<Type, type_list<Other...>>::value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the types of a type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_index<Type, type_list<Type, Other...>> {
|
||||
static_assert(type_list_index<Type, type_list<Other...>>::value == sizeof...(Other), "Non-unique type");
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the types of a type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct type_list_index<Type, type_list<>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for and for which to return the index.
|
||||
*/
|
||||
template<typename Type, typename List>
|
||||
inline constexpr std::size_t type_list_index_v = type_list_index<Type, List>::value;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
@@ -251,40 +198,37 @@ struct type_list_cat<type_list<Type...>> {
|
||||
template<typename... List>
|
||||
using type_list_cat_t = typename type_list_cat<List...>::type;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename...>
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
struct type_list_unique;
|
||||
|
||||
template<typename First, typename... Other, typename... Type>
|
||||
struct type_list_unique<type_list<First, Other...>, Type...>
|
||||
: std::conditional_t<(std::is_same_v<First, Type> || ...), type_list_unique<type_list<Other...>, Type...>, type_list_unique<type_list<Other...>, Type..., First>> {};
|
||||
|
||||
template<typename... Type>
|
||||
struct type_list_unique<type_list<>, Type...> {
|
||||
using type = type_list<Type...>;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Removes duplicates types from a type list.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type One of the types provided by the given type list.
|
||||
* @tparam Other The other types provided by the given type list.
|
||||
*/
|
||||
template<typename List>
|
||||
struct type_list_unique {
|
||||
template<typename Type, typename... Other>
|
||||
struct type_list_unique<type_list<Type, Other...>> {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = typename internal::type_list_unique<List>::type;
|
||||
using type = std::conditional_t<
|
||||
(std::is_same_v<Type, Other> || ...),
|
||||
typename type_list_unique<type_list<Other...>>::type,
|
||||
type_list_cat_t<type_list<Type>, typename type_list_unique<type_list<Other...>>::type>>;
|
||||
};
|
||||
|
||||
/*! @brief Removes duplicates types from a type list. */
|
||||
template<>
|
||||
struct type_list_unique<type_list<>> {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = type_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type A type list.
|
||||
*/
|
||||
template<typename List>
|
||||
using type_list_unique_t = typename type_list_unique<List>::type;
|
||||
template<typename Type>
|
||||
using type_list_unique_t = typename type_list_unique<Type>::type;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a type list contains a
|
||||
@@ -301,8 +245,7 @@ struct type_list_contains;
|
||||
* @tparam Other Type to look for.
|
||||
*/
|
||||
template<typename... Type, typename Other>
|
||||
struct type_list_contains<type_list<Type...>, Other>
|
||||
: std::bool_constant<(std::is_same_v<Type, Other> || ...)> {};
|
||||
struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -334,30 +277,6 @@ struct type_list_diff<type_list<Type...>, type_list<Other...>> {
|
||||
template<typename... List>
|
||||
using type_list_diff_t = typename type_list_diff<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, template<typename...> class>
|
||||
struct type_list_transform;
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to a type list and generate a new list.
|
||||
* @tparam Type Types provided by the type list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<type_list<Type...>, Op> {
|
||||
/*! @brief Resulting type list after applying the transform function. */
|
||||
// NOLINTNEXTLINE(modernize-type-traits)
|
||||
using type = type_list<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename List, template<typename...> class Op>
|
||||
using type_list_transform_t = typename type_list_transform<List, Op>::type;
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of constant values, nothing more.
|
||||
* @tparam Value Values provided by the value list.
|
||||
@@ -391,20 +310,10 @@ struct value_list_element<Index, value_list<Value, Other...>>
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_element<0u, value_list<Value, Other...>> {
|
||||
/*! @brief Searched type. */
|
||||
using type = decltype(Value);
|
||||
/*! @brief Searched value. */
|
||||
static constexpr auto value = Value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam List Value list to search into.
|
||||
*/
|
||||
template<std::size_t Index, typename List>
|
||||
using value_list_element_t = typename value_list_element<Index, List>::type;
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the value to return.
|
||||
@@ -413,58 +322,6 @@ using value_list_element_t = typename value_list_element<Index, List>::type;
|
||||
template<std::size_t Index, typename List>
|
||||
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<auto, typename>
|
||||
struct value_list_index;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the values of a value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
* @tparam First First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto First, auto... Other>
|
||||
struct value_list_index<Value, value_list<First, Other...>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given value in the sublist. */
|
||||
static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the values of a value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_index<Value, value_list<Value, Other...>> {
|
||||
static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type");
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given value in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time type access to the values of a value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
*/
|
||||
template<auto Value>
|
||||
struct value_list_index<Value, value_list<>> {
|
||||
/*! @brief Unsigned integer type. */
|
||||
using value_type = std::size_t;
|
||||
/*! @brief Compile-time position of the given type in the sublist. */
|
||||
static constexpr value_type value = 0u;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for and for which to return the index.
|
||||
*/
|
||||
template<auto Value, typename List>
|
||||
inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
@@ -516,86 +373,6 @@ struct value_list_cat<value_list<Value...>> {
|
||||
template<typename... List>
|
||||
using value_list_cat_t = typename value_list_cat<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
struct value_list_unique;
|
||||
|
||||
/**
|
||||
* @brief Removes duplicates values from a value list.
|
||||
* @tparam Value One of the values provided by the given value list.
|
||||
* @tparam Other The other values provided by the given value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_unique<value_list<Value, Other...>> {
|
||||
/*! @brief A value list without duplicate types. */
|
||||
using type = std::conditional_t<
|
||||
((Value == Other) || ...),
|
||||
typename value_list_unique<value_list<Other...>>::type,
|
||||
value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>;
|
||||
};
|
||||
|
||||
/*! @brief Removes duplicates values from a value list. */
|
||||
template<>
|
||||
struct value_list_unique<value_list<>> {
|
||||
/*! @brief A value list without duplicate types. */
|
||||
using type = value_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A value list.
|
||||
*/
|
||||
template<typename Type>
|
||||
using value_list_unique_t = typename value_list_unique<Type>::type;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a value list contains
|
||||
* a given value, false otherwise.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for.
|
||||
*/
|
||||
template<typename List, auto Value>
|
||||
struct value_list_contains;
|
||||
|
||||
/**
|
||||
* @copybrief value_list_contains
|
||||
* @tparam Value Values provided by the value list.
|
||||
* @tparam Other Value to look for.
|
||||
*/
|
||||
template<auto... Value, auto Other>
|
||||
struct value_list_contains<value_list<Value...>, Other>
|
||||
: std::bool_constant<((Value == Other) || ...)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for.
|
||||
*/
|
||||
template<typename List, auto Value>
|
||||
inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct value_list_diff;
|
||||
|
||||
/**
|
||||
* @brief Computes the difference between two value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
*/
|
||||
template<auto... Value, auto... Other>
|
||||
struct value_list_diff<value_list<Value...>, value_list<Other...>> {
|
||||
/*! @brief A value list that is the difference between the two value lists. */
|
||||
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Value lists between which to compute the difference.
|
||||
*/
|
||||
template<typename... List>
|
||||
using value_list_diff_t = typename value_list_diff<List...>::type;
|
||||
|
||||
/*! @brief Same as std::is_invocable, but with tuples. */
|
||||
template<typename, typename>
|
||||
struct is_applicable: std::false_type {};
|
||||
@@ -677,7 +454,11 @@ inline constexpr bool is_complete_v = is_complete<Type>::value;
|
||||
template<typename Type, typename = void>
|
||||
struct is_iterator: std::false_type {};
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
@@ -687,11 +468,15 @@ template<typename Type>
|
||||
struct has_iterator_category<Type, std::void_t<typename std::iterator_traits<Type>::iterator_category>>: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*! @copydoc is_iterator */
|
||||
template<typename Type>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_void_v<std::remove_const_t<std::remove_pointer_t<Type>>>>>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_pointer_t<Type>>, void>>>
|
||||
: internal::has_iterator_category<Type> {};
|
||||
|
||||
/**
|
||||
@@ -708,7 +493,7 @@ inline constexpr bool is_iterator_v = is_iterator<Type>::value;
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_ebco_eligible
|
||||
: std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {};
|
||||
: std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -736,7 +521,19 @@ struct is_transparent<Type, std::void_t<typename Type::is_transparent>>: std::tr
|
||||
template<typename Type>
|
||||
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* equality comparable, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type, typename = void>
|
||||
struct is_equality_comparable: std::false_type {};
|
||||
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
@@ -745,67 +542,47 @@ struct has_tuple_size_value: std::false_type {};
|
||||
template<typename Type>
|
||||
struct has_tuple_size_value<Type, std::void_t<decltype(std::tuple_size<const Type>::value)>>: std::true_type {};
|
||||
|
||||
template<typename, typename = void>
|
||||
struct has_value_type: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct has_value_type<Type, std::void_t<typename Type::value_type>>: std::true_type {};
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] constexpr bool dispatch_is_equality_comparable();
|
||||
|
||||
template<typename Type, std::size_t... Index>
|
||||
[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence<Index...>) {
|
||||
return (dispatch_is_equality_comparable<std::tuple_element_t<Index, Type>>() && ...);
|
||||
return (is_equality_comparable<std::tuple_element_t<Index, Type>>::value && ...);
|
||||
}
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] constexpr bool maybe_equality_comparable(char) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval<Type>() == std::declval<Type>()) {
|
||||
[[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr bool dispatch_is_equality_comparable() {
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(is_complete_v<std::tuple_size<std::remove_const_t<Type>>>) {
|
||||
if constexpr(has_tuple_size_value<Type>::value) {
|
||||
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
}
|
||||
} else if constexpr(has_value_type<Type>::value) {
|
||||
if constexpr(is_iterator_v<Type> || std::is_same_v<typename Type::value_type, Type> || dispatch_is_equality_comparable<typename Type::value_type>()) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
[[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval<typename Type::value_type>(), bool{}) {
|
||||
if constexpr(is_iterator_v<Type>) {
|
||||
return true;
|
||||
} else if constexpr(std::is_same_v<typename Type::value_type, Type>) {
|
||||
return maybe_equality_comparable<Type>(choice<0>);
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
return is_equality_comparable<typename Type::value_type>::value;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr std::enable_if_t<is_complete_v<std::tuple_size<std::remove_const_t<Type>>>, bool> maybe_equality_comparable(choice_t<2>) {
|
||||
if constexpr(has_tuple_size_value<Type>::value) {
|
||||
return unpack_maybe_equality_comparable<Type>(std::make_index_sequence<std::tuple_size<Type>::value>{});
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(choice<1>);
|
||||
}
|
||||
// NOLINTEND(modernize-use-transparent-functors)
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is
|
||||
* equality comparable, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_equality_comparable: std::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {};
|
||||
|
||||
/*! @copydoc is_equality_comparable */
|
||||
template<typename Type>
|
||||
struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
|
||||
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
|
||||
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -829,7 +606,7 @@ struct constness_as {
|
||||
template<typename To, typename From>
|
||||
struct constness_as<To, const From> {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = const To;
|
||||
using type = std::add_const_t<To>;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -869,53 +646,6 @@ public:
|
||||
template<typename Member>
|
||||
using member_class_t = typename member_class<Member>::type;
|
||||
|
||||
/**
|
||||
* @brief Extracts the n-th argument of a _callable_ type.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid _callable_ type.
|
||||
*/
|
||||
template<std::size_t Index, typename Candidate>
|
||||
class nth_argument {
|
||||
template<typename Ret, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
|
||||
|
||||
template<typename Type, typename Class>
|
||||
static constexpr type_list<Type> pick_up(Type Class ::*);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&);
|
||||
|
||||
public:
|
||||
/*! @brief N-th argument of the _callable_ type. */
|
||||
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
*/
|
||||
template<std::size_t Index, typename Candidate>
|
||||
using nth_argument_t = typename nth_argument<Index, Candidate>::type;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
template<typename... Type>
|
||||
struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {};
|
||||
|
||||
template<std::size_t Index, typename... Type>
|
||||
struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {};
|
||||
|
||||
template<auto... Value>
|
||||
struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {};
|
||||
|
||||
template<std::size_t Index, auto... Value>
|
||||
struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#ifndef ENTT_CORE_UTILITY_HPP
|
||||
#define ENTT_CORE_UTILITY_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -17,8 +17,8 @@ struct identity {
|
||||
* @param value The actual argument.
|
||||
* @return The submitted value as-is.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept {
|
||||
template<class Type>
|
||||
[[nodiscard]] constexpr Type &&operator()(Type &&value) const ENTT_NOEXCEPT {
|
||||
return std::forward<Type>(value);
|
||||
}
|
||||
};
|
||||
@@ -31,7 +31,7 @@ struct identity {
|
||||
* @return Pointer to the member.
|
||||
*/
|
||||
template<typename Type, typename Class>
|
||||
[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept {
|
||||
[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT {
|
||||
return member;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ template<typename Type, typename Class>
|
||||
* @return Pointer to the function.
|
||||
*/
|
||||
template<typename Func>
|
||||
[[nodiscard]] constexpr auto overload(Func *func) noexcept {
|
||||
[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT {
|
||||
return func;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ template<typename Func>
|
||||
* @brief Helper type for visitors.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<typename... Func>
|
||||
template<class... Func>
|
||||
struct overloaded: Func... {
|
||||
using Func::operator()...;
|
||||
};
|
||||
@@ -59,20 +59,20 @@ struct overloaded: Func... {
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<typename... Func>
|
||||
template<class... Func>
|
||||
overloaded(Func...) -> overloaded<Func...>;
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a y-combinator.
|
||||
* @tparam Func Type of a potentially recursive function.
|
||||
*/
|
||||
template<typename Func>
|
||||
template<class Func>
|
||||
struct y_combinator {
|
||||
/**
|
||||
* @brief Constructs a y-combinator from a given function.
|
||||
* @param recursive A potentially recursive function.
|
||||
*/
|
||||
constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v<Func>)
|
||||
y_combinator(Func recursive)
|
||||
: func{std::move(recursive)} {}
|
||||
|
||||
/**
|
||||
@@ -81,14 +81,14 @@ struct y_combinator {
|
||||
* @param args Parameters to use to invoke the underlying function.
|
||||
* @return Return value of the underlying function, if any.
|
||||
*/
|
||||
template<typename... Args>
|
||||
constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) {
|
||||
template<class... Args>
|
||||
decltype(auto) operator()(Args &&...args) const {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<typename... Args>
|
||||
constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) {
|
||||
template<class... Args>
|
||||
decltype(auto) operator()(Args &&...args) {
|
||||
return func(*this, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,56 +4,58 @@
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename = void>
|
||||
struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {};
|
||||
|
||||
template<>
|
||||
struct in_place_delete<void>: std::false_type {};
|
||||
template<typename, typename = void>
|
||||
struct in_place_delete: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename Type, typename = void>
|
||||
struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
|
||||
|
||||
template<>
|
||||
struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
|
||||
struct page_size: std::integral_constant<std::size_t, (ENTT_IGNORE_IF_EMPTY && std::is_empty_v<Type>) ? 0u : ENTT_PACKED_PAGE> {};
|
||||
|
||||
template<typename Type>
|
||||
struct page_size<Type, std::void_t<decltype(Type::page_size)>>
|
||||
struct page_size<Type, std::enable_if_t<std::is_convertible_v<decltype(Type::page_size), std::size_t>>>
|
||||
: std::integral_constant<std::size_t, Type::page_size> {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Common way to access various properties of components.
|
||||
* @tparam Type Element type.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Type Type of component.
|
||||
*/
|
||||
template<typename Type, typename Entity, typename>
|
||||
template<typename Type, typename = void>
|
||||
struct component_traits {
|
||||
static_assert(std::is_same_v<std::decay_t<Type>, Type>, "Unsupported type");
|
||||
|
||||
/*! @brief Element type. */
|
||||
using element_type = Type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
|
||||
/*! @brief Pointer stability, default is `false`. */
|
||||
static constexpr bool in_place_delete = internal::in_place_delete<Type>::value;
|
||||
/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
|
||||
static constexpr std::size_t page_size = internal::page_size<Type>::value;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type Type of component.
|
||||
*/
|
||||
template<class Type>
|
||||
inline constexpr bool ignore_as_empty_v = (component_traits<Type>::page_size == 0u);
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/bit.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
@@ -18,71 +21,65 @@ struct entt_traits;
|
||||
|
||||
template<typename Type>
|
||||
struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>>
|
||||
: entt_traits<std::underlying_type_t<Type>> {
|
||||
using value_type = Type;
|
||||
};
|
||||
: entt_traits<std::underlying_type_t<Type>> {};
|
||||
|
||||
template<typename Type>
|
||||
struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>>
|
||||
: entt_traits<typename Type::entity_type> {
|
||||
using value_type = Type;
|
||||
};
|
||||
: entt_traits<typename Type::entity_type> {};
|
||||
|
||||
template<>
|
||||
struct entt_traits<std::uint32_t> {
|
||||
using value_type = std::uint32_t;
|
||||
|
||||
using entity_type = std::uint32_t;
|
||||
using version_type = std::uint16_t;
|
||||
|
||||
static constexpr entity_type entity_mask = 0xFFFFF;
|
||||
static constexpr entity_type version_mask = 0xFFF;
|
||||
static constexpr std::size_t entity_shift = 20u;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct entt_traits<std::uint64_t> {
|
||||
using value_type = std::uint64_t;
|
||||
|
||||
using entity_type = std::uint64_t;
|
||||
using version_type = std::uint32_t;
|
||||
|
||||
static constexpr entity_type entity_mask = 0xFFFFFFFF;
|
||||
static constexpr entity_type version_mask = 0xFFFFFFFF;
|
||||
static constexpr std::size_t entity_shift = 32u;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Common basic entity traits implementation.
|
||||
* @tparam Traits Actual entity traits to use.
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
template<typename Traits>
|
||||
class basic_entt_traits {
|
||||
static constexpr auto length = popcount(Traits::entity_mask);
|
||||
|
||||
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
|
||||
static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");
|
||||
/**
|
||||
* @brief Entity traits.
|
||||
* @tparam Type Type of identifier.
|
||||
*/
|
||||
template<typename Type>
|
||||
class entt_traits: internal::entt_traits<Type> {
|
||||
using base_type = internal::entt_traits<Type>;
|
||||
|
||||
public:
|
||||
/*! @brief Value type. */
|
||||
using value_type = typename Traits::value_type;
|
||||
using value_type = Type;
|
||||
/*! @brief Underlying entity type. */
|
||||
using entity_type = typename Traits::entity_type;
|
||||
using entity_type = typename base_type::entity_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename Traits::version_type;
|
||||
|
||||
/*! @brief Entity mask size. */
|
||||
static constexpr entity_type entity_mask = Traits::entity_mask;
|
||||
/*! @brief Version mask size */
|
||||
static constexpr entity_type version_mask = Traits::version_mask;
|
||||
using version_type = typename base_type::version_type;
|
||||
/*! @brief Reserved identifier. */
|
||||
static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift);
|
||||
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
|
||||
static constexpr auto page_size = ENTT_SPARSE_PAGE;
|
||||
|
||||
/**
|
||||
* @brief Converts an entity to its underlying type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the given value.
|
||||
*/
|
||||
[[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept {
|
||||
[[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT {
|
||||
return static_cast<entity_type>(value);
|
||||
}
|
||||
|
||||
@@ -91,8 +88,8 @@ public:
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the entity part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept {
|
||||
return (to_integral(value) & entity_mask);
|
||||
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT {
|
||||
return (to_integral(value) & base_type::entity_mask);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,22 +97,8 @@ public:
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return version_type{};
|
||||
} else {
|
||||
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the successor of a given identifier.
|
||||
* @param value The identifier of which to return the successor.
|
||||
* @return The successor of the given identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type next(const value_type value) noexcept {
|
||||
const auto vers = to_version(value) + 1;
|
||||
return construct(to_integral(value), static_cast<version_type>(vers + (vers == version_mask)));
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT {
|
||||
return (to_integral(value) >> base_type::entity_shift);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,12 +111,8 @@ public:
|
||||
* @param version The version part of the identifier.
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return value_type{entity & entity_mask};
|
||||
} else {
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
|
||||
}
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) ENTT_NOEXCEPT {
|
||||
return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -146,57 +125,36 @@ public:
|
||||
* @param rhs The identifier from which to take the version part.
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return value_type{lhs & entity_mask};
|
||||
} else {
|
||||
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
|
||||
}
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT {
|
||||
constexpr auto mask = (base_type::version_mask << base_type::entity_shift);
|
||||
return value_type{(lhs & base_type::entity_mask) | (rhs & mask)};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Entity traits.
|
||||
* @tparam Type Type of identifier.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
|
||||
/*! @brief Base type. */
|
||||
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
|
||||
/*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */
|
||||
static constexpr std::size_t page_size = ENTT_SPARSE_PAGE;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Converts an entity to its underlying type.
|
||||
* @copydoc entt_traits<Entity>::to_integral
|
||||
* @tparam Entity The value type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the given value.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_integral(const Entity value) ENTT_NOEXCEPT {
|
||||
return entt_traits<Entity>::to_integral(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity part once converted to the underlying type.
|
||||
* @copydoc entt_traits<Entity>::to_entity
|
||||
* @tparam Entity The value type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the entity part.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::entity_type to_entity(const Entity value) ENTT_NOEXCEPT {
|
||||
return entt_traits<Entity>::to_entity(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the version part once converted to the underlying type.
|
||||
* @copydoc entt_traits<Entity>::to_version
|
||||
* @tparam Entity The value type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
|
||||
[[nodiscard]] constexpr typename entt_traits<Entity>::version_type to_version(const Entity value) ENTT_NOEXCEPT {
|
||||
return entt_traits<Entity>::to_version(value);
|
||||
}
|
||||
|
||||
@@ -208,10 +166,9 @@ struct null_t {
|
||||
* @return The null representation for the given type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
|
||||
return value;
|
||||
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -219,7 +176,7 @@ struct null_t {
|
||||
* @param other A null object.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -228,7 +185,7 @@ struct null_t {
|
||||
* @param other A null object.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -239,9 +196,9 @@ struct null_t {
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
return traits_type::to_entity(entity) == traits_type::to_entity(*this);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::to_entity(entity) == entity_traits::to_entity(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +208,7 @@ struct null_t {
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
@@ -259,25 +216,25 @@ struct null_t {
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A null object yet to be converted.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
|
||||
return rhs.operator==(lhs);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a null object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A null object yet to be converted.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
|
||||
return !(rhs == lhs);
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
/*! @brief Tombstone object for all identifiers. */
|
||||
@@ -288,10 +245,9 @@ struct tombstone_t {
|
||||
* @return The tombstone representation for the given type.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask);
|
||||
return value;
|
||||
[[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::combine(entity_traits::reserved, entity_traits::reserved);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,7 +255,7 @@ struct tombstone_t {
|
||||
* @param other A tombstone object.
|
||||
* @return True in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -308,7 +264,7 @@ struct tombstone_t {
|
||||
* @param other A tombstone object.
|
||||
* @return False in all cases.
|
||||
*/
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -319,14 +275,9 @@ struct tombstone_t {
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
if constexpr(traits_type::version_mask == 0u) {
|
||||
return false;
|
||||
} else {
|
||||
return (traits_type::to_version(entity) == traits_type::to_version(*this));
|
||||
}
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT {
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
return entity_traits::to_version(entity) == entity_traits::to_version(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -336,7 +287,7 @@ struct tombstone_t {
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept {
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT {
|
||||
return !(entity == *this);
|
||||
}
|
||||
};
|
||||
@@ -344,25 +295,25 @@ struct tombstone_t {
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A tombstone object yet to be converted.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A tombstone object yet to be converted.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
|
||||
return rhs.operator==(lhs);
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
|
||||
return other.operator==(entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a tombstone object and an identifier of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @param lhs Identifier with which to compare.
|
||||
* @param rhs A tombstone object yet to be converted.
|
||||
* @param entity Identifier with which to compare.
|
||||
* @param other A tombstone object yet to be converted.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename Entity>
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
|
||||
return !(rhs == lhs);
|
||||
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT {
|
||||
return !(other == entity);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,62 +1,38 @@
|
||||
#ifndef ENTT_ENTITY_FWD_HPP
|
||||
#define ENTT_ENTITY_FWD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Default entity identifier. */
|
||||
enum class entity : id_type {};
|
||||
|
||||
/*! @brief Storage deletion policy. */
|
||||
enum class deletion_policy : std::uint8_t {
|
||||
/*! @brief Swap-and-pop deletion policy. */
|
||||
swap_and_pop = 0u,
|
||||
/*! @brief In-place deletion policy. */
|
||||
in_place = 1u,
|
||||
/*! @brief Swap-only deletion policy. */
|
||||
swap_only = 2u,
|
||||
/*! @brief Unspecified deletion policy. */
|
||||
unspecified = swap_and_pop
|
||||
};
|
||||
|
||||
template<typename Type, typename Entity = entity, typename = void>
|
||||
struct component_traits;
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
template<typename Entity, typename = std::allocator<Entity>>
|
||||
class basic_sparse_set;
|
||||
|
||||
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
|
||||
template<typename, typename Type, typename = std::allocator<Type>, typename = void>
|
||||
class basic_storage;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_sigh_mixin;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_reactive_mixin;
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
template<typename>
|
||||
class basic_registry;
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
template<typename, typename, typename, typename = void>
|
||||
class basic_view;
|
||||
|
||||
template<typename Type, typename = std::allocator<Type *>>
|
||||
class basic_runtime_view;
|
||||
template<typename>
|
||||
struct basic_runtime_view;
|
||||
|
||||
template<typename, typename, typename>
|
||||
template<typename, typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
template<typename>
|
||||
class basic_observer;
|
||||
|
||||
template<typename>
|
||||
class basic_organizer;
|
||||
|
||||
template<typename, typename...>
|
||||
class basic_handle;
|
||||
struct basic_handle;
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot;
|
||||
@@ -67,223 +43,74 @@ class basic_snapshot_loader;
|
||||
template<typename>
|
||||
class basic_continuous_loader;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using sparse_set = basic_sparse_set<>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Element type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using storage = basic_storage<Type>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Underlying storage type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Underlying storage type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
/*! @brief Default entity identifier. */
|
||||
enum class entity : id_type {};
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using organizer = basic_organizer<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using handle = basic_handle<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_handle = basic_handle<const registry>;
|
||||
using sparse_set = basic_sparse_set<entity>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using handle_view = basic_handle<registry, Args...>;
|
||||
using storage = basic_storage<entity, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using organizer = basic_organizer<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using handle = basic_handle<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_handle = basic_handle<const entity>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using const_handle_view = basic_handle<const registry, Args...>;
|
||||
using handle_view = basic_handle<entity, Args...>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using const_handle_view = basic_handle<const entity, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<registry>;
|
||||
using snapshot = basic_snapshot<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<registry>;
|
||||
using snapshot_loader = basic_snapshot_loader<entity>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<registry>;
|
||||
using continuous_loader = basic_continuous_loader<entity>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Get Types of components iterated by the view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
*/
|
||||
template<typename Get, typename Exclude = exclude_t<>>
|
||||
using view = basic_view<entity, Get, Exclude>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using runtime_view = basic_runtime_view<sparse_set>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_runtime_view = basic_runtime_view<const sparse_set>;
|
||||
|
||||
/**
|
||||
* @brief Alias for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct exclude_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr exclude_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct get_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr get_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of owned elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct owned_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit constexpr owned_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of owned elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr owned_t<Type...> owned{};
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to a get list and generate a new list.
|
||||
* @tparam Type Types provided by the get list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<get_t<Type...>, Op> {
|
||||
/*! @brief Resulting get list after applying the transform function. */
|
||||
using type = get_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to an exclude list and generate a new list.
|
||||
* @tparam Type Types provided by the exclude list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<exclude_t<Type...>, Op> {
|
||||
/*! @brief Resulting exclude list after applying the transform function. */
|
||||
using type = exclude_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Applies a given _function_ to an owned list and generate a new list.
|
||||
* @tparam Type Types provided by the owned list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<owned_t<Type...>, Op> {
|
||||
/*! @brief Resulting owned list after applying the transform function. */
|
||||
using type = owned_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides a common way to define storage types.
|
||||
* @tparam Type Storage value type.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
|
||||
struct storage_type {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
|
||||
};
|
||||
|
||||
/*! @brief Empty value type for reactive storage types. */
|
||||
struct reactive final {};
|
||||
|
||||
/**
|
||||
* @ brief Partial specialization for reactive storage types.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Entity, typename Allocator>
|
||||
struct storage_type<reactive, Entity, Allocator> {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_type_t = typename storage_type<Args...>::type;
|
||||
|
||||
/**
|
||||
* Type-to-storage conversion utility that preserves constness.
|
||||
* @tparam Type Storage value type, eventually const.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
|
||||
struct storage_for {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_for_t = typename storage_for<Args...>::type;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Get Types of storage iterated by the view.
|
||||
* @tparam Exclude Types of storage used to filter the view.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename Get, typename Exclude = exclude_t<>>
|
||||
using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Owned Types of storage _owned_ by the group.
|
||||
* @tparam Get Types of storage _observed_ by the group.
|
||||
* @tparam Exclude Types of storage used to filter the group.
|
||||
*/
|
||||
template<typename Owned, typename Get = get_t<>, typename Exclude = exclude_t<>>
|
||||
using group = basic_group<type_list_transform_t<Owned, storage_for>, type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
|
||||
template<typename... Args>
|
||||
using group = basic_group<entity, Args...>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,123 +1,38 @@
|
||||
#ifndef ENTT_ENTITY_HANDLE_HPP
|
||||
#define ENTT_ENTITY_HANDLE_HPP
|
||||
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/iterator.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
class handle_storage_iterator final {
|
||||
template<typename Other>
|
||||
friend class handle_storage_iterator;
|
||||
|
||||
using underlying_type = std::remove_reference_t<typename It::value_type::second_type>;
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
|
||||
public:
|
||||
using value_type = typename std::iterator_traits<It>::value_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;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr handle_storage_iterator() noexcept
|
||||
: entt{null},
|
||||
it{},
|
||||
last{} {}
|
||||
|
||||
constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept
|
||||
: entt{value},
|
||||
it{from},
|
||||
last{to} {
|
||||
while(it != last && !it->second.contains(entt)) {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr handle_storage_iterator &operator++() noexcept {
|
||||
for(++it; it != last && !it->second.contains(entt); ++it) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr handle_storage_iterator operator++(int) noexcept {
|
||||
const handle_storage_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *it;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return operator*();
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
friend constexpr bool operator==(const handle_storage_iterator<ILhs> &, const handle_storage_iterator<IRhs> &) noexcept;
|
||||
|
||||
private:
|
||||
entity_type entt;
|
||||
It it;
|
||||
It last;
|
||||
};
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
|
||||
return lhs.it == rhs.it;
|
||||
}
|
||||
|
||||
template<typename ILhs, typename IRhs>
|
||||
[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator<ILhs> &lhs, const handle_storage_iterator<IRhs> &rhs) noexcept {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Non-owning handle to an entity.
|
||||
*
|
||||
* Tiny wrapper around a registry and an entity.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Scope Types to which to restrict the scope of a handle.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Type Types to which to restrict the scope of a handle.
|
||||
*/
|
||||
template<typename Registry, typename... Scope>
|
||||
class basic_handle {
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<Registry &>(*owner);
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename Entity, typename... Type>
|
||||
struct basic_handle {
|
||||
/*! @brief Type of registry accepted by the handle. */
|
||||
using registry_type = Registry;
|
||||
using registry_type = constness_as_t<basic_registry<std::remove_const_t<Entity>>, Entity>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename traits_type::value_type;
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
/*! @brief Underlying version type. */
|
||||
using version_type = typename traits_type::version_type;
|
||||
using version_type = typename registry_type::version_type;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Iterable handle type. */
|
||||
using iterable = iterable_adaptor<internal::handle_storage_iterator<typename decltype(std::declval<registry_type>().storage())::iterator>>;
|
||||
using size_type = typename registry_type::size_type;
|
||||
|
||||
/*! @brief Constructs an invalid handle. */
|
||||
basic_handle() noexcept
|
||||
: owner{},
|
||||
basic_handle() ENTT_NOEXCEPT
|
||||
: reg{},
|
||||
entt{null} {}
|
||||
|
||||
/**
|
||||
@@ -125,228 +40,256 @@ public:
|
||||
* @param ref An instance of the registry class.
|
||||
* @param value A valid identifier.
|
||||
*/
|
||||
basic_handle(registry_type &ref, entity_type value) noexcept
|
||||
: owner{&ref},
|
||||
basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT
|
||||
: reg{&ref},
|
||||
entt{value} {}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterable object to use to _visit_ a handle.
|
||||
*
|
||||
* The iterable object returns a pair that contains the name and a reference
|
||||
* to the current storage.<br/>
|
||||
* Returned storage are those that contain the entity associated with the
|
||||
* handle.
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the handle.
|
||||
* @brief Constructs a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type (see entt_traits for more details).
|
||||
* @tparam Args Scope of the handle to construct.
|
||||
* @return A const handle referring to the same registry and the same
|
||||
* entity.
|
||||
*/
|
||||
[[nodiscard]] iterable storage() const noexcept {
|
||||
auto underlying = owner_or_assert().storage();
|
||||
return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}};
|
||||
}
|
||||
template<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const ENTT_NOEXCEPT {
|
||||
static_assert(std::is_same_v<Other, Entity> || std::is_same_v<std::remove_const_t<Other>, Entity>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v<type_list<Type...>, Args>))), "Invalid conversion between different handles");
|
||||
|
||||
/*! @copydoc valid */
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return owner && owner->valid(entt);
|
||||
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to a valid registry and entity.
|
||||
* @return True if the handle refers to a valid registry and entity, false
|
||||
* otherwise.
|
||||
* @brief Converts a handle to its underlying entity.
|
||||
* @return The contained identifier.
|
||||
*/
|
||||
[[nodiscard]] operator entity_type() const ENTT_NOEXCEPT {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to non-null registry pointer and entity.
|
||||
* @return True if the handle refers to non-null registry and entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT {
|
||||
return reg && reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle refers to a valid entity or not.
|
||||
* @return True if the handle refers to a valid entity, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool valid() const {
|
||||
return static_cast<bool>(*this);
|
||||
return reg->valid(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] registry_type *registry() const noexcept {
|
||||
return owner;
|
||||
[[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT {
|
||||
return reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a handle.
|
||||
* @return The entity associated with the handle.
|
||||
*/
|
||||
[[nodiscard]] entity_type entity() const noexcept {
|
||||
[[nodiscard]] entity_type entity() const ENTT_NOEXCEPT {
|
||||
return entt;
|
||||
}
|
||||
|
||||
/*! @copydoc entity */
|
||||
[[nodiscard]] operator entity_type() const noexcept {
|
||||
return entity();
|
||||
}
|
||||
|
||||
/*! @brief Destroys the entity associated with a handle. */
|
||||
void destroy() {
|
||||
owner_or_assert().destroy(std::exchange(entt, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
*/
|
||||
void destroy() {
|
||||
reg->destroy(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys the entity associated with a handle.
|
||||
* @sa basic_registry::destroy
|
||||
* @param version A desired version upon destruction.
|
||||
*/
|
||||
void destroy(const version_type version) {
|
||||
owner_or_assert().destroy(std::exchange(entt, null), version);
|
||||
reg->destroy(entt, version);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns the given element to a handle.
|
||||
* @tparam Type Type of element to create.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the newly created element.
|
||||
* @brief Assigns the given component to a handle.
|
||||
* @sa basic_registry::emplace
|
||||
* @tparam Component Type of component to create.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template emplace<Type>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns or replaces the given element for a handle.
|
||||
* @tparam Type Type of element to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the newly created element.
|
||||
* @brief Assigns or replaces the given component for a handle.
|
||||
* @sa basic_registry::emplace_or_replace
|
||||
* @tparam Component Type of component to assign or replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the newly created component.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) emplace_or_replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template emplace_or_replace<Type>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given element for a handle.
|
||||
* @tparam Type Type of element to patch.
|
||||
* @brief Patches the given component for a handle.
|
||||
* @sa basic_registry::patch
|
||||
* @tparam Component Type of component to patch.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched element.
|
||||
* @return A reference to the patched component.
|
||||
*/
|
||||
template<typename Type, typename... Func>
|
||||
template<typename Component, typename... Func>
|
||||
decltype(auto) patch(Func &&...func) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template patch<Type>(entt, std::forward<Func>(func)...);
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the given element for a handle.
|
||||
* @tparam Type Type of element to replace.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return A reference to the element being replaced.
|
||||
* @brief Replaces the given component for a handle.
|
||||
* @sa basic_registry::replace
|
||||
* @tparam Component Type of component to replace.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return A reference to the component being replaced.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
template<typename Component, typename... Args>
|
||||
decltype(auto) replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template replace<Type>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given elements from a handle.
|
||||
* @tparam Type Types of elements to remove.
|
||||
* @return The number of elements actually removed.
|
||||
* @brief Removes the given components from a handle.
|
||||
* @sa basic_registry::remove
|
||||
* @tparam Component Types of components to remove.
|
||||
* @return The number of components actually removed.
|
||||
*/
|
||||
template<typename... Type>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
template<typename... Component>
|
||||
size_type remove() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template remove<Type...>(entt);
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
return reg->template remove<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given elements from a handle.
|
||||
* @tparam Type Types of elements to erase.
|
||||
* @brief Erases the given components from a handle.
|
||||
* @sa basic_registry::erase
|
||||
* @tparam Component Types of components to erase.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename... Component>
|
||||
void erase() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
owner_or_assert().template erase<Type...>(entt);
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
reg->template erase<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has all the given elements.
|
||||
* @tparam Type Elements for which to perform the check.
|
||||
* @return True if the handle has all the elements, false otherwise.
|
||||
* @brief Checks if a handle has all the given components.
|
||||
* @sa basic_registry::all_of
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has all the components, false otherwise.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) all_of() const {
|
||||
return owner_or_assert().template all_of<Type...>(entt);
|
||||
return reg->template all_of<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has at least one of the given elements.
|
||||
* @tparam Type Elements for which to perform the check.
|
||||
* @return True if the handle has at least one of the given elements,
|
||||
* @brief Checks if a handle has at least one of the given components.
|
||||
* @sa basic_registry::any_of
|
||||
* @tparam Component Components for which to perform the check.
|
||||
* @return True if the handle has at least one of the given components,
|
||||
* false otherwise.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) any_of() const {
|
||||
return owner_or_assert().template any_of<Type...>(entt);
|
||||
return reg->template any_of<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns references to the given elements for a handle.
|
||||
* @tparam Type Types of elements to get.
|
||||
* @return References to the elements owned by the handle.
|
||||
* @brief Returns references to the given components for a handle.
|
||||
* @sa basic_registry::get
|
||||
* @tparam Component Types of components to get.
|
||||
* @return References to the components owned by the handle.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename... Component>
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template get<Type...>(entt);
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
return reg->template get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the given element for a handle.
|
||||
* @tparam Type Type of element to get.
|
||||
* @tparam Args Types of arguments to use to construct the element.
|
||||
* @param args Parameters to use to initialize the element.
|
||||
* @return Reference to the element owned by the handle.
|
||||
* @brief Returns a reference to the given component for a handle.
|
||||
* @sa basic_registry::get_or_emplace
|
||||
* @tparam Component Type of component to get.
|
||||
* @tparam Args Types of arguments to use to construct the component.
|
||||
* @param args Parameters to use to initialize the component.
|
||||
* @return Reference to the component owned by the handle.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
template<typename Component, typename... Args>
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template get_or_emplace<Type>(entt, std::forward<Args>(args)...);
|
||||
static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v<Component, Type>), "Invalid type");
|
||||
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns pointers to the given elements for a handle.
|
||||
* @tparam Type Types of elements to get.
|
||||
* @return Pointers to the elements owned by the handle.
|
||||
* @brief Returns pointers to the given components for a handle.
|
||||
* @sa basic_registry::try_get
|
||||
* @tparam Component Types of components to get.
|
||||
* @return Pointers to the components owned by the handle.
|
||||
*/
|
||||
template<typename... Type>
|
||||
template<typename... Component>
|
||||
[[nodiscard]] auto try_get() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template try_get<Type...>(entt);
|
||||
static_assert(sizeof...(Type) == 0 || (type_list_contains_v<type_list<Type...>, Component> && ...), "Invalid type");
|
||||
return reg->template try_get<Component...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a handle has elements assigned.
|
||||
* @return True if the handle has no elements assigned, false otherwise.
|
||||
* @brief Checks if a handle has components assigned.
|
||||
* @return True if the handle has no components assigned, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool orphan() const {
|
||||
return owner_or_assert().orphan(entt);
|
||||
return reg->orphan(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a const handle from a non-const one.
|
||||
* @tparam Other A valid entity type.
|
||||
* @tparam Args Scope of the handle to construct.
|
||||
* @return A const handle referring to the same registry and the same
|
||||
* entity.
|
||||
* @brief Visits a handle and returns the pools for its components.
|
||||
*
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(id_type, const basic_sparse_set<entity_type> &);
|
||||
* @endcode
|
||||
*
|
||||
* Returned pools are those that contain the entity associated with the
|
||||
* handle.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const noexcept {
|
||||
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
|
||||
return owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{};
|
||||
template<typename Func>
|
||||
void visit(Func &&func) const {
|
||||
for(auto [id, storage]: reg->storage()) {
|
||||
if(storage.contains(entt)) {
|
||||
func(id, storage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *owner;
|
||||
registry_type *reg;
|
||||
entity_type entt;
|
||||
};
|
||||
|
||||
@@ -360,7 +303,7 @@ private:
|
||||
* entity, false otherwise.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
|
||||
[[nodiscard]] bool operator==(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
|
||||
return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity();
|
||||
}
|
||||
|
||||
@@ -374,57 +317,23 @@ template<typename... Args, typename... Other>
|
||||
* entity, true otherwise.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) noexcept {
|
||||
[[nodiscard]] bool operator!=(const basic_handle<Args...> &lhs, const basic_handle<Other...> &rhs) ENTT_NOEXCEPT {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
|
||||
return (lhs.entity() == rhs);
|
||||
}
|
||||
template<typename Entity>
|
||||
basic_handle(basic_registry<Entity> &, Entity) -> basic_handle<Entity>;
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A null object yet to be converted.
|
||||
* @param rhs A valid handle.
|
||||
* @return False if the two elements differ, true otherwise.
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
|
||||
return (rhs == lhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A valid handle.
|
||||
* @param rhs A null object yet to be converted.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
|
||||
return (lhs.entity() != rhs);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Compares a handle with the null object.
|
||||
* @tparam Args Scope of the handle.
|
||||
* @param lhs A null object yet to be converted.
|
||||
* @param rhs A valid handle.
|
||||
* @return True if the two elements differ, false otherwise.
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
|
||||
return (rhs != lhs);
|
||||
}
|
||||
template<typename Entity>
|
||||
basic_handle(const basic_registry<Entity> &, Entity) -> basic_handle<const Entity>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -1,255 +1,155 @@
|
||||
#ifndef ENTT_ENTITY_HELPER_HPP
|
||||
#define ENTT_ENTITY_HELPER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "component.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "group.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "view.hpp"
|
||||
#include "registry.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a view.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Registry>
|
||||
class as_view {
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
return reg->template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = Registry;
|
||||
template<typename Entity>
|
||||
struct as_view {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_view(registry_type &source) noexcept
|
||||
: reg{&source} {}
|
||||
as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a view.
|
||||
* @tparam Get Type of storage used to construct the view.
|
||||
* @tparam Exclude Types of storage used to filter the view.
|
||||
* @tparam Exclude Types of components used to filter the view.
|
||||
* @tparam Component Type of components used to construct the view.
|
||||
* @return A newly created view.
|
||||
*/
|
||||
template<typename Get, typename Exclude>
|
||||
operator basic_view<Get, Exclude>() const {
|
||||
return dispatch(Get{}, Exclude{});
|
||||
template<typename Exclude, typename... Component>
|
||||
operator basic_view<entity_type, get_t<Component...>, Exclude>() const {
|
||||
return reg.template view<Component...>(Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a group.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
class as_group {
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
[[nodiscard]] auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
if constexpr(std::is_const_v<registry_type>) {
|
||||
return reg->template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_type...>{});
|
||||
} else {
|
||||
return reg->template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_group(registry_type &source) noexcept
|
||||
: reg{&source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a group.
|
||||
* @tparam Owned Types of _owned_ by the group.
|
||||
* @tparam Get Types of storage _observed_ by the group.
|
||||
* @tparam Exclude Types of storage used to filter the group.
|
||||
* @return A newly created group.
|
||||
*/
|
||||
template<typename Owned, typename Get, typename Exclude>
|
||||
operator basic_group<Owned, Get, Exclude>() const {
|
||||
return dispatch(Owned{}, Get{}, Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper to create a listener that directly invokes a member function.
|
||||
* @tparam Member Member function to invoke on an element of the given type.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @param reg A registry that contains the given entity and its elements.
|
||||
* @param entt Entity from which to get the element.
|
||||
*/
|
||||
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>>
|
||||
void invoke(Registry ®, const typename Registry::entity_type entt) {
|
||||
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
|
||||
(reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a given element.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default storage as it
|
||||
* makes assumptions about how the elements are laid out.
|
||||
*
|
||||
* @tparam Args Storage type template parameters.
|
||||
* @param storage A storage that contains the given element.
|
||||
* @param instance A valid element instance.
|
||||
* @return The entity associated with the given element.
|
||||
*/
|
||||
template<typename... Args>
|
||||
typename basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) {
|
||||
using traits_type = component_traits<typename basic_storage<Args...>::value_type, typename basic_storage<Args...>::entity_type>;
|
||||
static_assert(traits_type::page_size != 0u, "Unexpected page size");
|
||||
const auto *page = storage.raw();
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
for(std::size_t pos{}, count = storage.size(); pos < count; pos += traits_type::page_size, ++page) {
|
||||
if(const auto dist = (std::addressof(instance) - *page); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::page_size)) {
|
||||
return *(static_cast<const typename basic_storage<Args...>::base_type &>(storage).rbegin() + static_cast<decltype(dist)>(pos) + dist);
|
||||
}
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct sigh_helper;
|
||||
|
||||
/**
|
||||
* @brief Signal connection helper for registries.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
struct sigh_helper<Registry> {
|
||||
/*! @brief Registry type. */
|
||||
using registry_type = Registry;
|
||||
|
||||
/**
|
||||
* @brief Constructs a helper for a given registry.
|
||||
* @param ref A valid reference to a registry.
|
||||
*/
|
||||
sigh_helper(registry_type &ref)
|
||||
: bucket{&ref} {}
|
||||
|
||||
/**
|
||||
* @brief Binds a properly initialized helper to a given signal type.
|
||||
* @tparam Type Type of signal to bind the helper to.
|
||||
* @param id Optional name for the underlying storage to use.
|
||||
* @return A helper for a given registry and signal type.
|
||||
*/
|
||||
template<typename Type>
|
||||
auto with(const id_type id = type_hash<Type>::value()) noexcept {
|
||||
return sigh_helper<registry_type, Type>{*bucket, id};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the underlying registry.
|
||||
* @return A reference to the underlying registry.
|
||||
*/
|
||||
[[nodiscard]] registry_type ®istry() noexcept {
|
||||
return *bucket;
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *bucket;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Signal connection helper for registries.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Type Type of signal to connect listeners to.
|
||||
*/
|
||||
template<typename Registry, typename Type>
|
||||
struct sigh_helper<Registry, Type> final: sigh_helper<Registry> {
|
||||
/*! @brief Registry type. */
|
||||
using registry_type = Registry;
|
||||
|
||||
/**
|
||||
* @brief Constructs a helper for a given registry.
|
||||
* @param ref A valid reference to a registry.
|
||||
* @param id Optional name for the underlying storage to use.
|
||||
*/
|
||||
sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value())
|
||||
: sigh_helper<Registry>{ref},
|
||||
name{id} {}
|
||||
|
||||
/**
|
||||
* @brief Forwards the call to `on_construct` on the underlying storage.
|
||||
* @tparam Candidate Function or member to connect.
|
||||
* @tparam Args Type of class or type of payload, if any.
|
||||
* @param args A valid object that fits the purpose, if any.
|
||||
* @return This helper.
|
||||
*/
|
||||
template<auto Candidate, typename... Args>
|
||||
auto on_construct(Args &&...args) {
|
||||
this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards the call to `on_update` on the underlying storage.
|
||||
* @tparam Candidate Function or member to connect.
|
||||
* @tparam Args Type of class or type of payload, if any.
|
||||
* @param args A valid object that fits the purpose, if any.
|
||||
* @return This helper.
|
||||
*/
|
||||
template<auto Candidate, typename... Args>
|
||||
auto on_update(Args &&...args) {
|
||||
this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards the call to `on_destroy` on the underlying storage.
|
||||
* @tparam Candidate Function or member to connect.
|
||||
* @tparam Args Type of class or type of payload, if any.
|
||||
* @param args A valid object that fits the purpose, if any.
|
||||
* @return This helper.
|
||||
*/
|
||||
template<auto Candidate, typename... Args>
|
||||
auto on_destroy(Args &&...args) {
|
||||
this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
id_type name;
|
||||
registry_type ®
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Registry>
|
||||
sigh_helper(Registry &) -> sigh_helper<Registry>;
|
||||
template<typename Entity>
|
||||
as_view(basic_registry<Entity> &) -> as_view<Entity>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_view(const basic_registry<Entity> &) -> as_view<const Entity>;
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a group.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
struct as_group {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = std::remove_const_t<Entity>;
|
||||
/*! @brief Type of registry to convert. */
|
||||
using registry_type = constness_as_t<basic_registry<entity_type>, Entity>;
|
||||
|
||||
/**
|
||||
* @brief Constructs a converter for a given registry.
|
||||
* @param source A valid reference to a registry.
|
||||
*/
|
||||
as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {}
|
||||
|
||||
/**
|
||||
* @brief Conversion function from a registry to a group.
|
||||
* @tparam Get Types of components observed by the group.
|
||||
* @tparam Exclude Types of components used to filter the group.
|
||||
* @tparam Owned Types of components owned by the group.
|
||||
* @return A newly created group.
|
||||
*/
|
||||
template<typename Get, typename Exclude, typename... Owned>
|
||||
operator basic_group<entity_type, owned_t<Owned...>, Get, Exclude>() const {
|
||||
if constexpr(std::is_const_v<registry_type>) {
|
||||
return reg.template group_if_exists<Owned...>(Get{}, Exclude{});
|
||||
} else {
|
||||
return reg.template group<Owned...>(Get{}, Exclude{});
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type ®
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_group(basic_registry<Entity> &) -> as_group<Entity>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
as_group(const basic_registry<Entity> &) -> as_group<const Entity>;
|
||||
|
||||
/**
|
||||
* @brief Helper to create a listener that directly invokes a member function.
|
||||
* @tparam Member Member function to invoke on a component of the given type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param entt Entity from which to get the component.
|
||||
*/
|
||||
template<auto Member, typename Entity = entity>
|
||||
void invoke(basic_registry<Entity> ®, const Entity entt) {
|
||||
static_assert(std::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
|
||||
delegate<void(basic_registry<Entity> &, const Entity)> func;
|
||||
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
|
||||
func(reg, entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a given component.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default pool as it
|
||||
* makes assumptions about how the components are laid out.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Component Type of component.
|
||||
* @param reg A registry that contains the given entity and its components.
|
||||
* @param instance A valid component instance.
|
||||
* @return The entity associated with the given component.
|
||||
*/
|
||||
template<typename Entity, typename Component>
|
||||
Entity to_entity(const basic_registry<Entity> ®, const Component &instance) {
|
||||
const auto &storage = reg.template storage<Component>();
|
||||
const typename basic_registry<Entity>::base_type &base = storage;
|
||||
const auto *addr = std::addressof(instance);
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) {
|
||||
return *(it + dist);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -1,591 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_MIXIN_HPP
|
||||
#define ENTT_ENTITY_MIXIN_HPP
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/any.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
struct has_on_construct final: std::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
struct has_on_construct<Type, Registry, std::void_t<decltype(Type::on_construct(std::declval<Registry &>(), std::declval<Registry>().create()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
struct has_on_update final: std::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
struct has_on_update<Type, Registry, std::void_t<decltype(Type::on_update(std::declval<Registry &>(), std::declval<Registry>().create()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template<typename, typename, typename = void>
|
||||
struct has_on_destroy final: std::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
struct has_on_destroy<Type, Registry, std::void_t<decltype(Type::on_destroy(std::declval<Registry &>(), std::declval<Registry>().create()))>>
|
||||
: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add signal support to storage types.
|
||||
*
|
||||
* The function type of a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* This applies to all signals made available.
|
||||
*
|
||||
* @tparam Type Underlying storage type.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Type, typename Registry>
|
||||
class basic_sigh_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
|
||||
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
|
||||
using underlying_iterator = typename underlying_type::base_type::basic_iterator;
|
||||
|
||||
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*owner);
|
||||
}
|
||||
|
||||
private:
|
||||
void pop(underlying_iterator first, underlying_iterator last) final {
|
||||
if(auto ® = owner_or_assert(); destruction.empty()) {
|
||||
underlying_type::pop(first, last);
|
||||
} else {
|
||||
for(; first != last; ++first) {
|
||||
const auto entt = *first;
|
||||
destruction.publish(reg, entt);
|
||||
const auto it = underlying_type::find(entt);
|
||||
underlying_type::pop(it, it + 1u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pop_all() final {
|
||||
if(auto ® = owner_or_assert(); !destruction.empty()) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
|
||||
for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
|
||||
destruction.publish(reg, underlying_type::base_type::operator[](pos));
|
||||
}
|
||||
} else {
|
||||
for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
|
||||
if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
|
||||
if(entt != tombstone) {
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
} else {
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
underlying_type::pop_all();
|
||||
}
|
||||
|
||||
underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final {
|
||||
const auto it = underlying_type::try_emplace(entt, force_back, value);
|
||||
|
||||
if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) {
|
||||
construction.publish(reg, *it);
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
void bind_any(any value) noexcept final {
|
||||
owner = any_cast<basic_registry_type>(&value);
|
||||
|
||||
if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
|
||||
if(owner == nullptr) {
|
||||
owner = any_cast<registry_type>(&value);
|
||||
}
|
||||
}
|
||||
|
||||
underlying_type::bind_any(std::move(value));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = typename underlying_type::allocator_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
/*! @brief Expected registry type. */
|
||||
using registry_type = owner_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_sigh_mixin()
|
||||
: basic_sigh_mixin{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty storage with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_sigh_mixin(const allocator_type &allocator)
|
||||
: underlying_type{allocator},
|
||||
owner{},
|
||||
construction{allocator},
|
||||
destruction{allocator},
|
||||
update{allocator} {
|
||||
if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
|
||||
sink{construction}.template connect<&underlying_type::element_type::on_construct>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
|
||||
sink{update}.template connect<&underlying_type::element_type::on_update>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
|
||||
sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_sigh_mixin(const basic_sigh_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
|
||||
: underlying_type{static_cast<underlying_type &&>(other)},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction)},
|
||||
destruction{std::move(other.destruction)},
|
||||
update{std::move(other.update)} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{static_cast<underlying_type &&>(other), allocator},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction), allocator},
|
||||
destruction{std::move(other.destruction), allocator},
|
||||
update{std::move(other.update), allocator} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_sigh_mixin() override = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
|
||||
swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_sigh_mixin &other) noexcept {
|
||||
using std::swap;
|
||||
swap(owner, other.owner);
|
||||
swap(construction, other.construction);
|
||||
swap(destruction, other.destruction);
|
||||
swap(update, other.update);
|
||||
underlying_type::swap(other);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever a new instance is created and assigned to an entity.<br/>
|
||||
* Listeners are invoked after the object has been assigned to the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_construct() noexcept {
|
||||
return sink{construction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is explicitly updated.<br/>
|
||||
* Listeners are invoked after the object has been updated.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_update() noexcept {
|
||||
return sink{update};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is removed from an entity and thus destroyed.<br/>
|
||||
* Listeners are invoked before the object has been removed from the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_destroy() noexcept {
|
||||
return sink{destruction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a mixin refers to a valid registry.
|
||||
* @return True if the mixin refers to a valid registry, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return (owner != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] const registry_type ®istry() const noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/*! @copydoc registry */
|
||||
[[nodiscard]] registry_type ®istry() noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
auto generate() {
|
||||
const auto entt = underlying_type::generate();
|
||||
construction.publish(owner_or_assert(), entt);
|
||||
return entt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @param hint Required identifier.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
entity_type generate(const entity_type hint) {
|
||||
const auto entt = underlying_type::generate(hint);
|
||||
construction.publish(owner_or_assert(), entt);
|
||||
return entt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns each element in a range an identifier.
|
||||
* @tparam It Type of mutable forward iterator.
|
||||
* @param first An iterator to the first element of the range to generate.
|
||||
* @param last An iterator past the last element of the range to generate.
|
||||
*/
|
||||
template<typename It>
|
||||
void generate(It first, It last) {
|
||||
underlying_type::generate(first, last);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
for(; first != last; ++first) {
|
||||
construction.publish(reg, *first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a storage and constructs its object.
|
||||
* @tparam Args Types of arguments to forward to the underlying storage.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to forward to the underlying storage.
|
||||
* @return A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(const entity_type entt, Args &&...args) {
|
||||
underlying_type::emplace(entt, std::forward<Args>(args)...);
|
||||
construction.publish(owner_or_assert(), entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the instance assigned to a given entity in-place.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
underlying_type::patch(entt, std::forward<Func>(func)...);
|
||||
update.publish(owner_or_assert(), entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one or more entities to a storage and constructs their
|
||||
* objects from a given instance.
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of arguments to forward to the underlying storage.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param args Parameters to use to forward to the underlying storage.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
auto from = underlying_type::size();
|
||||
underlying_type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
if(auto ® = owner_or_assert(); !construction.empty()) {
|
||||
// fine as long as insert passes force_back true to try_emplace
|
||||
for(const auto to = underlying_type::size(); from != to; ++from) {
|
||||
construction.publish(reg, underlying_type::operator[](from));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry_type *owner;
|
||||
sigh_type construction;
|
||||
sigh_type destruction;
|
||||
sigh_type update;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add _reactive_ support to storage types.
|
||||
* @tparam Type Underlying storage type.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Type, typename Registry>
|
||||
class basic_reactive_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using alloc_traits = std::allocator_traits<typename underlying_type::allocator_type>;
|
||||
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
|
||||
using container_type = std::vector<connection, typename alloc_traits::template rebind_alloc<connection>>;
|
||||
|
||||
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*owner);
|
||||
}
|
||||
|
||||
void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
|
||||
if(!underlying_type::contains(entity)) {
|
||||
underlying_type::emplace(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void bind_any(any value) noexcept final {
|
||||
owner = any_cast<basic_registry_type>(&value);
|
||||
|
||||
if constexpr(!std::is_same_v<registry_type, basic_registry_type>) {
|
||||
if(owner == nullptr) {
|
||||
owner = any_cast<registry_type>(&value);
|
||||
}
|
||||
}
|
||||
|
||||
underlying_type::bind_any(std::move(value));
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = typename underlying_type::allocator_type;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
/*! @brief Expected registry type. */
|
||||
using registry_type = owner_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_reactive_mixin()
|
||||
: basic_reactive_mixin{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty storage with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_reactive_mixin(const allocator_type &allocator)
|
||||
: underlying_type{allocator},
|
||||
owner{},
|
||||
conn{allocator} {
|
||||
}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_reactive_mixin(const basic_reactive_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
|
||||
: underlying_type{static_cast<underlying_type &&>(other)},
|
||||
owner{other.owner},
|
||||
conn{std::move(other.conn)} {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
|
||||
: underlying_type{static_cast<underlying_type &&>(other), allocator},
|
||||
owner{other.owner},
|
||||
conn{std::move(other.conn), allocator} {
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_reactive_mixin() override = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This mixin.
|
||||
*/
|
||||
basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept {
|
||||
underlying_type::swap(other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes storage _react_ to creation of objects of the given type.
|
||||
* @tparam Clazz Type of element to _react_ to.
|
||||
* @tparam Candidate Function to use to _react_ to the event.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return This mixin.
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
|
||||
conn.push_back(std::move(curr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes storage _react_ to update of objects of the given type.
|
||||
* @tparam Clazz Type of element to _react_ to.
|
||||
* @tparam Candidate Function to use to _react_ to the event.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return This mixin.
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
|
||||
conn.push_back(std::move(curr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes storage _react_ to destruction of objects of the given type.
|
||||
* @tparam Clazz Type of element to _react_ to.
|
||||
* @tparam Candidate Function to use to _react_ to the event.
|
||||
* @param id Optional name used to map the storage within the registry.
|
||||
* @return This mixin.
|
||||
*/
|
||||
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
|
||||
conn.push_back(std::move(curr));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a mixin refers to a valid registry.
|
||||
* @return True if the mixin refers to a valid registry, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return (owner != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the underlying registry, if any.
|
||||
* @return A pointer to the underlying registry, if any.
|
||||
*/
|
||||
[[nodiscard]] const registry_type ®istry() const noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/*! @copydoc registry */
|
||||
[[nodiscard]] registry_type ®istry() noexcept {
|
||||
return owner_or_assert();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a view that is filtered by the underlying storage.
|
||||
* @tparam Get Types of elements used to construct the view.
|
||||
* @tparam Exclude Types of elements used to filter the view.
|
||||
* @return A newly created view.
|
||||
*/
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) const {
|
||||
const owner_type &parent = owner_or_assert();
|
||||
basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>> elem{};
|
||||
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<std::remove_const_t<Exclude>>()..., parent.template storage<std::remove_const_t<Get>>()..., this);
|
||||
return elem;
|
||||
}
|
||||
|
||||
/*! @copydoc view */
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) {
|
||||
std::conditional_t<((std::is_const_v<Get> && ...) && (std::is_const_v<Exclude> && ...)), const owner_type, owner_type> &parent = owner_or_assert();
|
||||
return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
|
||||
}
|
||||
|
||||
/*! @brief Releases all connections to the underlying registry, if any. */
|
||||
void reset() {
|
||||
for(auto &&curr: conn) {
|
||||
curr.release();
|
||||
}
|
||||
|
||||
conn.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
basic_registry_type *owner;
|
||||
container_type conn;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
436
src/entt/entity/observer.hpp
Normal file
436
src/entt/entity/observer.hpp
Normal file
@@ -0,0 +1,436 @@
|
||||
#ifndef ENTT_ENTITY_OBSERVER_HPP
|
||||
#define ENTT_ENTITY_OBSERVER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "registry.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @brief Grouping matcher. */
|
||||
template<typename...>
|
||||
struct matcher {};
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename...>
|
||||
struct basic_collector;
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
*
|
||||
* A collector contains a set of rules (literally, matchers) to use to track
|
||||
* entities.<br/>
|
||||
* Its main purpose is to generate a descriptor that allows an observer to know
|
||||
* how to connect to a registry.
|
||||
*/
|
||||
template<>
|
||||
struct basic_collector<> {
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>>{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Collector.
|
||||
* @copydetails basic_collector<>
|
||||
* @tparam Reject Untracked types used to filter out entities.
|
||||
* @tparam Require Untracked types required by the matcher.
|
||||
* @tparam Rule Specific details of the current matcher.
|
||||
* @tparam Other Other matchers.
|
||||
*/
|
||||
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
|
||||
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
|
||||
/*! @brief Current matcher. */
|
||||
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
|
||||
|
||||
/**
|
||||
* @brief Adds a grouping matcher to the collector.
|
||||
* @tparam AllOf Types of components tracked by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto group(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an observing matcher to the collector.
|
||||
* @tparam AnyOf Type of component for which changes should be detected.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename AnyOf>
|
||||
static constexpr auto update() ENTT_NOEXCEPT {
|
||||
return basic_collector<matcher<type_list<>, type_list<>, AnyOf>, current_type, Other...>{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the filter of the last added matcher.
|
||||
* @tparam AllOf Types of components required by the matcher.
|
||||
* @tparam NoneOf Types of components used to filter out entities.
|
||||
* @return The updated collector.
|
||||
*/
|
||||
template<typename... AllOf, typename... NoneOf>
|
||||
static constexpr auto where(exclude_t<NoneOf...> = {}) ENTT_NOEXCEPT {
|
||||
using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>;
|
||||
return basic_collector<extended_type, Other...>{};
|
||||
}
|
||||
};
|
||||
|
||||
/*! @brief Variable template used to ease the definition of collectors. */
|
||||
inline constexpr basic_collector<> collector{};
|
||||
|
||||
/**
|
||||
* @brief Observer.
|
||||
*
|
||||
* An observer returns all the entities and only the entities that fit the
|
||||
* requirements of at least one matcher. Moreover, it's guaranteed that the
|
||||
* entity list is tightly packed in memory for fast iterations.<br/>
|
||||
* In general, observers don't stay true to the order of any set of components.
|
||||
*
|
||||
* Observers work mainly with two types of matchers, provided through a
|
||||
* collector:
|
||||
*
|
||||
* * Observing matcher: an observer will return at least all the living entities
|
||||
* for which one or more of the given components have been updated and not yet
|
||||
* destroyed.
|
||||
* * Grouping matcher: an observer will return at least all the living entities
|
||||
* that would have entered the given group if it existed and that would have
|
||||
* not yet left it.
|
||||
*
|
||||
* If an entity respects the requirements of multiple matchers, it will be
|
||||
* returned once and only once by the observer in any case.
|
||||
*
|
||||
* Matchers support also filtering by means of a _where_ clause that accepts
|
||||
* both a list of types and an exclusion list.<br/>
|
||||
* Whenever a matcher finds that an entity matches its requirements, the
|
||||
* condition of the filter is verified before to register the entity itself.
|
||||
* Moreover, a registered entity isn't returned by the observer if the condition
|
||||
* set by the filter is broken in the meantime.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of an observer doesn't necessarily have to overcome that of the
|
||||
* registry to which it is connected. However, the observer must be disconnected
|
||||
* from the registry before being destroyed to avoid crashes due to dangling
|
||||
* pointers.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Entity>
|
||||
class basic_observer {
|
||||
using payload_type = std::uint32_t;
|
||||
|
||||
template<typename>
|
||||
struct matcher_handler;
|
||||
|
||||
template<typename... Reject, typename... Require, typename AnyOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
|
||||
template<std::size_t Index>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
|
||||
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
|
||||
obs.storage.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
|
||||
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(obs), ...);
|
||||
reg.template on_update<AnyOf>().disconnect(obs);
|
||||
reg.template on_destroy<AnyOf>().disconnect(obs);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
|
||||
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
|
||||
template<std::size_t Index, typename... Ignore>
|
||||
static void maybe_valid_if(basic_observer &obs, basic_registry<Entity> ®, const Entity entt) {
|
||||
auto condition = [®, entt]() {
|
||||
if constexpr(sizeof...(Ignore) == 0) {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
|
||||
} else {
|
||||
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
|
||||
}
|
||||
};
|
||||
|
||||
if(condition()) {
|
||||
if(!obs.storage.contains(entt)) {
|
||||
obs.storage.emplace(entt);
|
||||
}
|
||||
|
||||
obs.storage.get(entt) |= (1 << Index);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void discard_if(basic_observer &obs, basic_registry<Entity> &, const Entity entt) {
|
||||
if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) {
|
||||
obs.storage.erase(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t Index>
|
||||
static void connect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
|
||||
}
|
||||
|
||||
static void disconnect(basic_observer &obs, basic_registry<Entity> ®) {
|
||||
(reg.template on_destroy<Require>().disconnect(obs), ...);
|
||||
(reg.template on_construct<Reject>().disconnect(obs), ...);
|
||||
(reg.template on_construct<AllOf>().disconnect(obs), ...);
|
||||
(reg.template on_destroy<NoneOf>().disconnect(obs), ...);
|
||||
(reg.template on_destroy<AllOf>().disconnect(obs), ...);
|
||||
(reg.template on_construct<NoneOf>().disconnect(obs), ...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename... Matcher>
|
||||
static void disconnect(basic_registry<Entity> ®, basic_observer &obs) {
|
||||
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
|
||||
}
|
||||
|
||||
template<typename... Matcher, std::size_t... Index>
|
||||
void connect(basic_registry<Entity> ®, std::index_sequence<Index...>) {
|
||||
static_assert(sizeof...(Matcher) < std::numeric_limits<payload_type>::digits, "Too many matchers");
|
||||
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
|
||||
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Random access iterator type. */
|
||||
using iterator = typename basic_sparse_set<Entity>::iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_observer()
|
||||
: release{},
|
||||
storage{} {}
|
||||
|
||||
/*! @brief Default copy constructor, deleted on purpose. */
|
||||
basic_observer(const basic_observer &) = delete;
|
||||
/*! @brief Default move constructor, deleted on purpose. */
|
||||
basic_observer(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Creates an observer and connects it to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
basic_observer(basic_registry<entity_type> ®, basic_collector<Matcher...>)
|
||||
: basic_observer{} {
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_observer() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer &operator=(const basic_observer &) = delete;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator, deleted on purpose.
|
||||
* @return This observer.
|
||||
*/
|
||||
basic_observer &operator=(basic_observer &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Connects an observer to a given registry.
|
||||
* @tparam Matcher Types of matchers to use to initialize the observer.
|
||||
* @param reg A valid reference to a registry.
|
||||
*/
|
||||
template<typename... Matcher>
|
||||
void connect(basic_registry<entity_type> ®, basic_collector<Matcher...>) {
|
||||
disconnect();
|
||||
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/*! @brief Disconnects an observer from the registry it keeps track of. */
|
||||
void disconnect() {
|
||||
if(release) {
|
||||
release(*this);
|
||||
release.reset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in an observer.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const ENTT_NOEXCEPT {
|
||||
return storage.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether an observer is empty.
|
||||
* @return True if the observer is empty, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const ENTT_NOEXCEPT {
|
||||
return storage.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities of the observer.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size())` is
|
||||
* always a valid range, even if the container is empty.
|
||||
*
|
||||
* @note
|
||||
* Entities are in the reverse order as returned by the `begin`/`end`
|
||||
* iterators.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT {
|
||||
return storage.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the first entity of the observer. If the
|
||||
* container is empty, the returned iterator will be equal to `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity of the observer.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_type>::begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity of the observer.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity of
|
||||
* the observer. Attempting to dereference the returned iterator results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity of the
|
||||
* observer.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const ENTT_NOEXCEPT {
|
||||
return storage.basic_sparse_set<entity_type>::end();
|
||||
}
|
||||
|
||||
/*! @brief Clears the underlying container. */
|
||||
void clear() ENTT_NOEXCEPT {
|
||||
storage.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity.<br/>
|
||||
* The signature of the function must be equivalent to the following form:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them,
|
||||
* then clears the observer.
|
||||
*
|
||||
* @sa each
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) {
|
||||
std::as_const(*this).each(std::move(func));
|
||||
clear();
|
||||
}
|
||||
|
||||
private:
|
||||
delegate<void(basic_observer &)> release;
|
||||
basic_storage<entity_type, payload_type> storage;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
@@ -1,109 +1,97 @@
|
||||
#ifndef ENTT_ENTITY_ORGANIZER_HPP
|
||||
#define ENTT_ENTITY_ORGANIZER_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../container/dense_map.hpp"
|
||||
#include "../core/type_info.hpp"
|
||||
#include "../core/type_traits.hpp"
|
||||
#include "../core/utility.hpp"
|
||||
#include "../graph/adjacency_matrix.hpp"
|
||||
#include "../graph/flow.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "helper.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct is_view: std::false_type {};
|
||||
|
||||
template<typename... Args>
|
||||
struct is_view<basic_view<Args...>>: std::true_type {};
|
||||
template<typename Entity, typename... Component, typename... Exclude>
|
||||
struct is_view<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>>: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool is_view_v = is_view<Type>::value;
|
||||
|
||||
template<typename>
|
||||
struct is_group: std::false_type {};
|
||||
|
||||
template<typename... Args>
|
||||
struct is_group<basic_group<Args...>>: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool is_group_v = is_group<Type>::value;
|
||||
|
||||
template<typename Type, typename Override>
|
||||
struct unpack_type {
|
||||
using ro = std::conditional_t<
|
||||
type_list_contains_v<Override, const Type> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
|
||||
type_list_contains_v<Override, std::add_const_t<Type>> || (std::is_const_v<Type> && !type_list_contains_v<Override, std::remove_const_t<Type>>),
|
||||
type_list<std::remove_const_t<Type>>,
|
||||
type_list<>>;
|
||||
|
||||
using rw = std::conditional_t<
|
||||
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, const Type>),
|
||||
type_list_contains_v<Override, std::remove_const_t<Type>> || (!std::is_const_v<Type> && !type_list_contains_v<Override, std::add_const_t<Type>>),
|
||||
type_list<Type>,
|
||||
type_list<>>;
|
||||
};
|
||||
|
||||
template<typename... Args, typename... Override>
|
||||
struct unpack_type<basic_registry<Args...>, type_list<Override...>> {
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<basic_registry<Entity>, type_list<Override...>> {
|
||||
using ro = type_list<>;
|
||||
using rw = type_list<>;
|
||||
};
|
||||
|
||||
template<typename... Args, typename... Override>
|
||||
struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
|
||||
: unpack_type<basic_registry<Args...>, type_list<Override...>> {};
|
||||
template<typename Entity, typename... Override>
|
||||
struct unpack_type<const basic_registry<Entity>, type_list<Override...>>
|
||||
: unpack_type<basic_registry<Entity>, type_list<Override...>> {};
|
||||
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>;
|
||||
template<typename Entity, typename... Component, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<Exclude...>, typename unpack_type<Component, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Component, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
template<typename Entity, typename... Component, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_view<Entity, get_t<Component...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
|
||||
template<typename... Owned, typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro..., typename unpack_type<constness_as_t<typename Owned::element_type, Owned>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw..., typename unpack_type<constness_as_t<typename Owned::element_type, Owned>, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename... Owned, typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
|
||||
template<typename, typename, typename>
|
||||
template<typename, typename>
|
||||
struct resource_traits;
|
||||
|
||||
template<typename Registry, typename... Args, typename... Req>
|
||||
struct resource_traits<Registry, type_list<Args...>, type_list<Req...>> {
|
||||
template<typename... Args, typename... Req>
|
||||
struct resource_traits<type_list<Args...>, type_list<Req...>> {
|
||||
using args = type_list<std::remove_const_t<Args>...>;
|
||||
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
|
||||
static constexpr auto sync_point = (std::is_same_v<Args, Registry> || ...);
|
||||
};
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
|
||||
template<typename... Req, typename Ret, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
|
||||
template<typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
|
||||
template<typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<type_list<std::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const);
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Utility class for creating a static task graph.
|
||||
@@ -114,12 +102,12 @@ resource_traits<Registry, type_list<std::remove_reference_t<Args>...>, type_list
|
||||
* goal of the tool. Instead, they are returned to the user in the form of a
|
||||
* graph that allows for safe execution.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
*/
|
||||
template<typename Registry>
|
||||
template<typename Entity>
|
||||
class basic_organizer final {
|
||||
using callback_type = void(const void *, Registry &);
|
||||
using prepare_type = void(Registry &);
|
||||
using callback_type = void(const void *, basic_registry<Entity> &);
|
||||
using prepare_type = void(basic_registry<Entity> &);
|
||||
using dependency_type = std::size_t(const bool, const type_info **, const std::size_t);
|
||||
|
||||
struct vertex_data final {
|
||||
@@ -128,60 +116,119 @@ class basic_organizer final {
|
||||
const char *name{};
|
||||
const void *payload{};
|
||||
callback_type *callback{};
|
||||
dependency_type *dependency{};
|
||||
dependency_type *dependency;
|
||||
prepare_type *prepare{};
|
||||
const type_info *info{};
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] static decltype(auto) extract(Registry ®) {
|
||||
if constexpr(std::is_same_v<Type, Registry>) {
|
||||
[[nodiscard]] static decltype(auto) extract(basic_registry<Entity> ®) {
|
||||
if constexpr(std::is_same_v<Type, basic_registry<Entity>>) {
|
||||
return reg;
|
||||
} else if constexpr(internal::is_view_v<Type>) {
|
||||
return static_cast<Type>(as_view{reg});
|
||||
} else if constexpr(internal::is_group_v<Type>) {
|
||||
return static_cast<Type>(as_group{reg});
|
||||
return as_view{reg};
|
||||
} else {
|
||||
return reg.ctx().template emplace<std::remove_reference_t<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] static auto to_args(Registry ®, type_list<Args...>) {
|
||||
[[nodiscard]] static auto to_args(basic_registry<Entity> ®, type_list<Args...>) {
|
||||
return std::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
|
||||
}
|
||||
|
||||
template<typename... Type>
|
||||
[[nodiscard]] static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
|
||||
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
|
||||
if constexpr(sizeof...(Type) == 0u) {
|
||||
return {};
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
const type_info *info[]{&type_id<Type>()...};
|
||||
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
|
||||
|
||||
for(std::size_t pos{}; pos < length; ++pos) {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
buffer[pos] = info[pos];
|
||||
}
|
||||
|
||||
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
|
||||
const auto length = (std::min)(count, sizeof...(Type));
|
||||
std::copy_n(info, length, buffer);
|
||||
return length;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... RO, typename... RW>
|
||||
void track_dependencies(std::size_t index, const bool sync_point, type_list<RO...>, type_list<RW...>) {
|
||||
builder.bind(static_cast<id_type>(index));
|
||||
builder.set(type_hash<Registry>::value(), sync_point || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(builder.ro(type_hash<RO>::value()), ...);
|
||||
(builder.rw(type_hash<RW>::value()), ...);
|
||||
void track_dependencies(std::size_t index, const bool requires_registry, type_list<RO...>, type_list<RW...>) {
|
||||
dependencies[type_hash<basic_registry<Entity>>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(dependencies[type_hash<RO>::value()].emplace_back(index, false), ...);
|
||||
(dependencies[type_hash<RW>::value()].emplace_back(index, true), ...);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<bool> adjacency_matrix() {
|
||||
const auto length = vertices.size();
|
||||
std::vector<bool> edges(length * length, false);
|
||||
|
||||
// creates the adjacency matrix
|
||||
for(const auto &deps: dependencies) {
|
||||
const auto last = deps.second.cend();
|
||||
auto it = deps.second.cbegin();
|
||||
|
||||
while(it != last) {
|
||||
if(it->second) {
|
||||
// rw item
|
||||
if(auto curr = it++; it != last) {
|
||||
if(it->second) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
} else {
|
||||
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
edges[it->first * length + next->first] = true;
|
||||
}
|
||||
} else {
|
||||
for(; it != next; ++it) {
|
||||
edges[curr->first * length + it->first] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ro item, possibly only on first iteration
|
||||
if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) {
|
||||
for(; it != next; ++it) {
|
||||
edges[it->first * length + next->first] = true;
|
||||
}
|
||||
} else {
|
||||
it = last;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// computes the transitive closure
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// applies the transitive reduction
|
||||
for(std::size_t vert{}; vert < length; ++vert) {
|
||||
edges[vert * length + vert] = false;
|
||||
}
|
||||
|
||||
for(std::size_t vj{}; vj < length; ++vj) {
|
||||
for(std::size_t vi{}; vi < length; ++vi) {
|
||||
if(edges[vi * length + vj]) {
|
||||
for(std::size_t vk{}; vk < length; ++vk) {
|
||||
if(edges[vj * length + vk]) {
|
||||
edges[vi * length + vk] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
public:
|
||||
/*! Basic registry type. */
|
||||
using registry_type = Registry;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename registry_type::entity_type;
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Raw task function type. */
|
||||
@@ -191,14 +238,14 @@ public:
|
||||
struct vertex {
|
||||
/**
|
||||
* @brief Constructs a vertex of the task graph.
|
||||
* @param vtype True if the vertex is a top-level one, false otherwise.
|
||||
* @param data The data associated with the vertex.
|
||||
* @param from List of in-edges of the vertex.
|
||||
* @param to List of out-edges of the vertex.
|
||||
* @param edges The indices of the children in the adjacency list.
|
||||
*/
|
||||
vertex(vertex_data data, std::vector<std::size_t> from, std::vector<std::size_t> to)
|
||||
: node{std::move(data)},
|
||||
in{std::move(from)},
|
||||
out{std::move(to)} {}
|
||||
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
|
||||
: is_top_level{vtype},
|
||||
node{std::move(data)},
|
||||
reachable{std::move(edges)} {}
|
||||
|
||||
/**
|
||||
* @brief Fills a buffer with the type info objects for the writable
|
||||
@@ -207,7 +254,7 @@ public:
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
[[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
|
||||
return node.dependency(false, buffer, length);
|
||||
}
|
||||
|
||||
@@ -218,7 +265,7 @@ public:
|
||||
* @param length The length of the user-supplied buffer.
|
||||
* @return The number of type info objects written to the buffer.
|
||||
*/
|
||||
[[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
|
||||
size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT {
|
||||
return node.dependency(true, buffer, length);
|
||||
}
|
||||
|
||||
@@ -226,7 +273,7 @@ public:
|
||||
* @brief Returns the number of read-only resources of a vertex.
|
||||
* @return The number of read-only resources of the vertex.
|
||||
*/
|
||||
[[nodiscard]] size_type ro_count() const noexcept {
|
||||
size_type ro_count() const ENTT_NOEXCEPT {
|
||||
return node.ro_count;
|
||||
}
|
||||
|
||||
@@ -234,7 +281,7 @@ public:
|
||||
* @brief Returns the number of writable resources of a vertex.
|
||||
* @return The number of writable resources of the vertex.
|
||||
*/
|
||||
[[nodiscard]] size_type rw_count() const noexcept {
|
||||
size_type rw_count() const ENTT_NOEXCEPT {
|
||||
return node.rw_count;
|
||||
}
|
||||
|
||||
@@ -242,15 +289,15 @@ public:
|
||||
* @brief Checks if a vertex is also a top-level one.
|
||||
* @return True if the vertex is a top-level one, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool top_level() const noexcept {
|
||||
return in.empty();
|
||||
bool top_level() const ENTT_NOEXCEPT {
|
||||
return is_top_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a type info object associated with a vertex.
|
||||
* @return A properly initialized type info object.
|
||||
*/
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
const type_info &info() const ENTT_NOEXCEPT {
|
||||
return *node.info;
|
||||
}
|
||||
|
||||
@@ -258,7 +305,7 @@ public:
|
||||
* @brief Returns a user defined name associated with a vertex, if any.
|
||||
* @return The user defined name associated with the vertex, if any.
|
||||
*/
|
||||
[[nodiscard]] const char *name() const noexcept {
|
||||
const char *name() const ENTT_NOEXCEPT {
|
||||
return node.name;
|
||||
}
|
||||
|
||||
@@ -266,7 +313,7 @@ public:
|
||||
* @brief Returns the function associated with a vertex.
|
||||
* @return The function associated with the vertex.
|
||||
*/
|
||||
[[nodiscard]] function_type *callback() const noexcept {
|
||||
function_type *callback() const ENTT_NOEXCEPT {
|
||||
return node.callback;
|
||||
}
|
||||
|
||||
@@ -274,24 +321,16 @@ public:
|
||||
* @brief Returns the payload associated with a vertex, if any.
|
||||
* @return The payload associated with the vertex, if any.
|
||||
*/
|
||||
[[nodiscard]] const void *data() const noexcept {
|
||||
const void *data() const ENTT_NOEXCEPT {
|
||||
return node.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of in-edges of a vertex.
|
||||
* @return The list of in-edges of a vertex.
|
||||
* @brief Returns the list of nodes reachable from a given vertex.
|
||||
* @return The list of nodes reachable from the vertex.
|
||||
*/
|
||||
[[nodiscard]] const std::vector<std::size_t> &in_edges() const noexcept {
|
||||
return in;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the list of out-edges of a vertex.
|
||||
* @return The list of out-edges of a vertex.
|
||||
*/
|
||||
[[nodiscard]] const std::vector<std::size_t> &out_edges() const noexcept {
|
||||
return out;
|
||||
const std::vector<std::size_t> &children() const ENTT_NOEXCEPT {
|
||||
return reachable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,14 +338,14 @@ public:
|
||||
* are properly instantiated before using them.
|
||||
* @param reg A valid registry.
|
||||
*/
|
||||
void prepare(registry_type ®) const {
|
||||
void prepare(basic_registry<entity_type> ®) const {
|
||||
node.prepare ? node.prepare(reg) : void();
|
||||
}
|
||||
|
||||
private:
|
||||
bool is_top_level;
|
||||
vertex_data node;
|
||||
std::vector<std::size_t> in;
|
||||
std::vector<std::size_t> out;
|
||||
std::vector<std::size_t> reachable;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -317,9 +356,10 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Req>
|
||||
void emplace(const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::free_function_to_resource_traits<registry_type, Req...>(Candidate));
|
||||
using resource_type = decltype(internal::free_function_to_resource_traits<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
|
||||
|
||||
callback_type *callback = +[](const void *, registry_type ®) {
|
||||
callback_type *callback = +[](const void *, basic_registry<entity_type> ®) {
|
||||
std::apply(Candidate, to_args(reg, typename resource_type::args{}));
|
||||
};
|
||||
|
||||
@@ -330,10 +370,10 @@ public:
|
||||
nullptr,
|
||||
callback,
|
||||
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
|
||||
+[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
+[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
@@ -348,9 +388,10 @@ public:
|
||||
*/
|
||||
template<auto Candidate, typename... Req, typename Type>
|
||||
void emplace(Type &value_or_instance, const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::constrained_function_to_resource_traits<registry_type, Req...>(Candidate));
|
||||
using resource_type = decltype(internal::constrained_function_to_resource_traits<Req...>(Candidate));
|
||||
constexpr auto requires_registry = type_list_contains_v<typename resource_type::args, basic_registry<entity_type>>;
|
||||
|
||||
callback_type *callback = +[](const void *payload, registry_type ®) {
|
||||
callback_type *callback = +[](const void *payload, basic_registry<entity_type> ®) {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload));
|
||||
std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{})));
|
||||
};
|
||||
@@ -362,10 +403,10 @@ public:
|
||||
&value_or_instance,
|
||||
callback,
|
||||
+[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); },
|
||||
+[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
+[](basic_registry<entity_type> ®) { void(to_args(reg, typename resource_type::args{})); },
|
||||
&type_id<std::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
vertices.push_back(std::move(vdata));
|
||||
}
|
||||
|
||||
@@ -379,7 +420,7 @@ public:
|
||||
*/
|
||||
template<typename... Req>
|
||||
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
|
||||
using resource_type = internal::resource_traits<registry_type, type_list<>, type_list<Req...>>;
|
||||
using resource_type = internal::resource_traits<type_list<>, type_list<Req...>>;
|
||||
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertex_data vdata{
|
||||
@@ -399,24 +440,29 @@ public:
|
||||
* @brief Generates a task graph for the current content.
|
||||
* @return The adjacency list of the task graph.
|
||||
*/
|
||||
[[nodiscard]] std::vector<vertex> graph() const {
|
||||
std::vector<vertex> graph() {
|
||||
const auto edges = adjacency_matrix();
|
||||
|
||||
// creates the adjacency list
|
||||
std::vector<vertex> adjacency_list{};
|
||||
adjacency_list.reserve(vertices.size());
|
||||
auto adjacency_matrix = builder.graph();
|
||||
|
||||
for(auto curr: adjacency_matrix.vertices()) {
|
||||
std::vector<std::size_t> in{};
|
||||
std::vector<std::size_t> out{};
|
||||
for(std::size_t col{}, length = vertices.size(); col < length; ++col) {
|
||||
std::vector<std::size_t> reachable{};
|
||||
const auto row = col * length;
|
||||
bool is_top_level = true;
|
||||
|
||||
for(auto &&edge: adjacency_matrix.in_edges(curr)) {
|
||||
in.push_back(edge.first);
|
||||
for(std::size_t next{}; next < length; ++next) {
|
||||
if(edges[row + next]) {
|
||||
reachable.push_back(next);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
|
||||
out.push_back(edge.second);
|
||||
for(std::size_t next{}; next < length && is_top_level; ++next) {
|
||||
is_top_level = !edges[next * length + col];
|
||||
}
|
||||
|
||||
adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out));
|
||||
adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable));
|
||||
}
|
||||
|
||||
return adjacency_list;
|
||||
@@ -424,13 +470,13 @@ public:
|
||||
|
||||
/*! @brief Erases all elements from a container. */
|
||||
void clear() {
|
||||
builder.clear();
|
||||
dependencies.clear();
|
||||
vertices.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
dense_map<id_type, std::vector<std::pair<std::size_t, bool>>, identity> dependencies;
|
||||
std::vector<vertex_data> vertices;
|
||||
flow builder;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#ifndef ENTT_ENTITY_RANGES_HPP
|
||||
#define ENTT_ENTITY_RANGES_HPP
|
||||
|
||||
#if __has_include(<version>)
|
||||
# include <version>
|
||||
#
|
||||
# if defined(__cpp_lib_ranges)
|
||||
# include <ranges>
|
||||
# include "fwd.hpp"
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_view<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_group<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_view<entt::basic_view<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool std::ranges::enable_view<entt::basic_group<Args...>>{true};
|
||||
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,22 +2,27 @@
|
||||
#define ENTT_ENTITY_RUNTIME_VIEW_HPP
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "sparse_set.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Set>
|
||||
class runtime_view_iterator final {
|
||||
using iterator_type = typename Set::iterator;
|
||||
using iterator_traits = std::iterator_traits<iterator_type>;
|
||||
|
||||
[[nodiscard]] bool valid() const {
|
||||
return (!tombstone_check || *it != tombstone)
|
||||
@@ -26,19 +31,19 @@ class runtime_view_iterator final {
|
||||
}
|
||||
|
||||
public:
|
||||
using value_type = typename iterator_traits::value_type;
|
||||
using pointer = typename iterator_traits::pointer;
|
||||
using reference = typename iterator_traits::reference;
|
||||
using difference_type = typename iterator_traits::difference_type;
|
||||
using difference_type = typename iterator_type::difference_type;
|
||||
using value_type = typename iterator_type::value_type;
|
||||
using pointer = typename iterator_type::pointer;
|
||||
using reference = typename iterator_type::reference;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
constexpr runtime_view_iterator() noexcept
|
||||
runtime_view_iterator() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{},
|
||||
it{},
|
||||
tombstone_check{} {}
|
||||
|
||||
runtime_view_iterator(const std::vector<Set *> &cpools, iterator_type curr, const std::vector<Set *> &ignore) noexcept
|
||||
runtime_view_iterator(const std::vector<const Set *> &cpools, const std::vector<const Set *> &ignore, iterator_type curr) ENTT_NOEXCEPT
|
||||
: pools{&cpools},
|
||||
filter{&ignore},
|
||||
it{curr},
|
||||
@@ -49,187 +54,129 @@ public:
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator++() {
|
||||
++it;
|
||||
for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {}
|
||||
while(++it != (*pools)[0]->end() && !valid()) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
runtime_view_iterator operator++(int) {
|
||||
const runtime_view_iterator orig = *this;
|
||||
runtime_view_iterator orig = *this;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
runtime_view_iterator &operator--() {
|
||||
--it;
|
||||
for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {}
|
||||
while(--it != (*pools)[0]->begin() && !valid()) {}
|
||||
return *this;
|
||||
}
|
||||
|
||||
runtime_view_iterator operator--(int) {
|
||||
const runtime_view_iterator orig = *this;
|
||||
runtime_view_iterator orig = *this;
|
||||
return operator--(), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] pointer operator->() const noexcept {
|
||||
[[nodiscard]] pointer operator->() const ENTT_NOEXCEPT {
|
||||
return it.operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const noexcept {
|
||||
[[nodiscard]] reference operator*() const ENTT_NOEXCEPT {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept {
|
||||
[[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
|
||||
return it == other.it;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept {
|
||||
[[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_NOEXCEPT {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<Set *> *pools;
|
||||
const std::vector<Set *> *filter;
|
||||
const std::vector<const Set *> *pools;
|
||||
const std::vector<const Set *> *filter;
|
||||
iterator_type it;
|
||||
bool tombstone_check;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Runtime view implementation.
|
||||
*
|
||||
* Primary template isn't defined on purpose. All the specializations give a
|
||||
* compile-time error, but for a few reasonable cases.
|
||||
*/
|
||||
template<typename>
|
||||
struct basic_runtime_view;
|
||||
|
||||
/**
|
||||
* @brief Generic runtime view.
|
||||
*
|
||||
* Runtime views iterate over those entities that are at least in the given
|
||||
* storage. During initialization, a runtime view looks at the number of
|
||||
* entities available for each element and uses the smallest set in order to get
|
||||
* a performance boost when iterating.
|
||||
* Runtime views iterate over those entities that have at least all the given
|
||||
* components in their bags. During initialization, a runtime view looks at the
|
||||
* number of entities available for each component and picks up a reference to
|
||||
* the smallest set of candidate entities in order to get a performance boost
|
||||
* when iterate.<br/>
|
||||
* Order of elements during iterations are highly dependent on the order of the
|
||||
* underlying data structures. See sparse_set and its specializations for more
|
||||
* details.
|
||||
*
|
||||
* @b Important
|
||||
*
|
||||
* Iterators aren't invalidated if:
|
||||
*
|
||||
* * New elements are added to the storage.
|
||||
* * The entity currently pointed is modified (for example, elements are added
|
||||
* or removed from it).
|
||||
* * New instances of the given components are created and assigned to entities.
|
||||
* * The entity currently pointed is modified (as an example, if one of the
|
||||
* given components is removed from the entity to which the iterator points).
|
||||
* * The entity currently pointed is destroyed.
|
||||
*
|
||||
* In all other cases, modifying the storage iterated by the view in any way
|
||||
* invalidates all the iterators.
|
||||
* In all the other cases, modifying the pools of the given components in any
|
||||
* way invalidates all the iterators and using them results in undefined
|
||||
* behavior.
|
||||
*
|
||||
* @tparam Type Common base type.
|
||||
* @note
|
||||
* Views share references to the underlying data structures of the registry that
|
||||
* generated them. Therefore any change to the entities and to the components
|
||||
* made by means of the registry are immediately reflected by the views, unless
|
||||
* a pool was missing when the view was built (in this case, the view won't
|
||||
* have a valid reference and won't be updated accordingly).
|
||||
*
|
||||
* @warning
|
||||
* Lifetime of a view must not overcome that of the registry that generated it.
|
||||
* In any other case, attempting to use a view results in undefined behavior.
|
||||
*
|
||||
* @tparam Entity A valid entity type (see entt_traits for more details).
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Allocator>
|
||||
class basic_runtime_view {
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
|
||||
using container_type = std::vector<Type *, Allocator>;
|
||||
|
||||
[[nodiscard]] auto offset() const noexcept {
|
||||
ENTT_ASSERT(!pools.empty(), "Invalid view");
|
||||
const auto &leading = *pools.front();
|
||||
return (leading.policy() == deletion_policy::swap_only) ? leading.free_list() : leading.size();
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
template<typename Entity, typename Allocator>
|
||||
struct basic_runtime_view<basic_sparse_set<Entity, Allocator>> {
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename Type::entity_type;
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Signed integer type. */
|
||||
using difference_type = std::ptrdiff_t;
|
||||
/*! @brief Common type among all storage types. */
|
||||
using common_type = Type;
|
||||
using base_type = basic_sparse_set<Entity, Allocator>;
|
||||
/*! @brief Bidirectional iterator type. */
|
||||
using iterator = internal::runtime_view_iterator<common_type>;
|
||||
using iterator = internal::runtime_view_iterator<base_type>;
|
||||
|
||||
/*! @brief Default constructor to use to create empty, invalid views. */
|
||||
basic_runtime_view() noexcept
|
||||
: basic_runtime_view{allocator_type{}} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty, invalid view with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_runtime_view(const allocator_type &allocator)
|
||||
: pools{allocator},
|
||||
filter{allocator} {}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
basic_runtime_view(const basic_runtime_view &) = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended copy constructor.
|
||||
* @param other The instance to copy from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator)
|
||||
: pools{other.pools, allocator},
|
||||
filter{other.filter, allocator} {}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
basic_runtime_view(basic_runtime_view &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator)
|
||||
: pools{std::move(other.pools), allocator},
|
||||
filter{std::move(other.filter), allocator} {}
|
||||
|
||||
/*! @brief Default destructor. */
|
||||
~basic_runtime_view() = default;
|
||||
|
||||
/**
|
||||
* @brief Default copy assignment operator.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &operator=(const basic_runtime_view &) = default;
|
||||
|
||||
/**
|
||||
* @brief Default move assignment operator.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &operator=(basic_runtime_view &&) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given view.
|
||||
* @param other View to exchange the content with.
|
||||
*/
|
||||
void swap(basic_runtime_view &other) noexcept {
|
||||
using std::swap;
|
||||
swap(pools, other.pools);
|
||||
swap(filter, other.filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return pools.get_allocator();
|
||||
}
|
||||
|
||||
/*! @brief Clears the view. */
|
||||
void clear() {
|
||||
pools.clear();
|
||||
filter.clear();
|
||||
}
|
||||
basic_runtime_view() ENTT_NOEXCEPT
|
||||
: pools{},
|
||||
filter{} {}
|
||||
|
||||
/**
|
||||
* @brief Appends an opaque storage object to a runtime view.
|
||||
* @param base An opaque reference to a storage object.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &iterate(common_type &base) {
|
||||
if(pools.empty() || !(base.size() < pools.front()->size())) {
|
||||
basic_runtime_view &iterate(const base_type &base) {
|
||||
if(pools.empty() || !(base.size() < pools[0u]->size())) {
|
||||
pools.push_back(&base);
|
||||
} else {
|
||||
pools.push_back(std::exchange(pools.front(), &base));
|
||||
pools.push_back(std::exchange(pools[0u], &base));
|
||||
}
|
||||
|
||||
return *this;
|
||||
@@ -240,7 +187,7 @@ public:
|
||||
* @param base An opaque reference to a storage object.
|
||||
* @return This runtime view.
|
||||
*/
|
||||
basic_runtime_view &exclude(common_type &base) {
|
||||
basic_runtime_view &exclude(const base_type &base) {
|
||||
filter.push_back(&base);
|
||||
return *this;
|
||||
}
|
||||
@@ -250,37 +197,36 @@ public:
|
||||
* @return Estimated number of entities iterated by the view.
|
||||
*/
|
||||
[[nodiscard]] size_type size_hint() const {
|
||||
return pools.empty() ? size_type{} : offset();
|
||||
return pools.empty() ? size_type{} : pools.front()->size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator to the first entity that has the given
|
||||
* elements.
|
||||
* components.
|
||||
*
|
||||
* If the view is empty, the returned iterator will be equal to `end()`.
|
||||
* The returned iterator points to the first entity that has the given
|
||||
* components. If the view is empty, the returned iterator will be equal to
|
||||
* `end()`.
|
||||
*
|
||||
* @return An iterator to the first entity that has the given elements.
|
||||
* @return An iterator to the first entity that has the given components.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const {
|
||||
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end() - static_cast<difference_type>(offset()), filter};
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns an iterator that is past the last entity that has the
|
||||
* given elements.
|
||||
* given components.
|
||||
*
|
||||
* The returned iterator points to the entity following the last entity that
|
||||
* has the given components. Attempting to dereference the returned iterator
|
||||
* results in undefined behavior.
|
||||
*
|
||||
* @return An iterator to the entity following the last entity that has the
|
||||
* given elements.
|
||||
* given components.
|
||||
*/
|
||||
[[nodiscard]] iterator end() const {
|
||||
return pools.empty() ? iterator{} : iterator{pools, pools.front()->end(), filter};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a view is initialized or not.
|
||||
* @return True if the view is initialized, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] explicit operator bool() const noexcept {
|
||||
return !(pools.empty() && filter.empty());
|
||||
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -291,15 +237,15 @@ public:
|
||||
[[nodiscard]] bool contains(const entity_type entt) const {
|
||||
return !pools.empty()
|
||||
&& std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); })
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); })
|
||||
&& pools.front()->index(entt) < offset();
|
||||
&& std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); });
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates entities and applies the given function object to them.
|
||||
*
|
||||
* The function object is invoked for each entity. It is provided only with
|
||||
* the entity itself.<br/>
|
||||
* the entity itself. To get the components, users can use the registry with
|
||||
* which the view was built.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
@@ -317,8 +263,8 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
container_type pools;
|
||||
container_type filter;
|
||||
std::vector<const base_type *> pools;
|
||||
std::vector<const base_type *> filter;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
177
src/entt/entity/sigh_storage_mixin.hpp
Normal file
177
src/entt/entity/sigh_storage_mixin.hpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
|
||||
#define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP
|
||||
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/any.hpp"
|
||||
#include "../signal/sigh.hpp"
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Mixin type used to add signal support to storage types.
|
||||
*
|
||||
* The function type of a listener is equivalent to:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(basic_registry<entity_type> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* This applies to all signals made available.
|
||||
*
|
||||
* @tparam Type The type of the underlying storage.
|
||||
*/
|
||||
template<typename Type>
|
||||
class sigh_storage_mixin final: public Type {
|
||||
using basic_iterator = typename Type::basic_iterator;
|
||||
|
||||
template<typename Func>
|
||||
void notify_destruction(basic_iterator first, basic_iterator last, Func func) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
|
||||
for(; first != last; ++first) {
|
||||
const auto entt = *first;
|
||||
destruction.publish(*owner, entt);
|
||||
const auto it = Type::find(entt);
|
||||
func(it, it + 1u);
|
||||
}
|
||||
}
|
||||
|
||||
void swap_and_pop(basic_iterator first, basic_iterator last) final {
|
||||
notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); });
|
||||
}
|
||||
|
||||
void in_place_pop(basic_iterator first, basic_iterator last) final {
|
||||
notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); });
|
||||
}
|
||||
|
||||
basic_iterator try_emplace(const typename Type::entity_type entt, const bool force_back, const void *value) final {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::try_emplace(entt, force_back, value);
|
||||
construction.publish(*owner, entt);
|
||||
return Type::find(entt);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename Type::entity_type;
|
||||
|
||||
/*! @brief Inherited constructors. */
|
||||
using Type::Type;
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever a new instance is created and assigned to an entity.<br/>
|
||||
* Listeners are invoked after the object has been assigned to the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_construct() ENTT_NOEXCEPT {
|
||||
return sink{construction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is explicitly updated.<br/>
|
||||
* Listeners are invoked after the object has been updated.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_update() ENTT_NOEXCEPT {
|
||||
return sink{update};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a sink object.
|
||||
*
|
||||
* The sink returned by this function can be used to receive notifications
|
||||
* whenever an instance is removed from an entity and thus destroyed.<br/>
|
||||
* Listeners are invoked before the object has been removed from the entity.
|
||||
*
|
||||
* @sa sink
|
||||
*
|
||||
* @return A temporary sink object.
|
||||
*/
|
||||
[[nodiscard]] auto on_destroy() ENTT_NOEXCEPT {
|
||||
return sink{destruction};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param entt A valid identifier.
|
||||
* @param args Parameters to use to initialize the object.
|
||||
* @return A reference to the newly created object.
|
||||
*/
|
||||
template<typename... Args>
|
||||
decltype(auto) emplace(const entity_type entt, Args &&...args) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::emplace(entt, std::forward<Args>(args)...);
|
||||
construction.publish(*owner, entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Patches the given instance for an entity.
|
||||
* @tparam Func Types of the function objects to invoke.
|
||||
* @param entt A valid identifier.
|
||||
* @param func Valid function objects.
|
||||
* @return A reference to the patched instance.
|
||||
*/
|
||||
template<typename... Func>
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::patch(entt, std::forward<Func>(func)...);
|
||||
update.publish(*owner, entt);
|
||||
return this->get(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns entities to a storage.
|
||||
* @tparam It Type of input iterator.
|
||||
* @tparam Args Types of arguments to use to construct the objects assigned
|
||||
* to the entities.
|
||||
* @param first An iterator to the first element of the range of entities.
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
* @param args Parameters to use to initialize the objects assigned to the
|
||||
* entities.
|
||||
*/
|
||||
template<typename It, typename... Args>
|
||||
void insert(It first, It last, Args &&...args) {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
Type::insert(first, last, std::forward<Args>(args)...);
|
||||
|
||||
for(auto it = construction.empty() ? last : first; it != last; ++it) {
|
||||
construction.publish(*owner, *it);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Forwards variables to mixins, if any.
|
||||
* @param value A variable wrapped in an opaque container.
|
||||
*/
|
||||
void bind(any value) ENTT_NOEXCEPT final {
|
||||
auto *reg = any_cast<basic_registry<entity_type>>(&value);
|
||||
owner = reg ? reg : owner;
|
||||
Type::bind(std::move(value));
|
||||
}
|
||||
|
||||
private:
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> construction{};
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> destruction{};
|
||||
sigh<void(basic_registry<entity_type> &, const entity_type)> update{};
|
||||
basic_registry<entity_type> *owner{};
|
||||
};
|
||||
|
||||
} // 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