Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
344e03ac64 | ||
|
|
da56665b03 | ||
|
|
f6f01ef1bc | ||
|
|
0ed514628c | ||
|
|
a41421d867 | ||
|
|
c1f6b11f7d | ||
|
|
b2233064a0 |
@@ -1 +0,0 @@
|
||||
test
|
||||
@@ -1 +0,0 @@
|
||||
USE_BAZEL_VERSION=6.x
|
||||
17
.bazelrc
17
.bazelrc
@@ -1,17 +0,0 @@
|
||||
common --enable_bzlmod
|
||||
build --enable_platform_specific_config
|
||||
build --incompatible_use_platforms_repo_for_constraints
|
||||
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
|
||||
26
.clang-tidy
26
.clang-tidy
@@ -1,26 +0,0 @@
|
||||
Checks: >
|
||||
bugprone-*,
|
||||
concurrency-*,
|
||||
cppcoreguidelines-*,
|
||||
misc-*,
|
||||
-misc-no-recursion,
|
||||
modernize-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
performance-*,
|
||||
portability-*,
|
||||
readibility-*
|
||||
CheckOptions:
|
||||
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
|
||||
value: true
|
||||
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
|
||||
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: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
|
||||
value: true
|
||||
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
|
||||
value: true
|
||||
6
.github/workflows/analyzer.yml
vendored
6
.github/workflows/analyzer.yml
vendored
@@ -12,14 +12,14 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
|
||||
env:
|
||||
IWYU: "0.20"
|
||||
LLVM: "16"
|
||||
IWYU: 0.19
|
||||
LLVM: 15
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install llvm/clang
|
||||
# see: https://apt.llvm.org/
|
||||
run: |
|
||||
|
||||
20
.github/workflows/bazel-release-archive.yml
vendored
20
.github/workflows/bazel-release-archive.yml
vendored
@@ -1,20 +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/checkout@v4
|
||||
- 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 }}
|
||||
20
.github/workflows/bazel.yml
vendored
20
.github/workflows/bazel.yml
vendored
@@ -1,20 +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
|
||||
16
.github/workflows/build.yml
vendored
16
.github/workflows/build.yml
vendored
@@ -55,7 +55,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install compiler
|
||||
run: |
|
||||
sudo apt update
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
windows:
|
||||
timeout-minutes: 15
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -100,14 +100,14 @@ 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@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -117,7 +117,7 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
extra:
|
||||
timeout-minutes: 15
|
||||
@@ -131,7 +131,7 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
run: |
|
||||
@@ -141,4 +141,4 @@ jobs:
|
||||
working-directory: build
|
||||
env:
|
||||
CTEST_OUTPUT_ON_FAILURE: 1
|
||||
run: ctest -C Debug -j4
|
||||
run: ctest --timeout 30 -C Debug -j4
|
||||
|
||||
4
.github/workflows/coverage.yml
vendored
4
.github/workflows/coverage.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v3
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
@@ -22,7 +22,7 @@ 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: |
|
||||
|
||||
6
.github/workflows/sanitizer.yml
vendored
6
.github/workflows/sanitizer.yml
vendored
@@ -16,9 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
# temporary workaround for https://github.com/actions/runner-images/issues/8659
|
||||
- uses: mjp41/workaround8649@c8550b715ccdc17f89c8d5c28d7a48eeff9c94a8
|
||||
- uses: actions/checkout@v3
|
||||
- name: Compile tests
|
||||
working-directory: build
|
||||
env:
|
||||
@@ -30,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
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -11,5 +11,3 @@ cpp.hint
|
||||
|
||||
# Bazel
|
||||
/bazel-*
|
||||
/test/bazel-*
|
||||
/user.bazelrc
|
||||
|
||||
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,
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -1,15 +1,21 @@
|
||||
#
|
||||
# EnTT
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.15.7)
|
||||
|
||||
#
|
||||
# Read project version
|
||||
#
|
||||
|
||||
set(ENTT_VERSION_REGEX "#define ENTT_VERSION_.*[ \t]+(.+)")
|
||||
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/src/entt/config/version.h" ENTT_VERSION REGEX ${ENTT_VERSION_REGEX})
|
||||
list(TRANSFORM ENTT_VERSION REPLACE ${ENTT_VERSION_REGEX} "\\1")
|
||||
string(JOIN "." ENTT_VERSION ${ENTT_VERSION})
|
||||
|
||||
#
|
||||
# Project configuration
|
||||
#
|
||||
|
||||
project(
|
||||
EnTT
|
||||
@@ -28,15 +34,18 @@ message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
|
||||
message(VERBOSE "* Copyright (c) 2017-2023 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 +65,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 +76,13 @@ 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)
|
||||
@@ -213,15 +216,13 @@ 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(CMAKE_CXX_CLANG_TIDY "${ENTT_CLANG_TIDY_EXECUTABLE};--config-file=${EnTT_SOURCE_DIR}/.clang-tidy;--header-filter=${EnTT_SOURCE_DIR}/src/entt/.*;--extra-arg=/EHsc")
|
||||
endif()
|
||||
|
||||
if(ENTT_HAS_LIBCPP)
|
||||
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Install pkg-config file
|
||||
#
|
||||
|
||||
include(JoinPaths)
|
||||
|
||||
@@ -240,7 +241,9 @@ install(
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
)
|
||||
|
||||
#
|
||||
# Install EnTT
|
||||
#
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
@@ -286,7 +289,9 @@ install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
export(PACKAGE EnTT)
|
||||
|
||||
#
|
||||
# Tests
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
|
||||
|
||||
@@ -305,15 +310,9 @@ if(ENTT_BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
# Tools
|
||||
|
||||
option(ENTT_BUILD_TOOLS "Enable building tools." OFF)
|
||||
|
||||
if(ENTT_BUILD_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
#
|
||||
# Documentation
|
||||
#
|
||||
|
||||
option(ENTT_BUILD_DOCS "Enable building with documentation." OFF)
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
module(
|
||||
name = "entt",
|
||||
version = "3.12.2",
|
||||
compatibility_level = 3012,
|
||||
)
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.0.8")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.4.2")
|
||||
52
README.md
52
README.md
@@ -6,11 +6,11 @@
|
||||
[](https://github.com/skypjack/entt/actions)
|
||||
[](https://codecov.io/gh/skypjack/entt)
|
||||
[](https://godbolt.org/z/zxW73f)
|
||||
[](https://skypjack.github.io/entt/)
|
||||
[](https://vcpkg.link/ports/entt)
|
||||
[](https://conan.io/center/recipes/entt)
|
||||
[](https://skypjack.github.io/entt/)
|
||||
[](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
|
||||
@@ -39,8 +39,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:
|
||||
|
||||
@@ -52,7 +51,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)
|
||||
@@ -176,28 +175,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
|
||||
|
||||
@@ -319,18 +327,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
|
||||
|
||||
27
TODO
27
TODO
@@ -4,28 +4,23 @@ EXAMPLES
|
||||
|
||||
DOC:
|
||||
* custom storage/view
|
||||
* examples (and credits) from @alanjfs :)
|
||||
* update entity doc when the storage based model is in place
|
||||
* in-place O(1) release/destroy for non-orphaned entities, out-of-sync model
|
||||
* view: single vs multi type views are no longer a thing actually
|
||||
* bump entities, reserved bits on identifiers
|
||||
|
||||
TODO:
|
||||
TODO (high prio):
|
||||
* check natvis files (periodically :)
|
||||
* resource cache: avoid using shared ptr with loader and the others
|
||||
* further optimize exclusion lists in multi type views (no existence check)
|
||||
* further improve meta resolve function by id (bimap)
|
||||
* further improve the snapshot stuff, ie component functions
|
||||
* use fixture for storage tests to reduce loc number and duplication as much as possible
|
||||
* basic_view<...>::reach(...)
|
||||
* doc: bump entities
|
||||
|
||||
WIP:
|
||||
* get rid of observers, storage based views made them pointless - document alternatives
|
||||
* exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details)
|
||||
* process scheduler: reviews, use free lists internally
|
||||
* deprecate non-owning groups in favor of owning views and view packs, introduce lazy owning views
|
||||
* bring nested groups back in place (see bd34e7f)
|
||||
* work stealing job system (see #100) + mt scheduler based on const awareness for types
|
||||
* view: reduce inst due to/improve perf with index-based approach in dispatch_get/pick_and_each/each (single type too, define storage ::at and ::at_as_tuple)
|
||||
* view: update natvis as needed after the last rework, merge pools/filter in the same array, drop check (?) and turn view into a position
|
||||
* view: type-only view_iterator (dyn get/excl sizes), type-only basic_common_view (dyn get/excl sizes with pointer to array from derived)
|
||||
* combine version-mask-vs-version-bits tricks with reserved bits to allow things like enabling/disabling
|
||||
* basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) uses moved-from packed in assert (same for storage)
|
||||
* maybe drop begin(v)/end(v) from sparse set and only use scoped begin/end at call sites
|
||||
* view unchecked_refresh loop is never executed if Get is 1u
|
||||
* review all // NOLINT + linter workflow + review clang-tidy file
|
||||
* self contained entity traits to avoid explicit specializations (ie enum constants)
|
||||
* review assure conflicts check, hash doesn't fit the purpose maybe
|
||||
* allow to zero-sized versions (with non-regression tests)
|
||||
* auto type info data from types if present
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
#
|
||||
# Doxygen configuration (documentation)
|
||||
|
||||
include(FetchContent)
|
||||
#
|
||||
|
||||
FetchContent_Declare(
|
||||
doxygen-awesome-css
|
||||
|
||||
129
docs/doxy.in
129
docs/doxy.in
@@ -1,4 +1,4 @@
|
||||
# Doxyfile 1.9.7
|
||||
# Doxyfile 1.9.6
|
||||
|
||||
# This file describes the settings to be used by the documentation system
|
||||
# doxygen (www.doxygen.org) for a project.
|
||||
@@ -353,17 +353,6 @@ MARKDOWN_SUPPORT = YES
|
||||
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
|
||||
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
|
||||
# generate identifiers for the Markdown headings. Note: Every identifier is
|
||||
# unique.
|
||||
# Possible values are: DOXYGEN Use a fixed 'autotoc_md' string followed by a
|
||||
# sequence number starting at 0. and GITHUB Use the lower case version of title
|
||||
# with any whitespace replaced by '-' and punctations characters removed..
|
||||
# The default value is: DOXYGEN.
|
||||
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
|
||||
|
||||
MARKDOWN_ID_STYLE = DOXYGEN
|
||||
|
||||
# When enabled doxygen tries to link words that correspond to documented
|
||||
# classes, or namespaces to their corresponding documentation. Such a link can
|
||||
# be prevented in individual cases by putting a % sign in front of the word or
|
||||
@@ -488,14 +477,6 @@ LOOKUP_CACHE_SIZE = 0
|
||||
|
||||
NUM_PROC_THREADS = 1
|
||||
|
||||
# If the TIMESTAMP tag is set different from NO then each generated page will
|
||||
# contain the date or date and time when the page was generated. Setting this to
|
||||
# NO can help when comparing the output of multiple runs.
|
||||
# Possible values are: YES, NO, DATETIME and DATE.
|
||||
# The default value is: NO.
|
||||
|
||||
TIMESTAMP = NO
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
@@ -881,14 +862,7 @@ WARN_IF_UNDOC_ENUM_VAL = NO
|
||||
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
|
||||
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
|
||||
# at the end of the doxygen process doxygen will return with a non-zero status.
|
||||
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
|
||||
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
|
||||
# write the warning messages in between other messages but write them at the end
|
||||
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
|
||||
# besides being in the defined file also be shown at the end of a run, unless
|
||||
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
|
||||
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
|
||||
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
|
||||
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
|
||||
# The default value is: NO.
|
||||
|
||||
WARN_AS_ERROR = NO
|
||||
@@ -1016,6 +990,9 @@ EXCLUDE_PATTERNS =
|
||||
# output. The symbol name can be a fully qualified name, a word, or if the
|
||||
# wildcard * is used, a substring. Examples: ANamespace, AClass,
|
||||
# ANamespace::AClass, ANamespace::*Test
|
||||
#
|
||||
# Note that the wildcards are matched against the file with absolute path, so to
|
||||
# exclude all test directories use the pattern */test/*
|
||||
|
||||
EXCLUDE_SYMBOLS =
|
||||
|
||||
@@ -1398,6 +1375,15 @@ HTML_COLORSTYLE_SAT = 100
|
||||
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
|
||||
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to YES can help to show when doxygen was last run and thus if the
|
||||
# documentation is up to date.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
HTML_TIMESTAMP = NO
|
||||
|
||||
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
|
||||
# documentation will contain a main index with vertical navigation menus that
|
||||
# are dynamically created via JavaScript. If disabled, the navigation index will
|
||||
@@ -1547,16 +1533,6 @@ BINARY_TOC = NO
|
||||
|
||||
TOC_EXPAND = NO
|
||||
|
||||
# The SITEMAP_URL tag is used to specify the full URL of the place where the
|
||||
# generated documentation will be placed on the server by the user during the
|
||||
# deployment of the documentation. The generated sitemap is called sitemap.xml
|
||||
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
|
||||
# is specified no sitemap is generated. For information about the sitemap
|
||||
# protocol see https://www.sitemaps.org
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
SITEMAP_URL =
|
||||
|
||||
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
|
||||
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
|
||||
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
|
||||
@@ -2045,16 +2021,9 @@ PDF_HYPERLINKS = YES
|
||||
|
||||
USE_PDFLATEX = YES
|
||||
|
||||
# The LATEX_BATCHMODE tag ignals the behavior of LaTeX in case of an error.
|
||||
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
|
||||
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
|
||||
# hit at every error; missing files that TeX tries to input or request from
|
||||
# keyboard input (\read on a not open input stream) cause the job to abort,
|
||||
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
|
||||
# but there is no possibility of user interaction just like in batch mode,
|
||||
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
|
||||
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
|
||||
# each error, asking for user intervention.
|
||||
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
|
||||
# command to the generated LaTeX files. This will instruct LaTeX to keep running
|
||||
# if errors occur, instead of asking the user for help.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
@@ -2075,6 +2044,14 @@ LATEX_HIDE_INDICES = NO
|
||||
|
||||
LATEX_BIB_STYLE = plain
|
||||
|
||||
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
|
||||
# page will contain the date and time when the page was generated. Setting this
|
||||
# to NO can help when comparing the output of multiple runs.
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_TIMESTAMP = NO
|
||||
|
||||
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# path from which the emoji images will be read. If a relative path is entered,
|
||||
# it will be relative to the LATEX_OUTPUT directory. If left blank the
|
||||
@@ -2240,7 +2217,7 @@ DOCBOOK_OUTPUT = docbook
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
|
||||
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
|
||||
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
|
||||
# the structure of the code including all documentation. Note that this feature
|
||||
# is still experimental and incomplete at the moment.
|
||||
# The default value is: NO.
|
||||
@@ -2411,9 +2388,16 @@ EXTERNAL_GROUPS = YES
|
||||
EXTERNAL_PAGES = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to diagram generator tools
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
|
||||
DIA_PATH =
|
||||
|
||||
# If set to YES the inheritance and collaboration graphs will hide inheritance
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
# The default value is: YES.
|
||||
@@ -2422,7 +2406,7 @@ HIDE_UNDOC_RELATIONS = YES
|
||||
|
||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||
# available from the path. This tool is part of Graphviz (see:
|
||||
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
|
||||
# Bell Labs. The other options in this section have no effect if this option is
|
||||
# set to NO
|
||||
# The default value is: NO.
|
||||
@@ -2475,15 +2459,13 @@ DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
|
||||
|
||||
DOT_FONTPATH =
|
||||
|
||||
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
|
||||
# generate a graph for each documented class showing the direct and indirect
|
||||
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
|
||||
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
|
||||
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
|
||||
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
|
||||
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
|
||||
# relations will be shown as texts / links.
|
||||
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
|
||||
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
|
||||
# graph for each documented class showing the direct and indirect inheritance
|
||||
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
|
||||
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
|
||||
# to TEXT the direct and indirect inheritance relations will be shown as texts /
|
||||
# links.
|
||||
# Possible values are: NO, YES, TEXT and GRAPH.
|
||||
# The default value is: YES.
|
||||
|
||||
CLASS_GRAPH = YES
|
||||
@@ -2624,7 +2606,7 @@ DIR_GRAPH_MAX_DEPTH = 1
|
||||
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
|
||||
# generated by dot. For an explanation of the image formats see the section
|
||||
# output formats in the documentation of the dot tool (Graphviz (see:
|
||||
# https://www.graphviz.org/)).
|
||||
# http://www.graphviz.org/)).
|
||||
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
|
||||
# to make the SVG files visible in IE 9+ (other browsers do not have this
|
||||
# requirement).
|
||||
@@ -2661,12 +2643,11 @@ DOT_PATH =
|
||||
|
||||
DOTFILE_DIRS =
|
||||
|
||||
# You can include diagrams made with dia in doxygen documentation. Doxygen will
|
||||
# then run dia to produce the diagram and insert it in the documentation. The
|
||||
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
|
||||
# If left empty dia is assumed to be found in the default search path.
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
DIA_PATH =
|
||||
MSCFILE_DIRS =
|
||||
|
||||
# The DIAFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain dia files that are included in the documentation (see the \diafile
|
||||
@@ -2743,19 +2724,3 @@ GENERATE_LEGEND = YES
|
||||
# The default value is: YES.
|
||||
|
||||
DOT_CLEANUP = YES
|
||||
|
||||
# You can define message sequence charts within doxygen comments using the \msc
|
||||
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
|
||||
# use a built-in version of mscgen tool to produce the charts. Alternatively,
|
||||
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
|
||||
# specifying prog as the value, doxygen will call the tool as prog -T
|
||||
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
|
||||
# output file formats "png", "eps", "svg", and "ismap".
|
||||
|
||||
MSCGEN_TOOL =
|
||||
|
||||
# The MSCFILE_DIRS tag can be used to specify one or more directories that
|
||||
# contain msc files that are included in the documentation (see the \mscfile
|
||||
# command).
|
||||
|
||||
MSCFILE_DIRS =
|
||||
|
||||
@@ -63,5 +63,4 @@ 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`).
|
||||
Therefore, there is no need to go into the API description.
|
||||
|
||||
@@ -773,7 +773,7 @@ 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)>;
|
||||
using type = entt::nth_argument_t<1u, &clazz::member>;
|
||||
```
|
||||
|
||||
Disambiguation of overloaded functions is the responsibility of the user, should
|
||||
|
||||
@@ -583,7 +583,7 @@ There are two functions that respond to slightly different needs:
|
||||
* Components are sorted either directly:
|
||||
|
||||
```cpp
|
||||
registry.sort<renderable>([](const renderable &lhs, const renderable &rhs) {
|
||||
registry.sort<renderable>([](const auto &lhs, const auto &rhs) {
|
||||
return lhs.z < rhs.z;
|
||||
});
|
||||
```
|
||||
@@ -1372,7 +1372,7 @@ Finally, when the user asks the registry for an iterable object to visit all the
|
||||
storage elements inside it as follows:
|
||||
|
||||
```cpp
|
||||
for(auto [id, storage]: registry.storage()) {
|
||||
for(auto [id, storage]: registry.each()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -1549,7 +1549,7 @@ own API for them. However, there is still no limit to the possibilities of use:
|
||||
auto &&other = registry.storage<velocity>("other"_hs);
|
||||
|
||||
registry.emplace<velocity>(entity);
|
||||
other.push(entity);
|
||||
storage.push(entity);
|
||||
```
|
||||
|
||||
Anything that can be done via the registry interface can also be done directly
|
||||
@@ -1817,10 +1817,7 @@ However, it's possible to _enforce_ iteration of a view by given component order
|
||||
by means of the `use` function:
|
||||
|
||||
```cpp
|
||||
auto view = registry.view<position, velocity>();
|
||||
view.use<position>();
|
||||
|
||||
for(auto entity: view) {
|
||||
for(auto entity : registry.view<position, velocity>().use<position>()) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
@@ -2108,27 +2105,28 @@ The same concepts apply to groups as well.
|
||||
Views and groups are narrow windows on the entire list of entities. They work by
|
||||
filtering entities according to their components.<br/>
|
||||
In some cases there may be the need to iterate all the entities still in use
|
||||
regardless of their components. This is done by accessing entity storage:
|
||||
regardless of their components. The registry offers a specific member function
|
||||
to do that:
|
||||
|
||||
```cpp
|
||||
for(auto entity: registry.view<entt::entity>()) {
|
||||
registry.each([](auto entity) {
|
||||
// ...
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
As a rule of thumb, consider using a view or a group if the goal is to iterate
|
||||
entities that have a determinate set of components. These tools are usually much
|
||||
faster than filtering entities with a bunch of custom tests.<br/>
|
||||
faster than combining the `each` function with a bunch of custom tests.<br/>
|
||||
In all the other cases, this is the way to go. For example, it's possible to
|
||||
combine this view with the `orphan` member function to clean up orphan entities
|
||||
combine `each` with the `orphan` member function to clean up orphan entities
|
||||
(that is, entities that are still in use and have no assigned components):
|
||||
|
||||
```cpp
|
||||
for(auto entity: registry.view<entt::entity>()) {
|
||||
registry.each([®istry](auto entity) {
|
||||
if(registry.orphan(entity)) {
|
||||
registry.release(entity);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
In general, iterating all entities can result in poor performance. It should not
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
* [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
|
||||
-->
|
||||
@@ -214,28 +213,3 @@ 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's 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.
|
||||
|
||||
@@ -39,9 +39,6 @@ I hope the following lists can grow much more in the future.
|
||||
* [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.
|
||||
@@ -127,16 +124,6 @@ I hope the following lists can grow much more in the future.
|
||||
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`.
|
||||
|
||||
## Engines and the like:
|
||||
|
||||
@@ -213,14 +200,7 @@ I hope the following lists can grow much more in the future.
|
||||
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.
|
||||
engine entirely implemented as an entity-component-ystem.
|
||||
|
||||
## Articles, videos and blog posts:
|
||||
|
||||
@@ -247,17 +227,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.
|
||||
|
||||
@@ -385,10 +385,6 @@ to case. In particular:
|
||||
true in case of success.<br/>
|
||||
For example, it's not possible to clear fixed size containers.
|
||||
|
||||
* The `reserve` member function allows to increase the capacity of the wrapped
|
||||
container and returns true in case of success.<br/>
|
||||
For example, it's not possible to increase capacity of fixed size containers.
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that is used to
|
||||
iterate the container directly:
|
||||
|
||||
@@ -476,10 +472,6 @@ differences in behavior in the case of key-only containers. In particular:
|
||||
* The `clear` member function allows to clear the wrapped container and returns
|
||||
true in case of success.
|
||||
|
||||
* The `reserve` member function allows to increase the capacity of the wrapped
|
||||
container and returns true in case of success.<br/>
|
||||
For example, it's not possible to increase capacity of standard maps.
|
||||
|
||||
* The `begin` and `end` member functions return opaque iterators that are used
|
||||
to iterate the container directly:
|
||||
|
||||
|
||||
@@ -177,8 +177,8 @@ To attach a process to a scheduler there are mainly two ways:
|
||||
scheduler.attach([](auto...){ /* ... */ });
|
||||
```
|
||||
|
||||
In both cases, the scheduler is returned and its `then` member function can be
|
||||
used to create chains of processes to run sequentially.<br/>
|
||||
In both cases, the return value is an opaque object that offers a `then` member
|
||||
function used to create chains of processes to run sequentially.<br/>
|
||||
As a minimal example of use:
|
||||
|
||||
```cpp
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
## 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
|
||||
|
||||
68
entt.imp
68
entt.imp
@@ -1,42 +1,34 @@
|
||||
[
|
||||
# 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": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.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/observer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.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" ] }
|
||||
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_map.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/container/fwd.hpp[\">]", "private", "<entt/container/dense_set.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/core/fwd.hpp[\">]", "private", "<entt/core/any.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/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/observer.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/entity/fwd.hpp[\">]", "private", "<entt/entity/organizer.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": [ "@[\"<].*/meta/fwd.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
|
||||
{ "include": [ "@[\"<].*/poly/fwd.hpp[\">]", "private", "<entt/poly/poly.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" ] }
|
||||
]
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
|
||||
<Type Name="entt::basic_registry<*>">
|
||||
<Intrinsic Name="to_entity" Expression="*((traits_type::entity_type *)&entity) & traits_type::entity_mask">
|
||||
<Parameter Name="entity" Type="traits_type::value_type &"/>
|
||||
</Intrinsic>
|
||||
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[entities]">entities</Item>
|
||||
@@ -30,12 +33,10 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_sparse_set<*>">
|
||||
<Intrinsic Name="cap" Expression="(traits_type::version_mask << traits_type::length)"/>
|
||||
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
|
||||
<Item Name="[policy]">mode,en</Item>
|
||||
<Item Name="[free_list]">head</Item>
|
||||
<Synthetic Name="[sparse]">
|
||||
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
|
||||
<Expand>
|
||||
@@ -49,7 +50,7 @@
|
||||
<Break Condition="pos == last"/>
|
||||
<Exec>page = pos / traits_type::page_size</Exec>
|
||||
<Exec>offset = pos & (traits_type::page_size - 1)</Exec>
|
||||
<If Condition="sparse[page] && (*((traits_type::entity_type *)&sparse[page][offset]) < cap())">
|
||||
<If Condition="sparse[page] && (*((traits_type::entity_type *)&sparse[page][offset]) < ~traits_type::entity_mask)">
|
||||
<Item Name="[{ pos }]">*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -66,7 +67,7 @@
|
||||
<Variable Name="last" InitialValue="packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((traits_type::entity_type *)&packed[pos]) < cap()">
|
||||
<If Condition="*((traits_type::entity_type *)&packed[pos]) < ~traits_type::entity_mask">
|
||||
<Item Name="[{ pos }]">packed[pos]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -77,11 +78,11 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_storage<*>">
|
||||
<Intrinsic Name="cap" Expression="(base_type::traits_type::version_mask << base_type::traits_type::length)"/>
|
||||
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
|
||||
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
|
||||
<Item Name="[length]" Optional="true" ExcludeView="simple">length</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 :) -->
|
||||
@@ -90,7 +91,7 @@
|
||||
<Variable Name="last" InitialValue="base_type::packed.size()"/>
|
||||
<Loop>
|
||||
<Break Condition="pos == last"/>
|
||||
<If Condition="*((base_type::traits_type::entity_type *)&base_type::packed[pos]) < cap()">
|
||||
<If Condition="*((base_type::traits_type::entity_type *)&base_type::packed[pos]) < ~base_type::traits_type::entity_mask">
|
||||
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos & (traits_type::page_size - 1)]</Item>
|
||||
</If>
|
||||
<Exec>++pos</Exec>
|
||||
@@ -99,12 +100,10 @@
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_view<*>">
|
||||
<DisplayString Condition="leading != nullptr">{{ size_hint={ leading->packed.size() } }}</DisplayString>
|
||||
<DisplayString>{{ size_hint=0 }}</DisplayString>
|
||||
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="[pools]" Optional="true">pools,na</Item>
|
||||
<Item Name="[filter]" Optional="true">filter,na</Item>
|
||||
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
|
||||
<Item Name="[pools]">pools,na</Item>
|
||||
<Item Name="[filter]">filter,na</Item>
|
||||
</Expand>
|
||||
</Type>
|
||||
<Type Name="entt::basic_runtime_view<*>">
|
||||
|
||||
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,
|
||||
)
|
||||
@@ -25,8 +25,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
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#include "macro.h"
|
||||
|
||||
#define ENTT_VERSION_MAJOR 3
|
||||
#define ENTT_VERSION_MINOR 13
|
||||
#define ENTT_VERSION_PATCH 0
|
||||
#define ENTT_VERSION_MINOR 12
|
||||
#define ENTT_VERSION_PATCH 2
|
||||
|
||||
#define ENTT_VERSION \
|
||||
ENTT_XSTR(ENTT_VERSION_MAJOR) \
|
||||
|
||||
@@ -20,7 +20,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Key, typename Type>
|
||||
@@ -65,7 +69,6 @@ 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
|
||||
: it{} {}
|
||||
@@ -187,7 +190,6 @@ 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
|
||||
: it{},
|
||||
@@ -239,7 +241,11 @@ template<typename Lhs, typename Rhs>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Associative container for key-value pairs with unique keys.
|
||||
@@ -983,7 +989,7 @@ public:
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
elem = std::numeric_limits<size_type>::max();
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
@@ -1027,7 +1033,11 @@ private:
|
||||
|
||||
} // 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>
|
||||
@@ -1035,6 +1045,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
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -202,7 +206,11 @@ template<typename Lhs, typename Rhs>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Associative container for unique objects of a given type.
|
||||
@@ -303,10 +311,6 @@ public:
|
||||
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. */
|
||||
@@ -443,46 +447,6 @@ public:
|
||||
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.
|
||||
@@ -878,7 +842,7 @@ public:
|
||||
sparse.first().resize(sz);
|
||||
|
||||
for(auto &&elem: sparse.first()) {
|
||||
elem = (std::numeric_limits<size_type>::max)();
|
||||
elem = std::numeric_limits<size_type>::max();
|
||||
}
|
||||
|
||||
for(size_type pos{}, last = size(); pos < last; ++pos) {
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
namespace entt {
|
||||
|
||||
|
||||
@@ -13,7 +13,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
enum class any_operation : std::uint8_t {
|
||||
@@ -26,19 +30,19 @@ enum class any_operation : std::uint8_t {
|
||||
get
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/*! @brief Possible modes of an any object. */
|
||||
enum class any_policy : std::uint8_t {
|
||||
/*! @brief Default mode, the object owns the contained element. */
|
||||
owner,
|
||||
/*! @brief Aliasing mode, the object _points_ to a non-const element. */
|
||||
ref,
|
||||
/*! @brief Const aliasing mode, the object _points_ to a const element. */
|
||||
cref
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A SBO friendly, type-safe container for single values of any type.
|
||||
* @tparam Len Size of the storage reserved for the small buffer optimization.
|
||||
@@ -47,6 +51,7 @@ enum class any_policy : std::uint8_t {
|
||||
template<std::size_t Len, std::size_t Align>
|
||||
class basic_any {
|
||||
using operation = internal::any_operation;
|
||||
using policy = internal::any_policy;
|
||||
using vtable_type = const void *(const operation, const basic_any &, const void *);
|
||||
|
||||
struct storage_type {
|
||||
@@ -58,11 +63,11 @@ class basic_any {
|
||||
|
||||
template<typename Type>
|
||||
static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
|
||||
static_assert(!std::is_void_v<Type> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
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;
|
||||
|
||||
if constexpr(in_situ<Type>) {
|
||||
element = (value.mode == any_policy::owner) ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
|
||||
} else {
|
||||
element = static_cast<const Type *>(value.instance);
|
||||
}
|
||||
@@ -75,7 +80,7 @@ class basic_any {
|
||||
break;
|
||||
case operation::move:
|
||||
if constexpr(in_situ<Type>) {
|
||||
if(value.mode == any_policy::owner) {
|
||||
if(value.owner()) {
|
||||
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
|
||||
}
|
||||
}
|
||||
@@ -124,7 +129,7 @@ class basic_any {
|
||||
|
||||
if constexpr(std::is_lvalue_reference_v<Type>) {
|
||||
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
|
||||
instance = (std::addressof(args), ...);
|
||||
} else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
|
||||
if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
|
||||
@@ -142,7 +147,7 @@ class basic_any {
|
||||
}
|
||||
}
|
||||
|
||||
basic_any(const basic_any &other, const any_policy pol) noexcept
|
||||
basic_any(const basic_any &other, const policy pol) noexcept
|
||||
: instance{other.data()},
|
||||
info{other.info},
|
||||
vtable{other.vtable},
|
||||
@@ -169,7 +174,7 @@ public:
|
||||
: instance{},
|
||||
info{},
|
||||
vtable{},
|
||||
mode{any_policy::owner} {
|
||||
mode{policy::owner} {
|
||||
initialize<Type>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@@ -209,7 +214,7 @@ public:
|
||||
|
||||
/*! @brief Frees the internal storage, whatever it means. */
|
||||
~basic_any() {
|
||||
if(vtable && (mode == any_policy::owner)) {
|
||||
if(vtable && owner()) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
}
|
||||
@@ -290,7 +295,7 @@ public:
|
||||
* @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());
|
||||
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,7 +304,7 @@ public:
|
||||
* @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));
|
||||
return mode == policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +325,7 @@ public:
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool assign(const basic_any &other) {
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
return (vtable(operation::assign, *this, other.data()) != nullptr);
|
||||
}
|
||||
|
||||
@@ -329,7 +334,7 @@ public:
|
||||
|
||||
/*! @copydoc assign */
|
||||
bool assign(basic_any &&other) {
|
||||
if(vtable && mode != any_policy::cref && *info == *other.info) {
|
||||
if(vtable && mode != policy::cref && *info == *other.info) {
|
||||
if(auto *val = other.data(); val) {
|
||||
return (vtable(operation::transfer, *this, val) != nullptr);
|
||||
} else {
|
||||
@@ -342,7 +347,7 @@ public:
|
||||
|
||||
/*! @brief Destroys contained object */
|
||||
void reset() {
|
||||
if(vtable && (mode == any_policy::owner)) {
|
||||
if(vtable && owner()) {
|
||||
vtable(operation::destroy, *this, nullptr);
|
||||
}
|
||||
|
||||
@@ -350,7 +355,7 @@ public:
|
||||
ENTT_ASSERT((instance = nullptr) == nullptr, "");
|
||||
info = &type_id<void>();
|
||||
vtable = nullptr;
|
||||
mode = any_policy::owner;
|
||||
mode = policy::owner;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,28 +393,20 @@ public:
|
||||
* @return A wrapper that shares a reference to an unmanaged object.
|
||||
*/
|
||||
[[nodiscard]] basic_any as_ref() noexcept {
|
||||
return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
|
||||
return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)};
|
||||
}
|
||||
|
||||
/*! @copydoc as_ref */
|
||||
[[nodiscard]] basic_any as_ref() const noexcept {
|
||||
return basic_any{*this, any_policy::cref};
|
||||
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.
|
||||
*/
|
||||
[[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
|
||||
return (mode == any_policy::owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 noexcept {
|
||||
return (mode == policy::owner);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -419,7 +416,7 @@ private:
|
||||
};
|
||||
const type_info *info;
|
||||
vtable_type *vtable;
|
||||
any_policy mode;
|
||||
policy mode;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,11 @@
|
||||
|
||||
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>
|
||||
@@ -69,7 +73,11 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief A compressed pair.
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
@@ -39,7 +43,11 @@ struct basic_hashed_string {
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Zero overhead unique identifier.
|
||||
@@ -167,7 +175,7 @@ public:
|
||||
* @return The size of the hashed string.
|
||||
*/
|
||||
[[nodiscard]] constexpr size_type size() const noexcept {
|
||||
return base_type::length; // NOLINT
|
||||
return base_type::length;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
namespace entt {
|
||||
|
||||
/**
|
||||
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
|
||||
* `std::has_single_bit`).
|
||||
* @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.
|
||||
*/
|
||||
@@ -22,8 +21,7 @@ namespace entt {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Computes the smallest power of two greater than or equal to a value
|
||||
* (waiting for C++20 and `std::bit_ceil`).
|
||||
* @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.
|
||||
*/
|
||||
@@ -165,7 +163,11 @@ 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>
|
||||
@@ -221,7 +223,11 @@ struct uses_allocator_construction<std::pair<Type, Other>> {
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
@@ -17,7 +21,11 @@ template<typename... Args>
|
||||
struct is_tuple_impl<std::tuple<Args...>>: std::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` to true if a given type is a
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct ENTT_API type_index final {
|
||||
@@ -34,26 +38,26 @@ template<typename Type>
|
||||
}
|
||||
|
||||
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) 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) 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) 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) noexcept {
|
||||
static const auto value = [](const auto stripped) {
|
||||
return hashed_string::value(stripped.data(), stripped.size());
|
||||
}(stripped_type_name<Type>());
|
||||
@@ -61,7 +65,11 @@ template<typename Type>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
|
||||
@@ -17,7 +17,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 */
|
||||
{};
|
||||
|
||||
@@ -250,40 +250,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
|
||||
@@ -678,7 +675,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>
|
||||
@@ -688,11 +689,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_cv_t<std::remove_pointer_t<Type>>>>>
|
||||
struct is_iterator<Type, std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_pointer_t<Type>>, void>>>
|
||||
: internal::has_iterator_category<Type> {};
|
||||
|
||||
/**
|
||||
@@ -737,7 +742,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>
|
||||
@@ -746,69 +763,51 @@ 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() {
|
||||
if constexpr(std::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(is_iterator_v<Type>) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else if constexpr(has_value_type<Type>::value) {
|
||||
if constexpr(std::is_same_v<typename Type::value_type, Type>) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else if constexpr(dispatch_is_equality_comparable<typename Type::value_type>()) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if constexpr(is_complete_v<std::tuple_size<std::remove_cv_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);
|
||||
}
|
||||
[[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_cv_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>);
|
||||
}
|
||||
}
|
||||
|
||||
} // 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>)> {};
|
||||
|
||||
/*! @copydoc is_equality_comparable */
|
||||
template<typename Type, auto N>
|
||||
struct is_equality_comparable<Type[N]>: std::false_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
@@ -875,9 +874,9 @@ using member_class_t = typename member_class<Member>::type;
|
||||
/**
|
||||
* @brief Extracts the n-th argument of a given function or member function.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
* @tparam Candidate A valid function, member function or data member.
|
||||
*/
|
||||
template<std::size_t Index, typename Candidate>
|
||||
template<std::size_t Index, auto Candidate>
|
||||
class nth_argument {
|
||||
template<typename Ret, typename... Args>
|
||||
static constexpr type_list<Args...> pick_up(Ret (*)(Args...));
|
||||
@@ -893,15 +892,15 @@ class nth_argument {
|
||||
|
||||
public:
|
||||
/*! @brief N-th argument of the given function or member function. */
|
||||
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
|
||||
using type = type_list_element_t<Index, decltype(pick_up(Candidate))>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
* @tparam Candidate A valid function, member function or data member.
|
||||
*/
|
||||
template<std::size_t Index, typename Candidate>
|
||||
template<std::size_t Index, auto Candidate>
|
||||
using nth_argument_t = typename nth_argument<Index, Candidate>::type;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename = void>
|
||||
@@ -28,11 +32,15 @@ template<>
|
||||
struct page_size<void>: std::integral_constant<std::size_t, 0u> {};
|
||||
|
||||
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.
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
// waiting for C++20 and std::popcount
|
||||
// waiting for C++20 (and std::popcount)
|
||||
template<typename Type>
|
||||
constexpr int popcount(Type value) noexcept {
|
||||
static constexpr int popcount(Type value) noexcept {
|
||||
return value ? (int(value & 1) + popcount(value >> 1)) : 0;
|
||||
}
|
||||
|
||||
@@ -56,7 +60,11 @@ struct entt_traits<std::uint64_t> {
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Common basic entity traits implementation.
|
||||
@@ -106,7 +114,7 @@ public:
|
||||
* @return The integral representation of the version part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
|
||||
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
|
||||
return static_cast<version_type>(to_integral(value) >> length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +124,7 @@ public:
|
||||
*/
|
||||
[[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)));
|
||||
return construct(to_entity(value), static_cast<version_type>(vers + (vers == version_mask)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -130,7 +138,7 @@ public:
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version) << length)};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,7 +152,8 @@ public:
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
|
||||
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
|
||||
constexpr auto mask = (version_mask << length);
|
||||
return value_type{(lhs & entity_mask) | (rhs & mask)};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -161,10 +170,8 @@ struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
@@ -172,10 +179,8 @@ template<typename Entity>
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
@@ -183,10 +188,8 @@ template<typename Entity>
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 {
|
||||
|
||||
@@ -17,9 +17,7 @@ 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
|
||||
in_place = 1u
|
||||
};
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
@@ -28,8 +26,46 @@ class basic_sparse_set;
|
||||
template<typename Type, typename = entity, typename = std::allocator<Type>, typename = void>
|
||||
class basic_storage;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_sigh_mixin;
|
||||
template<typename Type>
|
||||
class sigh_mixin;
|
||||
|
||||
/**
|
||||
* @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 = sigh_mixin<basic_storage<Type, 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;
|
||||
|
||||
template<typename Entity = entity, typename = std::allocator<Entity>>
|
||||
class basic_registry;
|
||||
@@ -61,67 +97,6 @@ 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 Type of objects assigned to the entities.
|
||||
*/
|
||||
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. */
|
||||
using registry = basic_registry<>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<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>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using handle_view = basic_handle<registry, 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 registry, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<registry>;
|
||||
|
||||
/*! @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.
|
||||
@@ -206,43 +181,53 @@ struct type_list_transform<owned_t<Type...>, Op> {
|
||||
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 = sigh_mixin<basic_storage<Type, Entity, Allocator>>;
|
||||
};
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using sparse_set = basic_sparse_set<>;
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Type of objects assigned to the entities.
|
||||
*/
|
||||
template<typename Type>
|
||||
using storage = basic_storage<Type>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using registry = basic_registry<>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using observer = basic_observer<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>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_type_t = typename storage_type<Args...>::type;
|
||||
using handle_view = basic_handle<registry, Args...>;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_for_t = typename storage_for<Args...>::type;
|
||||
using const_handle_view = basic_handle<const registry, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<registry>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
@@ -252,6 +237,12 @@ using storage_for_t = typename storage_for<Args...>::type;
|
||||
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. */
|
||||
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 declaration for the most common use case.
|
||||
* @tparam Owned Types of storage _owned_ by the group.
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename, typename>
|
||||
@@ -40,13 +44,12 @@ public:
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr extended_group_iterator()
|
||||
: it{},
|
||||
pools{} {}
|
||||
|
||||
extended_group_iterator(iterator_type from, const std::tuple<Owned *..., Get *...> &cpools)
|
||||
extended_group_iterator(It from, const std::tuple<Owned *..., Get *...> &cpools)
|
||||
: it{from},
|
||||
pools{cpools} {}
|
||||
|
||||
@@ -248,7 +251,11 @@ private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Group.
|
||||
@@ -491,6 +498,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the group results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam Type Type of the component to get.
|
||||
* @tparam Other Other types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
@@ -503,6 +515,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the groups results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam Index Indexes of the components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
@@ -643,28 +660,17 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort entities according to their order in a range.
|
||||
* @brief Sort the shared pool of entities according to a given storage.
|
||||
*
|
||||
* The shared pool of entities and thus its order is affected by the changes
|
||||
* to each and every pool that it tracks.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @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.
|
||||
*/
|
||||
template<typename It>
|
||||
void sort_as(It first, It last) const {
|
||||
if(*this) {
|
||||
descriptor->handle().sort_as(first, last);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort entities according to their order in a range.
|
||||
* @param other The storage to use to impose the order.
|
||||
*/
|
||||
[[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const {
|
||||
sort_as(other.begin(), other.end());
|
||||
void sort_as(const common_type &other) const {
|
||||
if(*this) {
|
||||
descriptor->handle().sort_as(other);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -897,6 +903,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the group results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam Type Type of the component to get.
|
||||
* @tparam Other Other types of components to get.
|
||||
* @param entt A valid identifier.
|
||||
@@ -909,6 +920,11 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Returns the components assigned to the given entity.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to use an entity that doesn't belong to the groups results in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @tparam Index Indexes of the components to get.
|
||||
* @param entt A valid identifier.
|
||||
* @return The components assigned to the entity.
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -29,7 +33,6 @@ 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 handle_storage_iterator() noexcept
|
||||
: entt{null},
|
||||
@@ -83,7 +86,11 @@ template<typename ILhs, typename IRhs>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Non-owning handle to an entity.
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include "../signal/delegate.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "group.hpp"
|
||||
#include "storage.hpp"
|
||||
#include "view.hpp"
|
||||
|
||||
namespace entt {
|
||||
@@ -104,7 +103,7 @@ private:
|
||||
* @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 Registry = std::decay_t<nth_argument_t<0u, decltype(Member)>>>
|
||||
template<auto Member, typename Registry = std::decay_t<nth_argument_t<0u, 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");
|
||||
delegate<void(Registry &, const typename Registry::entity_type)> func;
|
||||
@@ -116,41 +115,27 @@ void invoke(Registry ®, const typename Registry::entity_type entt) {
|
||||
* @brief Returns the entity associated with a given component.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default storage as it
|
||||
* Currently, this function only works correctly with the default pool as it
|
||||
* makes assumptions about how the components are laid out.
|
||||
*
|
||||
* @tparam Args Storage type template parameters.
|
||||
* @param storage A storage that contains the given component.
|
||||
* @param instance A valid component instance.
|
||||
* @return The entity associated with the given component.
|
||||
*/
|
||||
template<typename... Args>
|
||||
auto to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) -> typename basic_storage<Args...>::entity_type {
|
||||
constexpr auto page_size = basic_storage<Args...>::traits_type::page_size;
|
||||
const typename basic_storage<Args...>::base_type &base = storage;
|
||||
const auto *addr = std::addressof(instance);
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
|
||||
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
|
||||
return *(it + dist);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief to_entity
|
||||
* @tparam Args Registry type template parameters.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @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... Args, typename Component>
|
||||
[[deprecated("use storage based to_entity instead")]] typename basic_registry<Args...>::entity_type to_entity(const basic_registry<Args...> ®, const Component &instance) {
|
||||
template<typename Registry, typename Component>
|
||||
typename Registry::entity_type to_entity(const Registry ®, const Component &instance) {
|
||||
if(const auto *storage = reg.template storage<Component>(); storage) {
|
||||
return to_entity(*storage, instance);
|
||||
constexpr auto page_size = std::remove_const_t<std::remove_pointer_t<decltype(storage)>>::traits_type::page_size;
|
||||
const typename Registry::common_type &base = *storage;
|
||||
const auto *addr = std::addressof(instance);
|
||||
|
||||
for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) {
|
||||
if(const auto dist = (addr - std::addressof(storage->get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) {
|
||||
return *(it + dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -22,23 +22,18 @@ namespace entt {
|
||||
*
|
||||
* This applies to all signals made available.
|
||||
*
|
||||
* @tparam Type Underlying storage type.
|
||||
* @tparam Registry Basic registry type.
|
||||
* @tparam Type The type of the underlying storage.
|
||||
*/
|
||||
template<typename Type, typename Registry>
|
||||
class basic_sigh_mixin final: public Type {
|
||||
template<typename Type>
|
||||
class sigh_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_type::allocator_type>;
|
||||
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
|
||||
using sigh_type = sigh<void(basic_registry_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");
|
||||
|
||||
owner_type &owner_or_assert() const noexcept {
|
||||
basic_registry_type &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*owner);
|
||||
return *owner;
|
||||
}
|
||||
|
||||
void pop(underlying_iterator first, underlying_iterator last) final {
|
||||
@@ -56,17 +51,13 @@ class basic_sigh_mixin final: public Type {
|
||||
|
||||
void pop_all() final {
|
||||
if(auto ® = owner_or_assert(); !destruction.empty()) {
|
||||
for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) {
|
||||
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
|
||||
destruction.publish(reg, *it);
|
||||
} else {
|
||||
if constexpr(underlying_type::traits_type::in_place_delete) {
|
||||
if(const auto entt = *it; entt != tombstone) {
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
} else {
|
||||
destruction.publish(reg, *it);
|
||||
for(auto pos = underlying_type::each().begin().base().index(); !(pos < 0); --pos) {
|
||||
if constexpr(underlying_type::traits_type::in_place_delete) {
|
||||
if(const auto entt = underlying_type::operator[](static_cast<typename underlying_type::size_type>(pos)); entt != tombstone) {
|
||||
destruction.publish(reg, entt);
|
||||
}
|
||||
} else {
|
||||
destruction.publish(reg, underlying_type::operator[](static_cast<typename underlying_type::size_type>(pos)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,17 +81,17 @@ public:
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = typename underlying_type::entity_type;
|
||||
/*! @brief Expected registry type. */
|
||||
using registry_type = owner_type;
|
||||
using registry_type = basic_registry_type;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_sigh_mixin()
|
||||
: basic_sigh_mixin{allocator_type{}} {}
|
||||
sigh_mixin()
|
||||
: 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)
|
||||
explicit sigh_mixin(const allocator_type &allocator)
|
||||
: underlying_type{allocator},
|
||||
owner{},
|
||||
construction{allocator},
|
||||
@@ -111,7 +102,7 @@ public:
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other) noexcept
|
||||
sigh_mixin(sigh_mixin &&other) noexcept
|
||||
: underlying_type{std::move(other)},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction)},
|
||||
@@ -123,7 +114,7 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept
|
||||
sigh_mixin(sigh_mixin &&other, const allocator_type &allocator) noexcept
|
||||
: underlying_type{std::move(other), allocator},
|
||||
owner{other.owner},
|
||||
construction{std::move(other.construction), allocator},
|
||||
@@ -135,7 +126,7 @@ public:
|
||||
* @param other The instance to move from.
|
||||
* @return This storage.
|
||||
*/
|
||||
basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
|
||||
sigh_mixin &operator=(sigh_mixin &&other) noexcept {
|
||||
underlying_type::operator=(std::move(other));
|
||||
owner = other.owner;
|
||||
construction = std::move(other.construction);
|
||||
@@ -148,7 +139,7 @@ public:
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_sigh_mixin &other) {
|
||||
void swap(sigh_mixin &other) {
|
||||
using std::swap;
|
||||
underlying_type::swap(other);
|
||||
swap(owner, other.owner);
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
@@ -83,7 +87,11 @@ 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.
|
||||
|
||||
@@ -30,7 +30,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -46,7 +50,6 @@ 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 registry_storage_iterator() noexcept
|
||||
: it{} {}
|
||||
@@ -222,7 +225,11 @@ private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Fast and reliable entity-component system.
|
||||
@@ -242,18 +249,17 @@ class basic_registry {
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
return entities;
|
||||
} else {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
||||
auto &cpool = pools[id];
|
||||
|
||||
if(!cpool) {
|
||||
using storage_type = storage_for_type<Type>;
|
||||
using alloc_type = typename storage_type::allocator_type;
|
||||
|
||||
if constexpr(std::is_void_v<Type> && !std::is_constructible_v<alloc_type, allocator_type>) {
|
||||
if constexpr(std::is_same_v<Type, void> && !std::is_constructible_v<alloc_type, allocator_type>) {
|
||||
// std::allocator<void> has no cross constructors (waiting for C++20)
|
||||
cpool = std::allocate_shared<storage_type>(get_allocator(), alloc_type{});
|
||||
} else {
|
||||
@@ -270,11 +276,11 @@ class basic_registry {
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash<Type>::value()) const {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
return &entities;
|
||||
} else {
|
||||
static_assert(std::is_same_v<Type, std::decay_t<Type>>, "Non-decayed types not allowed");
|
||||
|
||||
if(const auto it = pools.find(id); it != pools.cend()) {
|
||||
ENTT_ASSERT(it->second->type() == type_id<Type>(), "Unexpected type");
|
||||
return static_cast<const storage_for_type<Type> *>(it->second.get());
|
||||
@@ -307,10 +313,6 @@ public:
|
||||
using common_type = base_type;
|
||||
/*! @brief Context type. */
|
||||
using context = internal::registry_context<allocator_type>;
|
||||
/*! @brief Iterable registry type. */
|
||||
using iterable = iterable_adaptor<internal::registry_storage_iterator<typename pool_container_type::iterator>>;
|
||||
/*! @brief Constant iterable registry type. */
|
||||
using const_iterable = iterable_adaptor<internal::registry_storage_iterator<typename pool_container_type::const_iterator>>;
|
||||
|
||||
/**
|
||||
* @copybrief storage_for
|
||||
@@ -393,7 +395,7 @@ public:
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return entities.get_allocator();
|
||||
return pools.get_allocator();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,12 +406,12 @@ public:
|
||||
*
|
||||
* @return An iterable object to use to _visit_ the registry.
|
||||
*/
|
||||
[[nodiscard]] iterable storage() noexcept {
|
||||
[[nodiscard]] auto storage() noexcept {
|
||||
return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}};
|
||||
}
|
||||
|
||||
/*! @copydoc storage */
|
||||
[[nodiscard]] const_iterable storage() const noexcept {
|
||||
[[nodiscard]] auto storage() const noexcept {
|
||||
return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}};
|
||||
}
|
||||
|
||||
@@ -454,13 +456,78 @@ public:
|
||||
return assure<Type>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities created so far.
|
||||
* @return Number of entities created so far.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().size() instead")]] [[nodiscard]] size_type size() const noexcept {
|
||||
return entities.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities still in use.
|
||||
* @return Number of entities still in use.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().in_use() instead")]] [[nodiscard]] size_type alive() const {
|
||||
return entities.in_use();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity (number of entities) of the registry.
|
||||
* @param cap Desired capacity.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().reserve(cap) instead")]] void reserve(const size_type cap) {
|
||||
entities.reserve(cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of entities that a registry has currently
|
||||
* allocated space for.
|
||||
* @return Capacity of the registry.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().capacity() instead")]] [[nodiscard]] size_type capacity() const noexcept {
|
||||
return entities.capacity();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether the registry is empty (no entities still in use).
|
||||
* @return True if the registry is empty, false otherwise.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().in_use() instead")]] [[nodiscard]] bool empty() const {
|
||||
return !alive();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Direct access to the list of entities of a registry.
|
||||
*
|
||||
* The returned pointer is such that range `[data(), data() + size())` is
|
||||
* always a valid range, even if the registry is empty.
|
||||
*
|
||||
* @warning
|
||||
* This list contains both valid and destroyed entities and isn't suitable
|
||||
* for direct use.
|
||||
*
|
||||
* @return A pointer to the array of entities.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().data() instead")]] [[nodiscard]] const entity_type *data() const noexcept {
|
||||
return entities.data();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of released entities.
|
||||
* @return The number of released entities.
|
||||
*/
|
||||
[[deprecated("use .storage<Entity>().size() and .storage<Entity>().in_use() instead")]] [[nodiscard]] size_type released() const noexcept {
|
||||
return (entities.size() - entities.in_use());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an identifier refers to a valid entity.
|
||||
* @param entt An identifier, either valid or not.
|
||||
* @return True if the identifier is valid, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool valid(const entity_type entt) const {
|
||||
return entities.contains(entt) && (entities.index(entt) < entities.free_list());
|
||||
return entities.contains(entt) && (entities.index(entt) < entities.in_use());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -508,6 +575,74 @@ public:
|
||||
entities.insert(std::move(first), std::move(last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns identifiers to an empty registry.
|
||||
*
|
||||
* This function is intended for use in conjunction with `data`, `size` and
|
||||
* `released`.<br/>
|
||||
* Don't try to inject ranges of randomly generated entities nor the _wrong_
|
||||
* head for the list of destroyed entities. There is no guarantee that a
|
||||
* registry will continue to work properly in this case.
|
||||
*
|
||||
* @warning
|
||||
* There must be no entities still alive for this to work properly.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @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 destroyed The number of released entities.
|
||||
*/
|
||||
template<typename It>
|
||||
[[deprecated("use .storage<Entity>().push(first, last) and .storage<Entity>().in_use(len) instead")]] void assign(It first, It last, const size_type destroyed) {
|
||||
ENTT_ASSERT(!entities.in_use(), "Non-empty registry");
|
||||
entities.push(first, last);
|
||||
entities.in_use(entities.size() - destroyed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases an identifier.
|
||||
*
|
||||
* The version is updated and the identifier can be recycled at any time.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @return The version of the recycled entity.
|
||||
*/
|
||||
[[deprecated("use .orphan(entt) and .storage<Entity>().erase(entt) instead")]] version_type release(const entity_type entt) {
|
||||
ENTT_ASSERT(orphan(entt), "Non-orphan entity");
|
||||
entities.erase(entt);
|
||||
return entities.current(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases an identifier.
|
||||
*
|
||||
* The suggested version or the valid version closest to the suggested one
|
||||
* is used instead of the implicitly generated version.
|
||||
*
|
||||
* @param entt A valid identifier.
|
||||
* @param version A desired version upon destruction.
|
||||
* @return The version actually assigned to the entity.
|
||||
*/
|
||||
[[deprecated("use .orphan(entt), then .storage<Entity>().erase(entt)/.bump(next) instead")]] version_type release(const entity_type entt, const version_type version) {
|
||||
ENTT_ASSERT(orphan(entt), "Non-orphan entity");
|
||||
entities.erase(entt);
|
||||
const auto elem = traits_type::construct(traits_type::to_entity(entt), version);
|
||||
return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Releases all identifiers in a range.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @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.
|
||||
*/
|
||||
template<typename It>
|
||||
[[deprecated("use .orphan(entt) and .storage<Entity>().erase(first, last) instead")]] void release(It first, It last) {
|
||||
ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return orphan(entt); }), "Non-orphan entity");
|
||||
entities.erase(std::move(first), std::move(last));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys an entity and releases its identifier.
|
||||
*
|
||||
@@ -556,13 +691,11 @@ public:
|
||||
*/
|
||||
template<typename It>
|
||||
void destroy(It first, It last) {
|
||||
entities.sort_as(first, last);
|
||||
const auto from = entities.each().cbegin().base();
|
||||
const auto to = from + entities.pack(first, last);
|
||||
|
||||
const auto from = entities.cbegin(0);
|
||||
const auto to = from + std::distance(first, last);
|
||||
|
||||
for(auto &&curr: pools) {
|
||||
curr.second->remove(from, to);
|
||||
for(size_type pos = pools.size(); pos; --pos) {
|
||||
pools.begin()[pos - 1u].second->remove(from, to);
|
||||
}
|
||||
|
||||
entities.erase(from, to);
|
||||
@@ -830,7 +963,7 @@ public:
|
||||
* @return True if the entity is part of all the storage, false otherwise.
|
||||
*/
|
||||
template<typename... Type>
|
||||
[[nodiscard]] bool all_of([[maybe_unused]] const entity_type entt) const {
|
||||
[[nodiscard]] bool all_of(const entity_type entt) const {
|
||||
if constexpr(sizeof...(Type) == 1u) {
|
||||
auto *cpool = assure<std::remove_const_t<Type>...>();
|
||||
return cpool && cpool->contains(entt);
|
||||
@@ -847,7 +980,7 @@ public:
|
||||
* otherwise.
|
||||
*/
|
||||
template<typename... Type>
|
||||
[[nodiscard]] bool any_of([[maybe_unused]] const entity_type entt) const {
|
||||
[[nodiscard]] bool any_of(const entity_type entt) const {
|
||||
return (all_of<Type>(entt) || ...);
|
||||
}
|
||||
|
||||
@@ -929,7 +1062,8 @@ public:
|
||||
template<typename... Type>
|
||||
[[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) {
|
||||
if constexpr(sizeof...(Type) == 1u) {
|
||||
return (const_cast<Type *>(std::as_const(*this).template try_get<Type>(entt)), ...);
|
||||
auto &cpool = assure<std::remove_const_t<Type>...>();
|
||||
return (static_cast<Type *>(cpool.contains(entt) ? std::addressof(cpool.get(entt)) : nullptr), ...);
|
||||
} else {
|
||||
return std::make_tuple(try_get<Type>(entt)...);
|
||||
}
|
||||
@@ -946,13 +1080,34 @@ public:
|
||||
pools.begin()[pos - 1u].second->clear();
|
||||
}
|
||||
|
||||
const auto elem = entities.each();
|
||||
entities.erase(elem.begin().base(), elem.end().base());
|
||||
const auto iterable = entities.each();
|
||||
entities.erase(iterable.begin().base(), iterable.end().base());
|
||||
} else {
|
||||
(assure<Type>().clear(), ...);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Iterates all the entities that are still in use.
|
||||
*
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* It's not defined whether entities created during iteration are returned.
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
[[deprecated("use .storage<Entity>().each() instead")]] void each(Func func) const {
|
||||
for(auto [entt]: entities.each()) {
|
||||
func(entt);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if an entity has components assigned.
|
||||
* @param entt A valid identifier.
|
||||
@@ -1175,8 +1330,7 @@ public:
|
||||
template<typename To, typename From>
|
||||
void sort() {
|
||||
ENTT_ASSERT(!owned<To>(), "Cannot sort owned storage");
|
||||
const base_type &cpool = assure<From>();
|
||||
assure<To>().sort_as(cpool.begin(), cpool.end());
|
||||
assure<To>().sort_as(assure<From>());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Set>
|
||||
@@ -91,7 +95,11 @@ private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Generic runtime view.
|
||||
|
||||
@@ -16,22 +16,30 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Registry>
|
||||
void orphans(Registry ®istry) {
|
||||
auto &storage = registry.template storage<typename Registry::entity_type>();
|
||||
auto view = registry.template view<typename Registry::entity_type>();
|
||||
|
||||
for(auto entt: storage) {
|
||||
for(auto entt: view) {
|
||||
if(registry.orphan(entt)) {
|
||||
storage.erase(entt);
|
||||
view.storage()->erase(entt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Utility class to create snapshots from a registry.
|
||||
@@ -81,7 +89,7 @@ public:
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->size()));
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->free_list()));
|
||||
archive(static_cast<typename traits_type::entity_type>(storage->in_use()));
|
||||
|
||||
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
|
||||
archive(*first);
|
||||
@@ -132,6 +140,45 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serializes all identifiers, including those to be recycled.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename Archive>
|
||||
[[deprecated("use .get<Entity>(archive) instead")]] const basic_snapshot &entities(Archive &archive) const {
|
||||
return get<entity_type>(archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serializes all elements of a type with associated identifiers.
|
||||
* @tparam Component Types of components to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
[[deprecated("use .get<Type>(archive) instead")]] const basic_snapshot &component(Archive &archive) const {
|
||||
return (get<Component>(archive), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serializes all elements of a type with associated identifiers for
|
||||
* the entities in a range.
|
||||
* @tparam Component Types of components to serialize.
|
||||
* @tparam Archive Type of output archive.
|
||||
* @tparam It Type of input iterator.
|
||||
* @param archive A valid reference to an output archive.
|
||||
* @param first An iterator to the first element of the range to serialize.
|
||||
* @param last An iterator past the last element of the range to serialize.
|
||||
* @return An object of this type to continue creating the snapshot.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename It>
|
||||
[[deprecated("use .get<Type>(archive, first, last) instead")]] const basic_snapshot &component(Archive &archive, It first, It last) const {
|
||||
return (get<Component>(archive, first, last), ...);
|
||||
}
|
||||
|
||||
private:
|
||||
const registry_type *reg;
|
||||
};
|
||||
@@ -164,7 +211,9 @@ public:
|
||||
basic_snapshot_loader(registry_type &source) noexcept
|
||||
: reg{&source} {
|
||||
// restoring a snapshot as a whole requires a clean registry
|
||||
ENTT_ASSERT(reg->template storage<entity_type>().empty() && (reg->storage().begin() == reg->storage().end()), "Registry must be empty");
|
||||
for([[maybe_unused]] auto elem: source.storage()) {
|
||||
ENTT_ASSERT(elem.second.empty(), "Registry must be empty");
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Default move constructor. */
|
||||
@@ -182,24 +231,24 @@ public:
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename Type, typename Archive>
|
||||
basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
basic_snapshot_loader &get([[maybe_unused]] Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
auto &storage = reg->template storage<Type>(id);
|
||||
typename traits_type::entity_type length{};
|
||||
|
||||
archive(length);
|
||||
|
||||
if constexpr(std::is_same_v<Type, entity_type>) {
|
||||
typename traits_type::entity_type count{};
|
||||
typename traits_type::entity_type in_use{};
|
||||
|
||||
storage.reserve(length);
|
||||
archive(count);
|
||||
archive(in_use);
|
||||
|
||||
for(entity_type entity = null; length; --length) {
|
||||
archive(entity);
|
||||
storage.emplace(entity);
|
||||
}
|
||||
|
||||
storage.free_list(count);
|
||||
storage.in_use(in_use);
|
||||
} else {
|
||||
auto &other = reg->template storage<entity_type>();
|
||||
entity_type entt{null};
|
||||
@@ -209,7 +258,7 @@ public:
|
||||
const auto entity = other.contains(entt) ? entt : other.emplace(entt);
|
||||
ENTT_ASSERT(entity == entt, "Entity not available for use");
|
||||
|
||||
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
|
||||
if constexpr(Registry::template storage_for_type<Type>::traits_type::page_size == 0u) {
|
||||
storage.emplace(entity);
|
||||
} else {
|
||||
Type elem{};
|
||||
@@ -223,6 +272,33 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores all identifiers, including those to be recycled.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename Archive>
|
||||
[[deprecated("use .get<Entity>(archive) instead")]] basic_snapshot_loader &entities(Archive &archive) {
|
||||
return get<entity_type>(archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores all elements of a type with associated identifiers.
|
||||
*
|
||||
* The template parameter list must be exactly the same used during
|
||||
* serialization.
|
||||
*
|
||||
* @tparam Component Type of component to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename... Component, typename Archive>
|
||||
[[deprecated("use .get<Type>(archive) instead")]] basic_snapshot_loader &component(Archive &archive) {
|
||||
return (get<Component>(archive), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
*
|
||||
@@ -353,7 +429,7 @@ public:
|
||||
* @return A valid loader to continue restoring data.
|
||||
*/
|
||||
template<typename Type, typename Archive>
|
||||
basic_continuous_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
basic_continuous_loader &get([[maybe_unused]] Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
auto &storage = reg->template storage<Type>(id);
|
||||
typename traits_type::entity_type length{};
|
||||
entity_type entt{null};
|
||||
@@ -391,7 +467,7 @@ public:
|
||||
if(archive(entt); entt != null) {
|
||||
restore(entt);
|
||||
|
||||
if constexpr(std::tuple_size_v<decltype(storage.get_as_tuple({}))> == 0u) {
|
||||
if constexpr(Registry::template storage_for_type<Type>::traits_type::page_size == 0u) {
|
||||
storage.emplace(map(entt));
|
||||
} else {
|
||||
Type elem{};
|
||||
@@ -405,6 +481,79 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Restores all identifiers, including those to be recycled.
|
||||
*
|
||||
* It creates local counterparts for remote elements as needed.
|
||||
*
|
||||
* @tparam Archive Type of input archive.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename Archive>
|
||||
[[deprecated("use .get<Entity>(archive) instead")]] basic_continuous_loader &entities(Archive &archive) {
|
||||
return get<entity_type>(archive);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serializes all elements of a type with associated identifiers.
|
||||
*
|
||||
* It creates local counterparts for remote elements as needed.<br/>
|
||||
* Members are either data members of type entity_type or containers of
|
||||
* entities. In both cases, a loader visits them and replaces entities with
|
||||
* their local counterpart.
|
||||
*
|
||||
* @tparam Component Type of component to restore.
|
||||
* @tparam Archive Type of input archive.
|
||||
* @tparam Member Types of members to update with their local counterparts.
|
||||
* @param archive A valid reference to an input archive.
|
||||
* @param member Members to update with their local counterparts.
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
template<typename... Component, typename Archive, typename... Member, typename... Clazz>
|
||||
[[deprecated("use .component<Type>(archive, members...) instead")]] basic_continuous_loader &component(Archive &archive, Member Clazz::*...member) {
|
||||
([&](auto &storage) {
|
||||
for(auto &&ref: remloc) {
|
||||
storage.remove(ref.second.second);
|
||||
}
|
||||
|
||||
typename traits_type::entity_type length{};
|
||||
entity_type entt{null};
|
||||
|
||||
archive(length);
|
||||
|
||||
while(length--) {
|
||||
if(archive(entt); entt != null) {
|
||||
restore(entt);
|
||||
|
||||
if constexpr(std::remove_reference_t<decltype(storage)>::traits_type::page_size == 0u) {
|
||||
storage.emplace(map(entt));
|
||||
} else {
|
||||
typename std::remove_reference_t<decltype(storage)>::value_type elem{};
|
||||
archive(elem);
|
||||
(update(elem, member), ...);
|
||||
storage.emplace(map(entt), std::move(elem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}(reg->template storage<Component>()),
|
||||
...);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helps to purge entities that no longer have a counterpart.
|
||||
*
|
||||
* Users should invoke this member function after restoring each snapshot,
|
||||
* unless they know exactly what they are doing.
|
||||
*
|
||||
* @return A non-const reference to this loader.
|
||||
*/
|
||||
[[deprecated("use .get<Entity>(archive) instead")]] basic_continuous_loader &shrink() {
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destroys those entities that have no components.
|
||||
*
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Container>
|
||||
@@ -133,7 +137,11 @@ template<typename Container>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic sparse set implementation.
|
||||
@@ -160,7 +168,6 @@ class basic_sparse_set {
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
|
||||
using sparse_container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
|
||||
using packed_container_type = std::vector<Entity, Allocator>;
|
||||
using underlying_type = typename entt_traits<Entity>::entity_type;
|
||||
|
||||
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
|
||||
@@ -187,13 +194,14 @@ class basic_sparse_set {
|
||||
}
|
||||
|
||||
if(!sparse[page]) {
|
||||
constexpr entity_type init = null;
|
||||
auto page_allocator{packed.get_allocator()};
|
||||
sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size);
|
||||
std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init);
|
||||
std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, null);
|
||||
}
|
||||
|
||||
return sparse[page][fast_mod(pos, traits_type::page_size)];
|
||||
auto &elem = sparse[page][fast_mod(pos, traits_type::page_size)];
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
return elem;
|
||||
}
|
||||
|
||||
void release_sparse_pages() {
|
||||
@@ -208,42 +216,31 @@ class basic_sparse_set {
|
||||
}
|
||||
}
|
||||
|
||||
void swap_at(const std::size_t from, const std::size_t to) {
|
||||
auto &lhs = packed[from];
|
||||
auto &rhs = packed[to];
|
||||
|
||||
sparse_ref(lhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(to), traits_type::to_integral(lhs));
|
||||
sparse_ref(rhs) = traits_type::combine(static_cast<typename traits_type::entity_type>(from), traits_type::to_integral(rhs));
|
||||
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
|
||||
underlying_type policy_to_head() {
|
||||
return traits_type::entity_mask * (mode != deletion_policy::swap_only);
|
||||
}
|
||||
|
||||
private:
|
||||
virtual const void *get_at(const std::size_t) const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {
|
||||
ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported");
|
||||
}
|
||||
virtual void swap_or_move(const std::size_t, const std::size_t) {}
|
||||
|
||||
protected:
|
||||
/*! @brief Random access iterator type. */
|
||||
using basic_iterator = internal::sparse_set_iterator<packed_container_type>;
|
||||
|
||||
/**
|
||||
* @brief Erases an entity from a sparse set.
|
||||
* @param it An iterator to the element to pop.
|
||||
* @brief Swaps two items at specific locations.
|
||||
* @param lhs A position to move from.
|
||||
* @param rhs The other position to move from.
|
||||
*/
|
||||
void swap_only(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch");
|
||||
const auto pos = static_cast<underlying_type>(index(*it));
|
||||
bump(traits_type::next(*it));
|
||||
swap_at(pos, static_cast<size_type>(head -= (pos < head)));
|
||||
void swap_at(const std::size_t lhs, const std::size_t rhs) {
|
||||
const auto entity = static_cast<typename traits_type::entity_type>(lhs);
|
||||
const auto other = static_cast<typename traits_type::entity_type>(rhs);
|
||||
|
||||
sparse_ref(packed[lhs]) = traits_type::combine(other, traits_type::to_integral(packed[lhs]));
|
||||
sparse_ref(packed[rhs]) = traits_type::combine(entity, traits_type::to_integral(packed[rhs]));
|
||||
|
||||
using std::swap;
|
||||
swap(packed[lhs], packed[rhs]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -251,7 +248,7 @@ protected:
|
||||
* @param it An iterator to the element to pop.
|
||||
*/
|
||||
void swap_and_pop(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch");
|
||||
ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched");
|
||||
auto &self = sparse_ref(*it);
|
||||
const auto entt = traits_type::to_entity(self);
|
||||
sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back()));
|
||||
@@ -268,9 +265,9 @@ protected:
|
||||
* @param it An iterator to the element to pop.
|
||||
*/
|
||||
void in_place_pop(const basic_iterator it) {
|
||||
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch");
|
||||
ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched");
|
||||
const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null));
|
||||
packed[static_cast<size_type>(entt)] = traits_type::combine(std::exchange(head, entt), tombstone);
|
||||
packed[static_cast<size_type>(entt)] = std::exchange(free_list, traits_type::combine(entt, tombstone));
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -280,47 +277,31 @@ protected:
|
||||
* @param last An iterator past the last element of the range of entities.
|
||||
*/
|
||||
virtual void pop(basic_iterator first, basic_iterator last) {
|
||||
switch(mode) {
|
||||
case deletion_policy::swap_and_pop:
|
||||
if(mode == deletion_policy::swap_and_pop) {
|
||||
for(; first != last; ++first) {
|
||||
swap_and_pop(first);
|
||||
}
|
||||
break;
|
||||
case deletion_policy::in_place:
|
||||
} else {
|
||||
for(; first != last; ++first) {
|
||||
in_place_pop(first);
|
||||
}
|
||||
break;
|
||||
case deletion_policy::swap_only:
|
||||
for(; first != last; ++first) {
|
||||
swap_only(first);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Erases all entities of a sparse set. */
|
||||
virtual void pop_all() {
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != traits_type::to_entity(null)) {
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
if(*first != tombstone) {
|
||||
sparse_ref(*first) = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case deletion_policy::swap_only:
|
||||
case deletion_policy::swap_and_pop:
|
||||
if(const auto prev = std::exchange(free_list, tombstone); prev == null) {
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
sparse_ref(*first) = null;
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
for(auto first = begin(); !(first.index() < 0); ++first) {
|
||||
if(*first != tombstone) {
|
||||
sparse_ref(*first) = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
head = policy_to_head();
|
||||
packed.clear();
|
||||
}
|
||||
|
||||
@@ -331,42 +312,18 @@ protected:
|
||||
* @return Iterator pointing to the emplaced element.
|
||||
*/
|
||||
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
|
||||
auto &elem = assure_at_least(entt);
|
||||
auto pos = size();
|
||||
ENTT_ASSERT(!contains(entt), "Set already contains entity");
|
||||
|
||||
switch(mode) {
|
||||
case deletion_policy::in_place:
|
||||
if(head != traits_type::to_entity(null) && !force_back) {
|
||||
pos = static_cast<size_type>(head);
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
elem = traits_type::combine(head, traits_type::to_integral(entt));
|
||||
head = traits_type::to_entity(std::exchange(packed[pos], entt));
|
||||
break;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case deletion_policy::swap_and_pop:
|
||||
if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
|
||||
packed.push_back(entt);
|
||||
ENTT_ASSERT(elem == null, "Slot not available");
|
||||
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
|
||||
break;
|
||||
case deletion_policy::swap_only:
|
||||
if(elem == null) {
|
||||
packed.push_back(entt);
|
||||
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
|
||||
} else {
|
||||
ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available");
|
||||
bump(entt);
|
||||
}
|
||||
|
||||
if(force_back) {
|
||||
pos = static_cast<size_type>(head++);
|
||||
swap_at(static_cast<size_type>(traits_type::to_entity(elem)), pos);
|
||||
}
|
||||
|
||||
break;
|
||||
return begin();
|
||||
} else {
|
||||
const auto pos = static_cast<size_type>(traits_type::to_entity(free_list));
|
||||
elem = traits_type::combine(traits_type::to_integral(free_list), traits_type::to_integral(entt));
|
||||
free_list = std::exchange(packed[pos], entt);
|
||||
return --(end() - pos);
|
||||
}
|
||||
|
||||
return --(end() - pos);
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -421,8 +378,8 @@ public:
|
||||
: sparse{allocator},
|
||||
packed{allocator},
|
||||
info{&elem},
|
||||
mode{pol},
|
||||
head{policy_to_head()} {}
|
||||
free_list{tombstone},
|
||||
mode{pol} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
@@ -432,8 +389,8 @@ public:
|
||||
: sparse{std::move(other.sparse)},
|
||||
packed{std::move(other.packed)},
|
||||
info{other.info},
|
||||
mode{other.mode},
|
||||
head{std::exchange(other.head, policy_to_head())} {}
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -444,8 +401,8 @@ public:
|
||||
: sparse{std::move(other.sparse), allocator},
|
||||
packed{std::move(other.packed), allocator},
|
||||
info{other.info},
|
||||
mode{other.mode},
|
||||
head{std::exchange(other.head, policy_to_head())} {
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
|
||||
}
|
||||
|
||||
@@ -466,8 +423,8 @@ public:
|
||||
sparse = std::move(other.sparse);
|
||||
packed = std::move(other.packed);
|
||||
info = other.info;
|
||||
free_list = std::exchange(other.free_list, tombstone);
|
||||
mode = other.mode;
|
||||
head = std::exchange(other.head, policy_to_head());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -480,8 +437,8 @@ public:
|
||||
swap(sparse, other.sparse);
|
||||
swap(packed, other.packed);
|
||||
swap(info, other.info);
|
||||
swap(free_list, other.free_list);
|
||||
swap(mode, other.mode);
|
||||
swap(head, other.head);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,23 +457,6 @@ public:
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the head of the free list, if any.
|
||||
* @return The head of the free list.
|
||||
*/
|
||||
[[nodiscard]] size_type free_list() const noexcept {
|
||||
return static_cast<size_type>(head);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the head of the free list, if possible.
|
||||
* @param len The value to use as the new head of the free list.
|
||||
*/
|
||||
void free_list(const size_type len) noexcept {
|
||||
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value");
|
||||
head = static_cast<underlying_type>(len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a sparse set.
|
||||
*
|
||||
@@ -584,7 +524,7 @@ public:
|
||||
* @return True if the sparse set is fully packed, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool contiguous() const noexcept {
|
||||
return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null));
|
||||
return (free_list == null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -603,7 +543,7 @@ public:
|
||||
*
|
||||
* @return An iterator to the first entity of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] iterator begin() const noexcept {
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
const auto pos = static_cast<typename iterator::difference_type>(packed.size());
|
||||
return iterator{packed, pos};
|
||||
}
|
||||
@@ -636,7 +576,7 @@ public:
|
||||
* @return An iterator to the first entity of the reversed internal packed
|
||||
* array.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterator rbegin() const noexcept {
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||
return std::make_reverse_iterator(end());
|
||||
}
|
||||
|
||||
@@ -659,53 +599,13 @@ public:
|
||||
return rend();
|
||||
}
|
||||
|
||||
/*! @copydoc begin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] iterator begin(int) const noexcept {
|
||||
return (mode == deletion_policy::swap_only) ? (end() - static_cast<typename iterator::difference_type>(head)) : begin();
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_iterator cbegin(int) const noexcept {
|
||||
return begin(0);
|
||||
}
|
||||
|
||||
/*! @copydoc end Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] iterator end(int) const noexcept {
|
||||
return end();
|
||||
}
|
||||
|
||||
/*! @copydoc cend Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_iterator cend(int) const noexcept {
|
||||
return end(0);
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] reverse_iterator rbegin(int) const noexcept {
|
||||
return std::make_reverse_iterator(end(0));
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_reverse_iterator crbegin(int) const noexcept {
|
||||
return rbegin(0);
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] reverse_iterator rend(int) const noexcept {
|
||||
return std::make_reverse_iterator(begin(0));
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin Useful only in case of swap-only policy. */
|
||||
[[nodiscard]] const_reverse_iterator crend(int) const noexcept {
|
||||
return rend(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an entity.
|
||||
* @param entt A valid identifier.
|
||||
* @return An iterator to the given entity if it's found, past the end
|
||||
* iterator otherwise.
|
||||
*/
|
||||
[[nodiscard]] const_iterator find(const entity_type entt) const noexcept {
|
||||
[[nodiscard]] iterator find(const entity_type entt) const noexcept {
|
||||
return contains(entt) ? to_iterator(entt) : end();
|
||||
}
|
||||
|
||||
@@ -716,10 +616,9 @@ public:
|
||||
*/
|
||||
[[nodiscard]] bool contains(const entity_type entt) const noexcept {
|
||||
const auto elem = sparse_ptr(entt);
|
||||
constexpr auto cap = traits_type::entity_mask;
|
||||
constexpr auto mask = traits_type::to_integral(null) & ~cap;
|
||||
constexpr auto cap = traits_type::to_entity(null);
|
||||
// testing versions permits to avoid accessing the packed array
|
||||
return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
|
||||
return elem && (((~cap & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -754,7 +653,7 @@ public:
|
||||
* @param pos The position for which to return the entity.
|
||||
* @return The entity at specified location if any, a null entity otherwise.
|
||||
*/
|
||||
[[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept {
|
||||
[[nodiscard]] entity_type at(const size_type pos) const noexcept {
|
||||
return pos < packed.size() ? packed[pos] : null;
|
||||
}
|
||||
|
||||
@@ -800,7 +699,7 @@ public:
|
||||
* `end()` iterator otherwise.
|
||||
*/
|
||||
iterator push(const entity_type entt, const void *elem = nullptr) {
|
||||
return try_emplace(entt, (mode == deletion_policy::swap_only), elem);
|
||||
return try_emplace(entt, false, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -923,26 +822,25 @@ public:
|
||||
|
||||
/*! @brief Removes all tombstones from a sparse set. */
|
||||
void compact() {
|
||||
if(mode == deletion_policy::in_place) {
|
||||
size_type from = packed.size();
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
underlying_type pos = std::exchange(head, traits_type::entity_mask);
|
||||
size_type from = packed.size();
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
|
||||
while(pos != traits_type::to_entity(null)) {
|
||||
if(const auto to = static_cast<size_type>(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) {
|
||||
--from;
|
||||
swap_or_move(from, to);
|
||||
for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[traits_type::to_entity(*it)])) {
|
||||
if(const size_type to = traits_type::to_entity(*it); to < from) {
|
||||
--from;
|
||||
swap_or_move(from, to);
|
||||
|
||||
packed[to] = packed[from];
|
||||
const auto entity = static_cast<typename traits_type::entity_type>(to);
|
||||
sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
|
||||
packed[to] = std::exchange(packed[from], tombstone);
|
||||
const auto entity = static_cast<typename traits_type::entity_type>(to);
|
||||
sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to]));
|
||||
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
}
|
||||
*it = traits_type::combine(static_cast<typename traits_type::entity_type>(from), tombstone);
|
||||
for(; from && packed[from - 1u] == tombstone; --from) {}
|
||||
}
|
||||
|
||||
packed.erase(packed.begin() + from, packed.end());
|
||||
}
|
||||
|
||||
free_list = tombstone;
|
||||
packed.resize(from);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -999,8 +897,8 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
|
||||
ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements");
|
||||
ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported");
|
||||
|
||||
algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
@@ -1034,27 +932,28 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
|
||||
sort_n(static_cast<size_type>(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
compact();
|
||||
sort_n(packed.size(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort entities according to their order in a range.
|
||||
* @brief Sort entities according to their order in another sparse set.
|
||||
*
|
||||
* Entities that are part of both the sparse set and the range are ordered
|
||||
* internally according to the order they have in the range.<br/>
|
||||
* All other entities goes to the end of the sparse set and there are no
|
||||
* Entities that are part of both the sparse sets are ordered internally
|
||||
* according to the order they have in `other`.<br/>
|
||||
* All the other entities goes to the end of the list and there are no
|
||||
* guarantees on their order.
|
||||
*
|
||||
* @tparam It Type of input iterator.
|
||||
* @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 other The sparse sets that imposes the order of the entities.
|
||||
*/
|
||||
template<typename It>
|
||||
void sort_as(It first, It last) {
|
||||
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed");
|
||||
void sort_as(const basic_sparse_set &other) {
|
||||
compact();
|
||||
|
||||
for(auto it = begin(0); it.index() && first != last; ++first) {
|
||||
if(const auto curr = *first; contains(curr)) {
|
||||
const auto to = other.end();
|
||||
auto from = other.begin();
|
||||
|
||||
for(auto it = begin(); it.index() && from != to; ++from) {
|
||||
if(const auto curr = *from; contains(curr)) {
|
||||
if(const auto entt = *it; entt != curr) {
|
||||
// basic no-leak guarantee (with invalid state) if swapping throws
|
||||
swap_elements(entt, curr);
|
||||
@@ -1065,20 +964,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @copybrief sort_as
|
||||
* @param other The sparse sets that imposes the order of the entities.
|
||||
*/
|
||||
[[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) {
|
||||
sort_as(other.begin(), other.end());
|
||||
}
|
||||
|
||||
/*! @brief Clears a sparse set. */
|
||||
void clear() {
|
||||
pop_all();
|
||||
// sanity check to avoid subtle issues due to storage classes
|
||||
ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set");
|
||||
head = policy_to_head();
|
||||
free_list = tombstone;
|
||||
packed.clear();
|
||||
}
|
||||
|
||||
@@ -1097,8 +988,8 @@ private:
|
||||
sparse_container_type sparse;
|
||||
packed_container_type packed;
|
||||
const type_info *info;
|
||||
entity_type free_list;
|
||||
deletion_policy mode;
|
||||
underlying_type head;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Container, std::size_t Size>
|
||||
@@ -157,12 +161,11 @@ public:
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
constexpr extended_storage_iterator()
|
||||
: it{} {}
|
||||
|
||||
constexpr extended_storage_iterator(iterator_type base, Other... other)
|
||||
constexpr extended_storage_iterator(It base, Other... other)
|
||||
: it{base, other...} {}
|
||||
|
||||
template<typename... Args, typename = std::enable_if_t<(!std::is_same_v<Other, Args> && ...) && (std::is_constructible_v<Other, Args> && ...)>>
|
||||
@@ -208,7 +211,11 @@ template<typename... Lhs, typename... Rhs>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic storage implementation.
|
||||
@@ -233,6 +240,8 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
|
||||
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
using underlying_iterator = typename underlying_type::basic_iterator;
|
||||
|
||||
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
|
||||
|
||||
[[nodiscard]] auto &element_at(const std::size_t pos) const {
|
||||
return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)];
|
||||
}
|
||||
@@ -281,7 +290,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
|
||||
|
||||
for(auto pos = sz, length = base_type::size(); pos < length; ++pos) {
|
||||
if constexpr(traits_type::in_place_delete) {
|
||||
if(base_type::data()[pos] != tombstone) {
|
||||
if(base_type::at(pos) != tombstone) {
|
||||
alloc_traits::destroy(allocator, std::addressof(element_at(pos)));
|
||||
}
|
||||
} else {
|
||||
@@ -302,7 +311,6 @@ private:
|
||||
}
|
||||
|
||||
void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override {
|
||||
static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>);
|
||||
// use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy
|
||||
ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type");
|
||||
|
||||
@@ -833,12 +841,7 @@ public:
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
// std::allocator<void> has no cross constructors (waiting for C++20)
|
||||
if constexpr(std::is_void_v<value_type> && !std::is_constructible_v<allocator_type, typename base_type::allocator_type>) {
|
||||
return allocator_type{};
|
||||
} else {
|
||||
return allocator_type{base_type::get_allocator()};
|
||||
}
|
||||
return allocator_type{base_type::get_allocator()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -955,13 +958,42 @@ class basic_storage<Entity, Entity, Allocator>
|
||||
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
|
||||
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
|
||||
using underlying_iterator = typename underlying_type::basic_iterator;
|
||||
using local_traits_type = entt_traits<Entity>;
|
||||
|
||||
auto entity_at(const std::size_t pos) const noexcept {
|
||||
ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element");
|
||||
return underlying_type::traits_type::combine(static_cast<typename underlying_type::traits_type::entity_type>(pos), {});
|
||||
ENTT_ASSERT(pos < local_traits_type::to_entity(null), "Invalid element");
|
||||
return local_traits_type::combine(static_cast<typename local_traits_type::entity_type>(pos), {});
|
||||
}
|
||||
|
||||
private:
|
||||
void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) override {
|
||||
ENTT_ASSERT(((lhs < length) + (rhs < length)) != 1u, "Cross swapping is not supported");
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Erases entities from a 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.
|
||||
*/
|
||||
void pop(underlying_iterator first, underlying_iterator last) override {
|
||||
for(; first != last; ++first) {
|
||||
if(const auto pos = base_type::index(*first); pos < length) {
|
||||
base_type::bump(local_traits_type::next(*first));
|
||||
|
||||
if(pos != --length) {
|
||||
base_type::swap_at(pos, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! @brief Erases all entities of a sparse set. */
|
||||
void pop_all() override {
|
||||
length = 0u;
|
||||
base_type::pop_all();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns an entity to a storage.
|
||||
* @param hint A valid identifier.
|
||||
@@ -976,6 +1008,8 @@ public:
|
||||
using base_type = basic_sparse_set<Entity, Allocator>;
|
||||
/*! @brief Type of the objects assigned to entities. */
|
||||
using value_type = Entity;
|
||||
/*! @brief Component traits. */
|
||||
using traits_type = component_traits<value_type>;
|
||||
/*! @brief Underlying entity identifier. */
|
||||
using entity_type = Entity;
|
||||
/*! @brief Unsigned integer type. */
|
||||
@@ -1001,14 +1035,16 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_storage(const allocator_type &allocator)
|
||||
: base_type{type_id<void>(), deletion_policy::swap_only, allocator} {}
|
||||
: base_type{type_id<value_type>(), deletion_policy::swap_and_pop, allocator},
|
||||
length{} {}
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_storage(basic_storage &&other) noexcept
|
||||
: base_type{std::move(other)} {}
|
||||
: base_type{std::move(other)},
|
||||
length{std::exchange(other.length, size_type{})} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
@@ -1016,7 +1052,8 @@ public:
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
|
||||
: base_type{std::move(other), allocator} {}
|
||||
: base_type{std::move(other), allocator},
|
||||
length{std::exchange(other.length, size_type{})} {}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
@@ -1025,6 +1062,7 @@ public:
|
||||
*/
|
||||
basic_storage &operator=(basic_storage &&other) noexcept {
|
||||
base_type::operator=(std::move(other));
|
||||
length = std::exchange(other.length, size_type{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1038,7 +1076,7 @@ public:
|
||||
* @param entt A valid identifier.
|
||||
*/
|
||||
void get([[maybe_unused]] const entity_type entt) const noexcept {
|
||||
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
|
||||
ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1052,18 +1090,30 @@ public:
|
||||
* @return Returns an empty tuple.
|
||||
*/
|
||||
[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept {
|
||||
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
|
||||
ENTT_ASSERT(base_type::index(entt) < length, "The requested entity is not a live one");
|
||||
return std::tuple{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given storage.
|
||||
* @param other Storage to exchange the content with.
|
||||
*/
|
||||
void swap(basic_storage &other) {
|
||||
using std::swap;
|
||||
base_type::swap(other);
|
||||
swap(length, other.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates a new identifier or recycles a destroyed one.
|
||||
* @return A valid identifier.
|
||||
*/
|
||||
entity_type emplace() {
|
||||
const auto len = base_type::free_list();
|
||||
const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len];
|
||||
return *base_type::try_emplace(entt, true);
|
||||
if(length == base_type::size()) {
|
||||
return *base_type::try_emplace(entity_at(length++), true);
|
||||
}
|
||||
|
||||
return base_type::operator[](length++);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1078,20 +1128,23 @@ public:
|
||||
entity_type emplace(const entity_type hint) {
|
||||
if(hint == null || hint == tombstone) {
|
||||
return emplace();
|
||||
} else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
|
||||
const auto pos = static_cast<size_type>(underlying_type::traits_type::to_entity(hint));
|
||||
const auto entt = *base_type::try_emplace(hint, true);
|
||||
} else if(const auto curr = local_traits_type::construct(local_traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) {
|
||||
const auto pos = static_cast<size_type>(local_traits_type::to_entity(hint));
|
||||
|
||||
while(!(pos < base_type::size())) {
|
||||
base_type::try_emplace(entity_at(base_type::size() - 1u), false);
|
||||
base_type::try_emplace(entity_at(base_type::size()), true);
|
||||
}
|
||||
|
||||
return entt;
|
||||
} else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) {
|
||||
base_type::swap_at(pos, length++);
|
||||
} else if(const auto idx = base_type::index(curr); idx < length) {
|
||||
return emplace();
|
||||
} else {
|
||||
return *base_type::try_emplace(hint, true);
|
||||
base_type::swap_at(idx, length++);
|
||||
}
|
||||
|
||||
base_type::bump(hint);
|
||||
|
||||
return hint;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1102,7 +1155,7 @@ public:
|
||||
*/
|
||||
template<typename... Func>
|
||||
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
|
||||
ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one");
|
||||
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
|
||||
(std::forward<Func>(func)(), ...);
|
||||
}
|
||||
|
||||
@@ -1114,12 +1167,12 @@ public:
|
||||
*/
|
||||
template<typename It>
|
||||
void insert(It first, It last) {
|
||||
for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) {
|
||||
*first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true);
|
||||
for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) {
|
||||
*first = base_type::operator[](length);
|
||||
}
|
||||
|
||||
for(; first != last; ++first) {
|
||||
*first = *base_type::try_emplace(entity_at(base_type::free_list()), true);
|
||||
*first = *base_type::try_emplace(entity_at(length++), true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1131,25 +1184,33 @@ public:
|
||||
* @return The number of elements within the newly created range.
|
||||
*/
|
||||
template<typename It>
|
||||
[[deprecated("use sort_as instead")]] size_type pack(It first, It last) {
|
||||
base_type::sort_as(first, last);
|
||||
return static_cast<size_type>(std::distance(first, last));
|
||||
size_type pack(It first, It last) {
|
||||
size_type len = length;
|
||||
|
||||
for(; first != last; ++first, --len) {
|
||||
const auto pos = base_type::index(*first);
|
||||
ENTT_ASSERT(pos < length, "Invalid element");
|
||||
base_type::swap_at(pos, static_cast<size_type>(len - 1u));
|
||||
}
|
||||
|
||||
return (length - len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements considered still in use.
|
||||
* @return The number of elements considered still in use.
|
||||
*/
|
||||
[[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept {
|
||||
return base_type::free_list();
|
||||
[[nodiscard]] size_type in_use() const noexcept {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the number of elements considered still in use.
|
||||
* @param len The number of elements considered still in use.
|
||||
*/
|
||||
[[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept {
|
||||
base_type::free_list(len);
|
||||
void in_use(const size_type len) noexcept {
|
||||
ENTT_ASSERT(!(len > base_type::size()), "Invalid length");
|
||||
length = len;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1160,12 +1221,12 @@ public:
|
||||
* @return An iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] iterable each() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}};
|
||||
return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}};
|
||||
}
|
||||
|
||||
/*! @copydoc each */
|
||||
[[nodiscard]] const_iterable each() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}};
|
||||
return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1176,13 +1237,16 @@ public:
|
||||
* @return A reverse iterable object to use to _visit_ the storage.
|
||||
*/
|
||||
[[nodiscard]] reverse_iterable reach() noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}};
|
||||
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rbegin() + length}};
|
||||
}
|
||||
|
||||
/*! @copydoc reach */
|
||||
[[nodiscard]] const_reverse_iterable reach() const noexcept {
|
||||
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}};
|
||||
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crbegin() + length}};
|
||||
}
|
||||
|
||||
private:
|
||||
size_type length;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,7 +13,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename It>
|
||||
@@ -26,7 +30,6 @@ 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 edge_iterator() noexcept
|
||||
: it{},
|
||||
@@ -84,7 +87,11 @@ template<typename Container>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a directed adjacency matrix.
|
||||
|
||||
@@ -134,8 +134,8 @@ public:
|
||||
*/
|
||||
explicit basic_flow(const allocator_type &allocator)
|
||||
: index{0u, allocator},
|
||||
vertices{allocator},
|
||||
deps{allocator},
|
||||
vertices{},
|
||||
deps{},
|
||||
sync_on{} {}
|
||||
|
||||
/*! @brief Default copy constructor. */
|
||||
|
||||
@@ -19,292 +19,153 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename, typename = void>
|
||||
struct fixed_size_sequence_container: std::true_type {};
|
||||
struct is_dynamic_sequence_container: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct fixed_size_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container<Type>::value;
|
||||
struct is_dynamic_sequence_container<Type, std::void_t<decltype(&Type::clear)>>: std::true_type {};
|
||||
|
||||
template<typename, typename = void>
|
||||
struct key_only_associative_container: std::true_type {};
|
||||
struct is_key_only_meta_associative_container: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct key_only_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
|
||||
struct is_key_only_meta_associative_container<Type, std::void_t<typename Type::mapped_type>>: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool key_only_associative_container_v = key_only_associative_container<Type>::value;
|
||||
|
||||
template<typename, typename = void>
|
||||
struct reserve_aware_container: std::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct reserve_aware_container<Type, std::void_t<decltype(&Type::reserve)>>: std::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool reserve_aware_container_v = reserve_aware_container<Type>::value;
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief General purpose implementation of meta sequence container traits.
|
||||
* @tparam Type Type of underlying sequence container.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct basic_meta_sequence_container_traits {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
using iterator = meta_sequence_container::iterator;
|
||||
using size_type = std::size_t;
|
||||
|
||||
/*! @brief True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
|
||||
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename meta_sequence_container::size_type;
|
||||
/*! @brief Meta iterator type. */
|
||||
using iterator = typename meta_sequence_container::iterator;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] static size_type size(const void *container) {
|
||||
return static_cast<const Type *>(container)->size();
|
||||
[[nodiscard]] static size_type size(const any &container) noexcept {
|
||||
return any_cast<const Type &>(container).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool clear([[maybe_unused]] void *container) {
|
||||
if constexpr(fixed_size) {
|
||||
return false;
|
||||
} else {
|
||||
static_cast<Type *>(container)->clear();
|
||||
return true;
|
||||
[[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
cont->resize(sz);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param sz Desired capacity.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(internal::reserve_aware_container_v<Type>) {
|
||||
static_cast<Type *>(container)->reserve(sz);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
[[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{ctx, as_end ? cont->end() : cont->begin()};
|
||||
}
|
||||
|
||||
const Type &as_const = any_cast<const Type &>(container);
|
||||
return iterator{ctx, as_end ? as_const.end() : as_const.begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Resizes a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param sz The new number of elements.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(fixed_size || !std::is_default_constructible_v<typename Type::value_type>) {
|
||||
return false;
|
||||
} else {
|
||||
static_cast<Type *>(container)->resize(sz);
|
||||
return true;
|
||||
[[nodiscard]] static iterator insert_or_erase([[maybe_unused]] const meta_ctx &ctx, [[maybe_unused]] any &container, [[maybe_unused]] const any &handle, [[maybe_unused]] meta_any &value) {
|
||||
if constexpr(is_dynamic_sequence_container<Type>::value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
typename Type::const_iterator it{};
|
||||
|
||||
if(auto *non_const = any_cast<typename Type::iterator>(&handle); non_const) {
|
||||
it = *non_const;
|
||||
} else {
|
||||
it = any_cast<const typename Type::const_iterator &>(handle);
|
||||
}
|
||||
|
||||
if(value) {
|
||||
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
|
||||
if(value.allow_cast<typename Type::const_reference>() || value.allow_cast<typename Type::value_type>()) {
|
||||
const auto *element = value.try_cast<std::remove_reference_t<typename Type::const_reference>>();
|
||||
return iterator{ctx, cont->insert(it, element ? *element : value.cast<typename Type::value_type>())};
|
||||
}
|
||||
} else {
|
||||
return iterator{ctx, cont->erase(it)};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the beginning.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, static_cast<const Type *>(as_const)->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns one element to a container and constructs its object from
|
||||
* a given opaque instance.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param value Optional opaque instance of the object to construct (as
|
||||
* value type).
|
||||
* @param cref Optional opaque instance of the object to construct (as
|
||||
* decayed const reference type).
|
||||
* @param it Iterator before which the element will be inserted.
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
[[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{area};
|
||||
} else {
|
||||
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
|
||||
return {area, static_cast<Type *>(container)->insert(
|
||||
non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()),
|
||||
value ? *static_cast<const typename Type::value_type *>(value) : *static_cast<const std::remove_reference_t<typename Type::const_reference> *>(cref))};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases an element from a container.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param it An opaque iterator to the element to erase.
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
[[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
|
||||
if constexpr(fixed_size) {
|
||||
return iterator{area};
|
||||
} else {
|
||||
auto *const non_const = any_cast<typename Type::iterator>(&it.base());
|
||||
return {area, static_cast<Type *>(container)->erase(non_const ? *non_const : any_cast<const typename Type::const_iterator &>(it.base()))};
|
||||
}
|
||||
return iterator{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief General purpose implementation of meta associative container traits.
|
||||
* @tparam Type Type of underlying associative container.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct basic_meta_associative_container_traits {
|
||||
static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Unexpected type");
|
||||
using iterator = meta_associative_container::iterator;
|
||||
using size_type = std::size_t;
|
||||
|
||||
/*! @brief True in case of key-only containers, false otherwise. */
|
||||
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
|
||||
static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
|
||||
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = typename meta_associative_container::size_type;
|
||||
/*! @brief Meta iterator type. */
|
||||
using iterator = typename meta_associative_container::iterator;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements in a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return Number of elements.
|
||||
*/
|
||||
[[nodiscard]] static size_type size(const void *container) {
|
||||
return static_cast<const Type *>(container)->size();
|
||||
[[nodiscard]] static size_type size(const any &container) noexcept {
|
||||
return any_cast<const Type &>(container).size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Clears a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool clear(void *container) {
|
||||
static_cast<Type *>(container)->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Increases the capacity of a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param sz Desired capacity.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) {
|
||||
if constexpr(internal::reserve_aware_container_v<Type>) {
|
||||
static_cast<Type *>(container)->reserve(sz);
|
||||
[[nodiscard]] static bool clear(any &container) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
cont->clear();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the beginning.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
static iterator begin(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->begin()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a possibly const iterator to the end.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
static iterator end(const meta_ctx &area, void *container, const void *as_const) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->end()}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->end()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts an element into a container, if the key does not exist.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param key An opaque key value of an element to insert.
|
||||
* @param value Optional opaque value to insert (key-value containers).
|
||||
* @return True if the insertion took place, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) {
|
||||
if constexpr(key_only) {
|
||||
return static_cast<Type *>(container)->insert(*static_cast<const typename Type::key_type *>(key)).second;
|
||||
} else {
|
||||
return static_cast<Type *>(container)->emplace(*static_cast<const typename Type::key_type *>(key), *static_cast<const typename Type::mapped_type *>(value)).second;
|
||||
[[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, as_end ? cont->end() : cont->begin()};
|
||||
}
|
||||
|
||||
const auto &as_const = any_cast<const Type &>(container);
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, as_end ? as_const.end() : as_const.begin()};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes an element from a container.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param key An opaque key value of an element to remove.
|
||||
* @return Number of elements removed (either 0 or 1).
|
||||
*/
|
||||
[[nodiscard]] static size_type erase(void *container, const void *key) {
|
||||
return static_cast<Type *>(container)->erase(*static_cast<const typename Type::key_type *>(key));
|
||||
[[nodiscard]] static size_type insert_or_erase(any &container, meta_any &key, meta_any &value) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont && key.allow_cast<const typename Type::key_type &>()) {
|
||||
if(value) {
|
||||
if constexpr(key_only) {
|
||||
return cont->insert(key.cast<const typename Type::key_type &>()).second;
|
||||
} else {
|
||||
return value.allow_cast<const typename Type::mapped_type &>() && cont->emplace(key.cast<const typename Type::key_type &>(), value.cast<const typename Type::mapped_type &>()).second;
|
||||
}
|
||||
} else {
|
||||
return cont->erase(key.cast<const typename Type::key_type &>());
|
||||
}
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Finds an element with a given key.
|
||||
* @param area The context to pass to the newly created iterator.
|
||||
* @param container Opaque pointer to a container of the given type.
|
||||
* @param as_const Const opaque pointer fallback.
|
||||
* @param key Opaque key value of an element to search for.
|
||||
* @return An iterator to the element with the given key, if any.
|
||||
*/
|
||||
static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) {
|
||||
return container ? iterator{area, std::bool_constant<key_only>{}, static_cast<Type *>(container)->find(*static_cast<const typename Type::key_type *>(key))}
|
||||
: iterator{area, std::bool_constant<key_only>{}, static_cast<const Type *>(as_const)->find(*static_cast<const typename Type::key_type *>(key))};
|
||||
[[nodiscard]] static iterator find(const meta_ctx &ctx, any &container, meta_any &key) {
|
||||
if(key.allow_cast<const typename Type::key_type &>()) {
|
||||
if(auto *const cont = any_cast<Type>(&container); cont) {
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, cont->find(key.cast<const typename Type::key_type &>())};
|
||||
}
|
||||
|
||||
return iterator{ctx, std::bool_constant<key_only>{}, any_cast<const Type &>(container).find(key.cast<const typename Type::key_type &>())};
|
||||
}
|
||||
|
||||
return iterator{};
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::vector`s of any type.
|
||||
* @tparam Args Template arguments for the container.
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_sequence_container_traits<std::vector<Args...>>
|
||||
: basic_meta_sequence_container_traits<std::vector<Args...>> {};
|
||||
: internal::basic_meta_sequence_container_traits<std::vector<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::array`s of any type.
|
||||
@@ -313,7 +174,7 @@ struct meta_sequence_container_traits<std::vector<Args...>>
|
||||
*/
|
||||
template<typename Type, auto N>
|
||||
struct meta_sequence_container_traits<std::array<Type, N>>
|
||||
: basic_meta_sequence_container_traits<std::array<Type, N>> {};
|
||||
: internal::basic_meta_sequence_container_traits<std::array<Type, N>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::list`s of any type.
|
||||
@@ -321,7 +182,7 @@ struct meta_sequence_container_traits<std::array<Type, N>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_sequence_container_traits<std::list<Args...>>
|
||||
: basic_meta_sequence_container_traits<std::list<Args...>> {};
|
||||
: internal::basic_meta_sequence_container_traits<std::list<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta sequence container traits for `std::deque`s of any type.
|
||||
@@ -329,7 +190,7 @@ struct meta_sequence_container_traits<std::list<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_sequence_container_traits<std::deque<Args...>>
|
||||
: basic_meta_sequence_container_traits<std::deque<Args...>> {};
|
||||
: internal::basic_meta_sequence_container_traits<std::deque<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::map`s of any type.
|
||||
@@ -337,7 +198,7 @@ struct meta_sequence_container_traits<std::deque<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::map<Args...>>
|
||||
: basic_meta_associative_container_traits<std::map<Args...>> {};
|
||||
: internal::basic_meta_associative_container_traits<std::map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::unordered_map`s of any
|
||||
@@ -346,7 +207,7 @@ struct meta_associative_container_traits<std::map<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_map<Args...>>
|
||||
: basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
|
||||
: internal::basic_meta_associative_container_traits<std::unordered_map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::set`s of any type.
|
||||
@@ -354,7 +215,7 @@ struct meta_associative_container_traits<std::unordered_map<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::set<Args...>>
|
||||
: basic_meta_associative_container_traits<std::set<Args...>> {};
|
||||
: internal::basic_meta_associative_container_traits<std::set<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `std::unordered_set`s of any
|
||||
@@ -363,7 +224,7 @@ struct meta_associative_container_traits<std::set<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<std::unordered_set<Args...>>
|
||||
: basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
|
||||
: internal::basic_meta_associative_container_traits<std::unordered_set<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `dense_map`s of any type.
|
||||
@@ -371,7 +232,7 @@ struct meta_associative_container_traits<std::unordered_set<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<dense_map<Args...>>
|
||||
: basic_meta_associative_container_traits<dense_map<Args...>> {};
|
||||
: internal::basic_meta_associative_container_traits<dense_map<Args...>> {};
|
||||
|
||||
/**
|
||||
* @brief Meta associative container traits for `dense_set`s of any type.
|
||||
@@ -379,7 +240,7 @@ struct meta_associative_container_traits<dense_map<Args...>>
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct meta_associative_container_traits<dense_set<Args...>>
|
||||
: basic_meta_associative_container_traits<dense_set<Args...>> {};
|
||||
: internal::basic_meta_associative_container_traits<dense_set<Args...>> {};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -9,7 +9,11 @@ namespace entt {
|
||||
|
||||
class meta_ctx;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct meta_type_node;
|
||||
@@ -17,12 +21,16 @@ struct meta_type_node;
|
||||
struct meta_context {
|
||||
dense_map<id_type, meta_type_node, identity> value{};
|
||||
|
||||
[[nodiscard]] inline static meta_context &from(meta_ctx &ctx);
|
||||
[[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx);
|
||||
[[nodiscard]] static inline meta_context &from(meta_ctx &ctx);
|
||||
[[nodiscard]] static inline const meta_context &from(const meta_ctx &ctx);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/*! @brief Disambiguation tag for constructors and the like. */
|
||||
class meta_ctx_arg_t final {};
|
||||
@@ -32,11 +40,15 @@ inline constexpr meta_ctx_arg_t meta_ctx_arg{};
|
||||
|
||||
/*! @brief Opaque meta context type. */
|
||||
class meta_ctx: private internal::meta_context {
|
||||
// attorney idiom like model to access the base class
|
||||
/*! @brief Attorney idiom like model to access the base class. */
|
||||
friend struct internal::meta_context;
|
||||
};
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) {
|
||||
return ctx;
|
||||
}
|
||||
@@ -44,7 +56,11 @@ class meta_ctx: private internal::meta_context {
|
||||
[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) {
|
||||
return ctx;
|
||||
}
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -22,7 +22,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
|
||||
@@ -53,7 +57,11 @@ inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_ty
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic meta factory to be used for reflection purposes.
|
||||
@@ -107,7 +115,7 @@ public:
|
||||
/**
|
||||
* @brief Assigns a custom unique identifier to a meta type.
|
||||
* @param id A custom unique identifier.
|
||||
* @return A meta factory for the given type.
|
||||
* @return An extended meta factory for the given type.
|
||||
*/
|
||||
auto type(const id_type id) noexcept {
|
||||
auto &&elem = internal::owner(*ctx, *info);
|
||||
@@ -184,7 +192,7 @@ public:
|
||||
*
|
||||
* @tparam Candidate The actual function to use as a constructor.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto ctor() noexcept {
|
||||
@@ -204,7 +212,7 @@ public:
|
||||
* type that can be invoked with parameters whose types are those given.
|
||||
*
|
||||
* @tparam Args Types of arguments to use to construct an instance.
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<typename... Args>
|
||||
auto ctor() noexcept {
|
||||
@@ -256,7 +264,7 @@ public:
|
||||
* @tparam Data The actual variable to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Data, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
@@ -321,7 +329,7 @@ public:
|
||||
* @tparam Getter The actual function to use as a getter.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Setter, auto Getter, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
@@ -378,7 +386,7 @@ public:
|
||||
* @tparam Getter The actual getter function.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<typename Setter, auto Getter, typename Policy = as_is_t>
|
||||
auto data(const id_type id) noexcept {
|
||||
@@ -397,7 +405,7 @@ public:
|
||||
* @tparam Candidate The actual function to attach to the meta type.
|
||||
* @tparam Policy Optional policy (no policy set by default).
|
||||
* @param id Unique identifier.
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the parent type.
|
||||
*/
|
||||
template<auto Candidate, typename Policy = as_is_t>
|
||||
auto func(const id_type id) noexcept {
|
||||
@@ -426,7 +434,7 @@ public:
|
||||
* @tparam Value Optional type of the property value.
|
||||
* @param id Property key.
|
||||
* @param value Optional property value.
|
||||
* @return A meta factory for the parent type.
|
||||
* @return An extended meta factory for the given type.
|
||||
*/
|
||||
template<typename... Value>
|
||||
meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
|
||||
|
||||
@@ -36,15 +36,11 @@ public:
|
||||
/*! @brief Meta iterator type. */
|
||||
using iterator = meta_iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_sequence_container() noexcept
|
||||
: meta_sequence_container{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
* @param area The context from which to search for meta types.
|
||||
*/
|
||||
meta_sequence_container(const meta_ctx &area) noexcept
|
||||
meta_sequence_container(const meta_ctx &area = locator<meta_ctx>::value_or()) noexcept
|
||||
: ctx{&area} {}
|
||||
|
||||
/**
|
||||
@@ -53,26 +49,19 @@ public:
|
||||
* @param instance The container to wrap.
|
||||
*/
|
||||
template<typename Type>
|
||||
void rebind(Type &instance) noexcept {
|
||||
void rebind(any instance) noexcept {
|
||||
value_type_node = &internal::resolve<typename Type::value_type>;
|
||||
const_reference_node = &internal::resolve<std::remove_const_t<std::remove_reference_t<typename Type::const_reference>>>;
|
||||
size_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::size;
|
||||
clear_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::clear;
|
||||
reserve_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::reserve;
|
||||
resize_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::resize;
|
||||
begin_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::begin;
|
||||
end_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::end;
|
||||
insert_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::insert;
|
||||
erase_fn = meta_sequence_container_traits<std::remove_const_t<Type>>::erase;
|
||||
const_only = std::is_const_v<Type>;
|
||||
data = &instance;
|
||||
size_fn = &meta_sequence_container_traits<Type>::size;
|
||||
resize_fn = &meta_sequence_container_traits<Type>::resize;
|
||||
iter_fn = &meta_sequence_container_traits<Type>::iter;
|
||||
insert_or_erase_fn = &meta_sequence_container_traits<Type>::insert_or_erase;
|
||||
storage = std::move(instance);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline meta_type value_type() const noexcept;
|
||||
[[nodiscard]] inline size_type size() const noexcept;
|
||||
inline bool resize(const size_type);
|
||||
inline bool clear();
|
||||
inline bool reserve(const size_type);
|
||||
[[nodiscard]] inline iterator begin();
|
||||
[[nodiscard]] inline iterator end();
|
||||
inline iterator insert(iterator, meta_any);
|
||||
@@ -83,17 +72,11 @@ public:
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
internal::meta_type_node (*value_type_node)(const internal::meta_context &){};
|
||||
internal::meta_type_node (*const_reference_node)(const internal::meta_context &){};
|
||||
size_type (*size_fn)(const void *){};
|
||||
bool (*clear_fn)(void *){};
|
||||
bool (*reserve_fn)(void *, const size_type){};
|
||||
bool (*resize_fn)(void *, const size_type){};
|
||||
iterator (*begin_fn)(const meta_ctx &, void *, const void *){};
|
||||
iterator (*end_fn)(const meta_ctx &, void *, const void *){};
|
||||
iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){};
|
||||
iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){};
|
||||
const void *data{};
|
||||
bool const_only{};
|
||||
size_type (*size_fn)(const any &) noexcept {};
|
||||
bool (*resize_fn)(any &, size_type){};
|
||||
iterator (*iter_fn)(const meta_ctx &, any &, const bool){};
|
||||
iterator (*insert_or_erase_fn)(const meta_ctx &, any &, const any &, meta_any &){};
|
||||
any storage{};
|
||||
};
|
||||
|
||||
/*! @brief Proxy object for associative containers. */
|
||||
@@ -106,15 +89,11 @@ public:
|
||||
/*! @brief Meta iterator type. */
|
||||
using iterator = meta_iterator;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
meta_associative_container() noexcept
|
||||
: meta_associative_container{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
/**
|
||||
* @brief Context aware constructor.
|
||||
* @param area The context from which to search for meta types.
|
||||
*/
|
||||
meta_associative_container(const meta_ctx &area) noexcept
|
||||
meta_associative_container(const meta_ctx &area = locator<meta_ctx>::value_or()) noexcept
|
||||
: ctx{&area} {}
|
||||
|
||||
/**
|
||||
@@ -123,24 +102,20 @@ public:
|
||||
* @param instance The container to wrap.
|
||||
*/
|
||||
template<typename Type>
|
||||
void rebind(Type &instance) noexcept {
|
||||
key_type_node = &internal::resolve<typename Type::key_type>;
|
||||
value_type_node = &internal::resolve<typename Type::value_type>;
|
||||
|
||||
if constexpr(!meta_associative_container_traits<std::remove_const_t<Type>>::key_only) {
|
||||
void rebind(any instance) noexcept {
|
||||
if constexpr(!meta_associative_container_traits<Type>::key_only) {
|
||||
mapped_type_node = &internal::resolve<typename Type::mapped_type>;
|
||||
}
|
||||
|
||||
size_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::size;
|
||||
clear_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::clear;
|
||||
reserve_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::reserve;
|
||||
begin_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::begin;
|
||||
end_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::end;
|
||||
insert_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::insert;
|
||||
erase_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::erase;
|
||||
find_fn = &meta_associative_container_traits<std::remove_const_t<Type>>::find;
|
||||
const_only = std::is_const_v<Type>;
|
||||
data = &instance;
|
||||
key_only_container = meta_associative_container_traits<Type>::key_only;
|
||||
key_type_node = &internal::resolve<typename Type::key_type>;
|
||||
value_type_node = &internal::resolve<typename Type::value_type>;
|
||||
size_fn = &meta_associative_container_traits<Type>::size;
|
||||
clear_fn = &meta_associative_container_traits<Type>::clear;
|
||||
iter_fn = &meta_associative_container_traits<Type>::iter;
|
||||
insert_or_erase_fn = &meta_associative_container_traits<Type>::insert_or_erase;
|
||||
find_fn = &meta_associative_container_traits<Type>::find;
|
||||
storage = std::move(instance);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool key_only() const noexcept;
|
||||
@@ -149,9 +124,9 @@ public:
|
||||
[[nodiscard]] inline meta_type value_type() const noexcept;
|
||||
[[nodiscard]] inline size_type size() const noexcept;
|
||||
inline bool clear();
|
||||
inline bool reserve(const size_type);
|
||||
[[nodiscard]] inline iterator begin();
|
||||
[[nodiscard]] inline iterator end();
|
||||
inline bool insert(meta_any);
|
||||
inline bool insert(meta_any, meta_any);
|
||||
inline size_type erase(meta_any);
|
||||
[[nodiscard]] inline iterator find(meta_any);
|
||||
@@ -159,63 +134,67 @@ public:
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
bool key_only_container{};
|
||||
internal::meta_type_node (*key_type_node)(const internal::meta_context &){};
|
||||
internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){};
|
||||
internal::meta_type_node (*value_type_node)(const internal::meta_context &){};
|
||||
size_type (*size_fn)(const void *){};
|
||||
bool (*clear_fn)(void *){};
|
||||
bool (*reserve_fn)(void *, const size_type){};
|
||||
iterator (*begin_fn)(const meta_ctx &, void *, const void *){};
|
||||
iterator (*end_fn)(const meta_ctx &, void *, const void *){};
|
||||
bool (*insert_fn)(void *, const void *, const void *){};
|
||||
size_type (*erase_fn)(void *, const void *){};
|
||||
iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){};
|
||||
const void *data{};
|
||||
bool const_only{};
|
||||
size_type (*size_fn)(const any &) noexcept {};
|
||||
bool (*clear_fn)(any &){};
|
||||
iterator (*iter_fn)(const meta_ctx &, any &, const bool){};
|
||||
size_type (*insert_or_erase_fn)(any &, meta_any &, meta_any &){};
|
||||
iterator (*find_fn)(const meta_ctx &, any &, meta_any &){};
|
||||
any storage{};
|
||||
};
|
||||
|
||||
/*! @brief Possible modes of a meta any object. */
|
||||
using meta_any_policy = any_policy;
|
||||
|
||||
/*! @brief Opaque wrapper for values of any type. */
|
||||
class meta_any {
|
||||
using vtable_type = void(const internal::meta_traits op, const bool, const void *, void *);
|
||||
enum class operation : std::uint8_t {
|
||||
deref,
|
||||
seq,
|
||||
assoc
|
||||
};
|
||||
|
||||
using vtable_type = void(const operation, const any &, void *);
|
||||
|
||||
template<typename Type>
|
||||
static std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>> basic_vtable([[maybe_unused]] const internal::meta_traits req, [[maybe_unused]] const bool const_only, [[maybe_unused]] const void *value, [[maybe_unused]] void *other) {
|
||||
if constexpr(is_meta_pointer_like_v<Type>) {
|
||||
if(req == internal::meta_traits::is_meta_pointer_like) {
|
||||
if constexpr(std::is_function_v<typename std::pointer_traits<Type>::element_type>) {
|
||||
static_cast<meta_any *>(other)->emplace<Type>(*static_cast<const Type *>(value));
|
||||
} else if constexpr(!std::is_void_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>>) {
|
||||
using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(*static_cast<const Type *>(value)));
|
||||
static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) {
|
||||
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
|
||||
|
||||
if constexpr(std::is_constructible_v<bool, Type>) {
|
||||
if(const auto &pointer_like = *static_cast<const Type *>(value); pointer_like) {
|
||||
static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like));
|
||||
if constexpr(!std::is_void_v<Type>) {
|
||||
switch(op) {
|
||||
case operation::deref:
|
||||
if constexpr(is_meta_pointer_like_v<Type>) {
|
||||
if constexpr(std::is_function_v<typename std::pointer_traits<Type>::element_type>) {
|
||||
static_cast<meta_any *>(other)->emplace<Type>(any_cast<Type>(value));
|
||||
} else if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) {
|
||||
using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value)));
|
||||
|
||||
if constexpr(std::is_constructible_v<bool, Type>) {
|
||||
if(const auto &pointer_like = any_cast<const Type &>(value); pointer_like) {
|
||||
static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(pointer_like));
|
||||
}
|
||||
} else {
|
||||
static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value)));
|
||||
}
|
||||
} else {
|
||||
static_cast<meta_any *>(other)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(*static_cast<const Type *>(value)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) {
|
||||
if(req == internal::meta_traits::is_meta_sequence_container) {
|
||||
const_only ? static_cast<meta_sequence_container *>(other)->rebind(*static_cast<const Type *>(value)) : static_cast<meta_sequence_container *>(other)->rebind(*static_cast<Type *>(const_cast<void *>(value)));
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr(is_complete_v<meta_associative_container_traits<Type>>) {
|
||||
if(req == internal::meta_traits::is_meta_associative_container) {
|
||||
const_only ? static_cast<meta_associative_container *>(other)->rebind(*static_cast<const Type *>(value)) : static_cast<meta_associative_container *>(other)->rebind(*static_cast<Type *>(const_cast<void *>(value)));
|
||||
break;
|
||||
case operation::seq:
|
||||
if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) {
|
||||
static_cast<meta_sequence_container *>(other)->rebind<Type>(std::move(const_cast<any &>(value)));
|
||||
}
|
||||
break;
|
||||
case operation::assoc:
|
||||
if constexpr(is_complete_v<meta_associative_container_traits<Type>>) {
|
||||
static_cast<meta_associative_container *>(other)->rebind<Type>(std::move(const_cast<any &>(value)));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void release() {
|
||||
if(node.dtor.dtor && (storage.policy() == any_policy::owner)) {
|
||||
if(node.dtor.dtor && owner()) {
|
||||
node.dtor.dtor(storage.data());
|
||||
}
|
||||
}
|
||||
@@ -439,6 +418,10 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Tries to cast an instance to a given type.
|
||||
*
|
||||
* @warning
|
||||
* Attempting to perform an invalid cast results is undefined behavior.
|
||||
*
|
||||
* @tparam Type Type to which to cast the instance.
|
||||
* @return A reference to the contained instance.
|
||||
*/
|
||||
@@ -473,7 +456,7 @@ public:
|
||||
*/
|
||||
[[nodiscard]] bool allow_cast(const meta_type &type) {
|
||||
if(auto other = std::as_const(*this).allow_cast(type); other) {
|
||||
if((other.storage.policy() == any_policy::owner)) {
|
||||
if(other.owner()) {
|
||||
std::swap(*this, other);
|
||||
}
|
||||
|
||||
@@ -505,7 +488,7 @@ public:
|
||||
* @return True if there exists a viable conversion, false otherwise.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] bool allow_cast() {
|
||||
bool allow_cast() {
|
||||
auto other = internal::resolve<std::remove_cv_t<std::remove_reference_t<Type>>>(internal::meta_context::from(*ctx));
|
||||
return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>) || storage.data() != nullptr);
|
||||
}
|
||||
@@ -538,15 +521,17 @@ public:
|
||||
* @return A sequence container proxy for the underlying object.
|
||||
*/
|
||||
[[nodiscard]] meta_sequence_container as_sequence_container() noexcept {
|
||||
any detached = storage.as_ref();
|
||||
meta_sequence_container proxy{*ctx};
|
||||
vtable(internal::meta_traits::is_meta_sequence_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy);
|
||||
vtable(operation::seq, detached, &proxy);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/*! @copydoc as_sequence_container */
|
||||
[[nodiscard]] meta_sequence_container as_sequence_container() const noexcept {
|
||||
any detached = storage.as_ref();
|
||||
meta_sequence_container proxy{*ctx};
|
||||
vtable(internal::meta_traits::is_meta_sequence_container, true, data(), &proxy);
|
||||
vtable(operation::seq, detached, &proxy);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
@@ -555,15 +540,17 @@ public:
|
||||
* @return An associative container proxy for the underlying object.
|
||||
*/
|
||||
[[nodiscard]] meta_associative_container as_associative_container() noexcept {
|
||||
any detached = storage.as_ref();
|
||||
meta_associative_container proxy{*ctx};
|
||||
vtable(internal::meta_traits::is_meta_associative_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy);
|
||||
vtable(operation::assoc, detached, &proxy);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
/*! @copydoc as_associative_container */
|
||||
[[nodiscard]] meta_associative_container as_associative_container() const noexcept {
|
||||
any detached = storage.as_ref();
|
||||
meta_associative_container proxy{*ctx};
|
||||
vtable(internal::meta_traits::is_meta_associative_container, true, data(), &proxy);
|
||||
vtable(operation::assoc, detached, &proxy);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
@@ -574,7 +561,7 @@ public:
|
||||
*/
|
||||
[[nodiscard]] meta_any operator*() const noexcept {
|
||||
meta_any ret{meta_ctx_arg, *ctx};
|
||||
vtable(internal::meta_traits::is_meta_pointer_like, true, storage.data(), &ret);
|
||||
vtable(operation::deref, storage, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -607,16 +594,8 @@ public:
|
||||
}
|
||||
|
||||
/*! @copydoc any::owner */
|
||||
[[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
|
||||
return (storage.policy() == any_policy::owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current mode of a meta any object.
|
||||
* @return The current mode of the meta any object.
|
||||
*/
|
||||
[[nodiscard]] meta_any_policy policy() const noexcept {
|
||||
return storage.policy();
|
||||
[[nodiscard]] bool owner() const noexcept {
|
||||
return storage.owner();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -653,7 +632,8 @@ template<typename Type>
|
||||
* @brief Opaque pointers to instances of any type.
|
||||
*
|
||||
* A handle doesn't perform copies and isn't responsible for the contained
|
||||
* object. It doesn't prolong the lifetime of the pointed instance.
|
||||
* object. It doesn't prolong the lifetime of the pointed instance.<br/>
|
||||
* Handles are used to generate references to actual objects when needed.
|
||||
*/
|
||||
struct meta_handle {
|
||||
/*! Default constructor. */
|
||||
@@ -1013,7 +993,8 @@ struct meta_func {
|
||||
* @brief Invokes the underlying function, if possible.
|
||||
*
|
||||
* @warning
|
||||
* The context of the arguments is **never** changed.
|
||||
* The context of the arguments is **not** changed.<br/>
|
||||
* It's up to the caller to bind them to the right context(s).
|
||||
*
|
||||
* @param instance An opaque instance of the underlying type.
|
||||
* @param args Parameters to use to invoke the function.
|
||||
@@ -1033,8 +1014,12 @@ struct meta_func {
|
||||
*/
|
||||
template<typename... Args>
|
||||
meta_any invoke(meta_handle instance, Args &&...args) const {
|
||||
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(std::move(instance), arguments, sizeof...(Args));
|
||||
if constexpr(sizeof...(Args) == 0u) {
|
||||
return invoke(std::move(instance), static_cast<meta_any *>(nullptr), size_type{});
|
||||
} else {
|
||||
meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(std::move(instance), arguments, sizeof...(Args));
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc meta_data::prop */
|
||||
@@ -1254,12 +1239,13 @@ public:
|
||||
* doesn't refer to a pointer type.
|
||||
*/
|
||||
[[nodiscard]] meta_type remove_pointer() const noexcept {
|
||||
return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT
|
||||
return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether a type is a pointer-like type or not.
|
||||
* @return True if the underlying type is pointer-like, false otherwise.
|
||||
* @return True if the underlying type is a pointer-like one, false
|
||||
* otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool is_pointer_like() const noexcept {
|
||||
return static_cast<bool>(node.traits & internal::meta_traits::is_meta_pointer_like);
|
||||
@@ -1316,25 +1302,6 @@ public:
|
||||
return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a type supports direct casting to another type.
|
||||
* @param other The meta type to test for.
|
||||
* @return True if direct casting is allowed, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool can_cast(const meta_type &other) const noexcept {
|
||||
// casting this is UB in all cases but we aren't going to use the resulting pointer, so...
|
||||
return (internal::try_cast(internal::meta_context::from(*ctx), node, other.node, this) != nullptr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks if a type supports conversion it to another type.
|
||||
* @param other The meta type to test for.
|
||||
* @return True if the conversion is allowed, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool can_convert(const meta_type &other) const noexcept {
|
||||
return (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast<void>(args), 1) + ... + 0u); }) != 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a range to visit registered top-level base meta types.
|
||||
* @return An iterable range to visit registered top-level base meta types.
|
||||
@@ -1359,8 +1326,19 @@ public:
|
||||
* @return The registered meta data for the given identifier, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_data data(const id_type id) const {
|
||||
const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id);
|
||||
return elem ? meta_data{*ctx, *elem} : meta_data{};
|
||||
if(node.details) {
|
||||
if(const auto it = node.details->data.find(id); it != node.details->data.cend()) {
|
||||
return meta_data{*ctx, it->second};
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &&curr: base()) {
|
||||
if(auto elem = curr.second.data(id); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
return meta_data{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1375,21 +1353,36 @@ public:
|
||||
/**
|
||||
* @brief Lookup utility for meta functions (bases are also visited).
|
||||
*
|
||||
* In case of overloaded functions, a random one is returned.
|
||||
* In case of overloaded functions, the first one with the required
|
||||
* identifier is returned.
|
||||
*
|
||||
* @param id Unique identifier.
|
||||
* @return The registered meta function for the given identifier, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_func func(const id_type id) const {
|
||||
const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id);
|
||||
return elem ? meta_func{*ctx, *elem} : meta_func{};
|
||||
if(node.details) {
|
||||
if(const auto it = node.details->func.find(id); it != node.details->func.cend()) {
|
||||
return meta_func{*ctx, it->second};
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &&curr: base()) {
|
||||
if(auto elem = curr.second.func(id); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
return meta_func{};
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates an instance of the underlying type, if possible.
|
||||
*
|
||||
* If suitable, the implicitly generated default constructor is used.
|
||||
*
|
||||
* @warning
|
||||
* The context of the arguments is **never** changed.
|
||||
* The context of the arguments is **not** changed.<br/>
|
||||
* It's up to the caller to bind them to the right context(s).
|
||||
*
|
||||
* @param args Parameters to use to construct the instance.
|
||||
* @param sz Number of parameters to use to construct the instance.
|
||||
@@ -1417,8 +1410,12 @@ public:
|
||||
*/
|
||||
template<typename... Args>
|
||||
[[nodiscard]] meta_any construct(Args &&...args) const {
|
||||
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return construct(arguments, sizeof...(Args));
|
||||
if constexpr(sizeof...(Args) == 0u) {
|
||||
return construct(static_cast<meta_any *>(nullptr), size_type{});
|
||||
} else {
|
||||
meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return construct(arguments, sizeof...(Args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1439,7 +1436,8 @@ public:
|
||||
* @brief Invokes a function given an identifier, if possible.
|
||||
*
|
||||
* @warning
|
||||
* The context of the arguments is **never** changed.
|
||||
* The context of the arguments is **not** changed.<br/>
|
||||
* It's up to the caller to bind them to the right context(s).
|
||||
*
|
||||
* @param id Unique identifier.
|
||||
* @param instance An opaque instance of the underlying type.
|
||||
@@ -1467,6 +1465,7 @@ public:
|
||||
|
||||
/**
|
||||
* @copybrief invoke
|
||||
*
|
||||
* @param id Unique identifier.
|
||||
* @tparam Args Types of arguments to use to invoke the function.
|
||||
* @param instance An opaque instance of the underlying type.
|
||||
@@ -1475,8 +1474,12 @@ public:
|
||||
*/
|
||||
template<typename... Args>
|
||||
meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const {
|
||||
meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(id, std::move(instance), arguments, sizeof...(Args));
|
||||
if constexpr(sizeof...(Args) == 0u) {
|
||||
return invoke(id, std::move(instance), static_cast<meta_any *>(nullptr), size_type{});
|
||||
} else {
|
||||
meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...};
|
||||
return invoke(id, std::move(instance), arguments, sizeof...(Args));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1519,8 +1522,19 @@ public:
|
||||
* @return The registered meta property for the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] meta_prop prop(const id_type key) const {
|
||||
const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key);
|
||||
return elem ? meta_prop{*ctx, *elem} : meta_prop{};
|
||||
if(node.details) {
|
||||
if(const auto it = node.details->prop.find(key); it != node.details->prop.cend()) {
|
||||
return meta_prop{*ctx, it->second};
|
||||
}
|
||||
}
|
||||
|
||||
for(auto &&curr: base()) {
|
||||
if(auto elem = curr.second.prop(key); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
|
||||
return meta_prop{};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1579,27 +1593,39 @@ bool meta_any::set(const id_type id, Type &&value) {
|
||||
}
|
||||
|
||||
[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const {
|
||||
return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) {
|
||||
if constexpr((std::is_same_v<std::remove_const_t<std::remove_reference_t<decltype(args)>>, internal::meta_type_node> || ...)) {
|
||||
return (args.from_void(*ctx, nullptr, instance), ...);
|
||||
} else if constexpr((std::is_same_v<std::remove_const_t<std::remove_reference_t<decltype(args)>>, internal::meta_conv_node> || ...)) {
|
||||
return (args.conv(*ctx, instance), ...);
|
||||
} else if constexpr((std::is_same_v<std::remove_const_t<std::remove_reference_t<decltype(args)>>, decltype(internal::meta_type_node::conversion_helper)> || ...)) {
|
||||
// exploits the fact that arithmetic types and enums are also default constructible
|
||||
auto other = type.construct();
|
||||
const auto value = (args(nullptr, instance), ...);
|
||||
other.node.conversion_helper(other.data(), &value);
|
||||
return other;
|
||||
} else {
|
||||
// forwards to force a compile-time error in case of available arguments
|
||||
return meta_any{meta_ctx_arg, *ctx, std::forward<decltype(args)>(args)...};
|
||||
if(node.info && *node.info == type.info()) {
|
||||
return as_ref();
|
||||
}
|
||||
|
||||
if(const auto *value = data(); node.details) {
|
||||
if(auto it = node.details->conv.find(type.info().hash()); it != node.details->conv.cend()) {
|
||||
return it->second.conv(*ctx, data());
|
||||
}
|
||||
});
|
||||
|
||||
for(auto &&curr: node.details->base) {
|
||||
const auto &as_const = curr.second.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.second.cast(value));
|
||||
|
||||
if(auto other = as_const.allow_cast(type); other) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(node.conversion_helper && (type.is_arithmetic() || type.is_enum())) {
|
||||
// exploits the fact that arithmetic types and enums are also default constructible
|
||||
auto other = type.construct();
|
||||
ENTT_ASSERT(other.node.conversion_helper, "Conversion helper not found");
|
||||
const auto value = node.conversion_helper(nullptr, storage.data());
|
||||
other.node.conversion_helper(other.storage.data(), &value);
|
||||
return other;
|
||||
}
|
||||
|
||||
return meta_any{meta_ctx_arg, *ctx};
|
||||
}
|
||||
|
||||
inline bool meta_any::assign(const meta_any &other) {
|
||||
auto value = other.allow_cast({*ctx, node});
|
||||
return value && storage.assign(value.storage);
|
||||
return value && storage.assign(std::move(value.storage));
|
||||
}
|
||||
|
||||
inline bool meta_any::assign(meta_any &&other) {
|
||||
@@ -1626,14 +1652,33 @@ inline bool meta_any::assign(meta_any &&other) {
|
||||
return index < arity() ? node->arg(*ctx, index) : meta_type{};
|
||||
}
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
class meta_sequence_container::meta_iterator final {
|
||||
using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *);
|
||||
friend class meta_sequence_container;
|
||||
|
||||
enum class operation : std::uint8_t {
|
||||
incr,
|
||||
deref
|
||||
};
|
||||
|
||||
using vtable_type = void(const operation, const any &, const std::ptrdiff_t, meta_any *);
|
||||
|
||||
template<typename It>
|
||||
static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) {
|
||||
const auto &it = *static_cast<const It *>(value);
|
||||
other ? other->emplace<decltype(*it)>(*it) : std::advance(const_cast<It &>(it), offset);
|
||||
static void basic_vtable(const operation op, const any &value, const std::ptrdiff_t offset, meta_any *other) {
|
||||
switch(op) {
|
||||
case operation::incr: {
|
||||
auto &it = any_cast<It &>(const_cast<any &>(value));
|
||||
it = std::next(it, offset);
|
||||
} break;
|
||||
case operation::deref: {
|
||||
const auto &it = any_cast<const It &>(value);
|
||||
other->emplace<decltype(*it)>(*it);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1642,45 +1687,43 @@ public:
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::bidirectional_iterator_tag;
|
||||
|
||||
meta_iterator() noexcept
|
||||
: meta_iterator{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
meta_iterator(const meta_ctx &area) noexcept
|
||||
: ctx{&area} {}
|
||||
constexpr meta_iterator() noexcept
|
||||
: ctx{},
|
||||
vtable{},
|
||||
handle{} {}
|
||||
|
||||
template<typename It>
|
||||
meta_iterator(const meta_ctx &area, It iter) noexcept
|
||||
explicit meta_iterator(const meta_ctx &area, It iter) noexcept
|
||||
: ctx{&area},
|
||||
vtable{&basic_vtable<It>},
|
||||
handle{iter} {}
|
||||
|
||||
meta_iterator &operator++() noexcept {
|
||||
vtable(handle.data(), 1, nullptr);
|
||||
vtable(operation::incr, handle, 1, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
meta_iterator operator++(int value) noexcept {
|
||||
meta_iterator orig = *this;
|
||||
vtable(handle.data(), ++value, nullptr);
|
||||
vtable(operation::incr, handle, ++value, nullptr);
|
||||
return orig;
|
||||
}
|
||||
|
||||
meta_iterator &operator--() noexcept {
|
||||
vtable(handle.data(), -1, nullptr);
|
||||
vtable(operation::incr, handle, -1, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
meta_iterator operator--(int value) noexcept {
|
||||
meta_iterator orig = *this;
|
||||
vtable(handle.data(), --value, nullptr);
|
||||
vtable(operation::incr, handle, --value, nullptr);
|
||||
return orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
reference other{meta_ctx_arg, *ctx};
|
||||
vtable(handle.data(), 0, &other);
|
||||
vtable(operation::deref, handle, 0, &other);
|
||||
return other;
|
||||
}
|
||||
|
||||
@@ -1700,30 +1743,35 @@ public:
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
[[nodiscard]] const any &base() const noexcept {
|
||||
return handle;
|
||||
}
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
vtable_type *vtable{};
|
||||
any handle{};
|
||||
const meta_ctx *ctx;
|
||||
vtable_type *vtable;
|
||||
any handle;
|
||||
};
|
||||
|
||||
class meta_associative_container::meta_iterator final {
|
||||
using vtable_type = void(const void *, std::pair<meta_any, meta_any> *);
|
||||
enum class operation : std::uint8_t {
|
||||
incr,
|
||||
deref
|
||||
};
|
||||
|
||||
using vtable_type = void(const operation, const any &, std::pair<meta_any, meta_any> *);
|
||||
|
||||
template<bool KeyOnly, typename It>
|
||||
static void basic_vtable(const void *value, std::pair<meta_any, meta_any> *other) {
|
||||
if(const auto &it = *static_cast<const It *>(value); other) {
|
||||
static void basic_vtable(const operation op, const any &value, std::pair<meta_any, meta_any> *other) {
|
||||
switch(op) {
|
||||
case operation::incr:
|
||||
++any_cast<It &>(const_cast<any &>(value));
|
||||
break;
|
||||
case operation::deref:
|
||||
const auto &it = any_cast<const It &>(value);
|
||||
if constexpr(KeyOnly) {
|
||||
other->first.emplace<decltype(*it)>(*it);
|
||||
} else {
|
||||
other->first.emplace<decltype((it->first))>(it->first);
|
||||
other->second.emplace<decltype((it->second))>(it->second);
|
||||
}
|
||||
} else {
|
||||
++const_cast<It &>(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1733,34 +1781,31 @@ public:
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::forward_iterator_tag;
|
||||
|
||||
meta_iterator() noexcept
|
||||
: meta_iterator{locator<meta_ctx>::value_or()} {}
|
||||
|
||||
meta_iterator(const meta_ctx &area) noexcept
|
||||
: ctx{&area} {}
|
||||
constexpr meta_iterator() noexcept
|
||||
: ctx{},
|
||||
vtable{},
|
||||
handle{} {}
|
||||
|
||||
template<bool KeyOnly, typename It>
|
||||
meta_iterator(const meta_ctx &area, std::bool_constant<KeyOnly>, It iter) noexcept
|
||||
meta_iterator(const meta_ctx &area, std::integral_constant<bool, KeyOnly>, It iter) noexcept
|
||||
: ctx{&area},
|
||||
vtable{&basic_vtable<KeyOnly, It>},
|
||||
handle{iter} {}
|
||||
|
||||
meta_iterator &operator++() noexcept {
|
||||
vtable(handle.data(), nullptr);
|
||||
vtable(operation::incr, handle, nullptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
meta_iterator operator++(int) noexcept {
|
||||
meta_iterator orig = *this;
|
||||
vtable(handle.data(), nullptr);
|
||||
return orig;
|
||||
return ++(*this), orig;
|
||||
}
|
||||
|
||||
[[nodiscard]] reference operator*() const {
|
||||
reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}};
|
||||
vtable(handle.data(), &other);
|
||||
vtable(operation::deref, handle, &other);
|
||||
return other;
|
||||
}
|
||||
|
||||
@@ -1781,11 +1826,15 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
const meta_ctx *ctx{};
|
||||
vtable_type *vtable{};
|
||||
any handle{};
|
||||
const meta_ctx *ctx;
|
||||
vtable_type *vtable;
|
||||
any handle;
|
||||
};
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Returns the meta value type of a container.
|
||||
@@ -1800,7 +1849,7 @@ private:
|
||||
* @return The size of the container.
|
||||
*/
|
||||
[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept {
|
||||
return size_fn(data);
|
||||
return size_fn(storage);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1809,7 +1858,7 @@ private:
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
inline bool meta_sequence_container::resize(const size_type sz) {
|
||||
return !const_only && resize_fn(const_cast<void *>(data), sz);
|
||||
return resize_fn(storage, sz);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1817,16 +1866,7 @@ inline bool meta_sequence_container::resize(const size_type sz) {
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
inline bool meta_sequence_container::clear() {
|
||||
return !const_only && clear_fn(const_cast<void *>(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves storage for at least the given number of elements.
|
||||
* @param sz The new capacity of the container.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
inline bool meta_sequence_container::reserve(const size_type sz) {
|
||||
return !const_only && reserve_fn(const_cast<void *>(data), sz);
|
||||
return resize_fn(storage, 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1834,7 +1874,7 @@ inline bool meta_sequence_container::reserve(const size_type sz) {
|
||||
* @return An iterator to the first element of the container.
|
||||
*/
|
||||
[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() {
|
||||
return begin_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data);
|
||||
return iter_fn(*ctx, storage, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1842,7 +1882,7 @@ inline bool meta_sequence_container::reserve(const size_type sz) {
|
||||
* @return An iterator that is past the last element of the container.
|
||||
*/
|
||||
[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() {
|
||||
return end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data);
|
||||
return iter_fn(*ctx, storage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1852,13 +1892,7 @@ inline bool meta_sequence_container::reserve(const size_type sz) {
|
||||
* @return A possibly invalid iterator to the inserted element.
|
||||
*/
|
||||
inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) {
|
||||
// this abomination is necessary because only on macos value_type and const_reference are different types for std::vector<bool>
|
||||
if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) {
|
||||
const bool is_value_type = (value.type().info() == *vtype.info);
|
||||
return insert_fn(*ctx, const_cast<void *>(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it);
|
||||
}
|
||||
|
||||
return iterator{*ctx};
|
||||
return insert_or_erase_fn(*ctx, storage, it.handle, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1867,7 +1901,7 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato
|
||||
* @return A possibly invalid iterator following the last removed element.
|
||||
*/
|
||||
inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) {
|
||||
return const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast<void *>(data), it);
|
||||
return insert(it, {});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1887,15 +1921,15 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator
|
||||
* @return False if the proxy is invalid, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept {
|
||||
return (data != nullptr);
|
||||
return static_cast<bool>(storage);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns true if a container is also key-only, false otherwise.
|
||||
* @return True if the associative container is also key-only, false otherwise.
|
||||
*/
|
||||
[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept {
|
||||
return (mapped_type_node == nullptr);
|
||||
[[nodiscard]] inline bool meta_associative_container::key_only() const noexcept {
|
||||
return key_only_container;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1921,39 +1955,42 @@ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator
|
||||
|
||||
/*! @copydoc meta_sequence_container::size */
|
||||
[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept {
|
||||
return size_fn(data);
|
||||
return size_fn(storage);
|
||||
}
|
||||
|
||||
/*! @copydoc meta_sequence_container::clear */
|
||||
inline bool meta_associative_container::clear() {
|
||||
return !const_only && clear_fn(const_cast<void *>(data));
|
||||
}
|
||||
|
||||
/*! @copydoc meta_sequence_container::reserve */
|
||||
inline bool meta_associative_container::reserve(const size_type sz) {
|
||||
return !const_only && reserve_fn(const_cast<void *>(data), sz);
|
||||
return clear_fn(storage);
|
||||
}
|
||||
|
||||
/*! @copydoc meta_sequence_container::begin */
|
||||
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() {
|
||||
return begin_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data);
|
||||
return iter_fn(*ctx, storage, false);
|
||||
}
|
||||
|
||||
/*! @copydoc meta_sequence_container::end */
|
||||
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() {
|
||||
return end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data);
|
||||
return iter_fn(*ctx, storage, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a key-only or key/value element into a container.
|
||||
* @brief Inserts a key only element into a container.
|
||||
* @param key The key of the element to insert.
|
||||
* @param value The value of the element to insert, if needed.
|
||||
* @return A bool denoting whether the insertion took place.
|
||||
*/
|
||||
inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
|
||||
return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})
|
||||
&& (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))}))
|
||||
&& insert_fn(const_cast<void *>(data), std::as_const(key).data(), std::as_const(value).data());
|
||||
inline bool meta_associative_container::insert(meta_any key) {
|
||||
meta_any value{*ctx, std::in_place_type<void>};
|
||||
return (insert_or_erase_fn(storage, key, value) != 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a key/value element into a container.
|
||||
* @param key The key of the element to insert.
|
||||
* @param value The value of the element to insert.
|
||||
* @return A bool denoting whether the insertion took place.
|
||||
*/
|
||||
inline bool meta_associative_container::insert(meta_any key, meta_any value) {
|
||||
return (insert_or_erase_fn(storage, key, value) != 0u);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1962,7 +1999,7 @@ inline bool meta_associative_container::insert(meta_any key, meta_any value = {}
|
||||
* @return A bool denoting whether the removal took place.
|
||||
*/
|
||||
inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) {
|
||||
return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast<void *>(data), std::as_const(key).data()) : 0u;
|
||||
return insert(std::move(key), meta_any{meta_ctx_arg, *ctx});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1971,7 +2008,7 @@ inline meta_associative_container::size_type meta_associative_container::erase(m
|
||||
* @return An iterator to the element with the given key, if any.
|
||||
*/
|
||||
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
|
||||
return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, std::as_const(key).data()) : iterator{*ctx};
|
||||
return find_fn(*ctx, storage, key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1979,7 +2016,7 @@ inline meta_associative_container::size_type meta_associative_container::erase(m
|
||||
* @return False if the proxy is invalid, true otherwise.
|
||||
*/
|
||||
[[nodiscard]] inline meta_associative_container::operator bool() const noexcept {
|
||||
return (data != nullptr);
|
||||
return static_cast<bool>(storage);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -22,7 +22,11 @@ class meta_any;
|
||||
class meta_type;
|
||||
struct meta_handle;
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
enum class meta_traits : std::uint32_t {
|
||||
@@ -127,23 +131,6 @@ struct meta_type_node {
|
||||
std::shared_ptr<meta_type_descriptor> details{};
|
||||
};
|
||||
|
||||
template<auto Member>
|
||||
auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
|
||||
if(node.details) {
|
||||
if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) {
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
for(auto &&curr: node.details->base) {
|
||||
if(auto *elem = look_for<Member>(context, curr.second.type(context), id); elem) {
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<typename std::remove_reference_t<decltype(node.details.get()->*Member)>::mapped_type *>(nullptr);
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
meta_type_node resolve(const meta_context &) noexcept;
|
||||
|
||||
@@ -172,31 +159,6 @@ template<typename... Args>
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) {
|
||||
if(from.info && *from.info == to) {
|
||||
return func(instance, from);
|
||||
}
|
||||
|
||||
if(from.details) {
|
||||
if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) {
|
||||
return func(instance, it->second);
|
||||
}
|
||||
|
||||
for(auto &&curr: from.details->base) {
|
||||
if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) {
|
||||
return other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(from.conversion_helper && arithmetic_or_enum) {
|
||||
return func(instance, from.conversion_helper);
|
||||
}
|
||||
|
||||
return func(instance);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept {
|
||||
const auto it = context.value.find(info.hash());
|
||||
return it != context.value.end() ? &it->second : nullptr;
|
||||
@@ -242,7 +204,7 @@ template<typename Type>
|
||||
};
|
||||
}
|
||||
|
||||
if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
|
||||
if constexpr(!std::is_same_v<Type, void> && !std::is_function_v<Type>) {
|
||||
node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) {
|
||||
if(element) {
|
||||
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(element)};
|
||||
@@ -263,7 +225,11 @@ template<typename Type>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
} // namespace entt
|
||||
|
||||
|
||||
@@ -7,34 +7,58 @@ namespace entt {
|
||||
|
||||
/*! @brief Empty class type used to request the _as ref_ policy. */
|
||||
struct as_ref_t final {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type> && !std::is_const_v<std::remove_reference_t<Type>>;
|
||||
/*! @endcond */
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as cref_ policy. */
|
||||
struct as_cref_t final {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
template<typename Type>
|
||||
static constexpr bool value = std::is_reference_v<Type>;
|
||||
/*! @endcond */
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as-is_ policy. */
|
||||
struct as_is_t final {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
/*! @endcond */
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
};
|
||||
|
||||
/*! @brief Empty class type used to request the _as void_ policy. */
|
||||
struct as_void_t final {
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
template<typename>
|
||||
static constexpr bool value = true;
|
||||
/*! @endcond */
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,7 +10,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename It>
|
||||
@@ -20,7 +24,6 @@ struct meta_range_iterator final {
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using iterator_concept = std::random_access_iterator_tag;
|
||||
|
||||
constexpr meta_range_iterator() noexcept
|
||||
: it{},
|
||||
@@ -128,7 +131,11 @@ template<typename... Args>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Iterable range to use to iterate all types of meta objects.
|
||||
|
||||
@@ -313,12 +313,16 @@ template<typename Type, auto Data, typename Policy = as_is_t>
|
||||
return meta_getter<Type, Data, Policy>(locator<meta_ctx>::value_or(), std::move(instance));
|
||||
}
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Policy, typename Candidate, typename... Args>
|
||||
[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) {
|
||||
if constexpr(std::is_void_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...))>) {
|
||||
if constexpr(std::is_same_v<decltype(std::invoke(std::forward<Candidate>(candidate), args...)), void>) {
|
||||
std::invoke(std::forward<Candidate>(candidate), args...);
|
||||
return meta_any{ctx, std::in_place_type<void>};
|
||||
} else {
|
||||
@@ -335,7 +339,7 @@ template<typename Type, typename Policy, typename Candidate, std::size_t... Inde
|
||||
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else if constexpr(std::is_invocable_v<std::remove_reference_t<Candidate>, Type &, type_list_element_t<Index, typename descriptor::args_type>...>) {
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) { // NOLINT
|
||||
if(auto *const clazz = instance->try_cast<Type>(); clazz && ((args + Index)->allow_cast<type_list_element_t<Index, typename descriptor::args_type>>() && ...)) {
|
||||
return meta_invoke_with_args<Policy>(ctx, std::forward<Candidate>(candidate), *clazz, (args + Index)->cast<type_list_element_t<Index, typename descriptor::args_type>>()...);
|
||||
}
|
||||
} else {
|
||||
@@ -357,7 +361,11 @@ template<typename Type, typename... Args, std::size_t... Index>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Tries to _invoke_ an object given a list of erased parameters.
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID__
|
||||
# include <android/ndk-version.h>
|
||||
# if __NDK_MAJOR__ == 17
|
||||
@@ -54,6 +58,10 @@ using invoke_result_t = typename std::invoke_result<Func, Args...>::type;
|
||||
|
||||
# endif
|
||||
#endif
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#endif
|
||||
|
||||
@@ -190,6 +190,7 @@ decltype(auto) poly_call(Poly &&self, Args &&...args) {
|
||||
*/
|
||||
template<typename Concept, std::size_t Len, std::size_t Align>
|
||||
class basic_poly: private Concept::template type<poly_base<basic_poly<Concept, Len, Align>>> {
|
||||
/*! @brief A poly base is allowed to snoop into a poly object. */
|
||||
friend struct poly_base<basic_poly>;
|
||||
|
||||
public:
|
||||
|
||||
@@ -2,14 +2,13 @@
|
||||
#define ENTT_PROCESS_FWD_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace entt {
|
||||
|
||||
template<typename, typename>
|
||||
class process;
|
||||
|
||||
template<typename = std::uint32_t, typename = std::allocator<void>>
|
||||
template<typename = std::uint32_t>
|
||||
class basic_scheduler;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "fwd.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
@@ -176,13 +175,13 @@ public:
|
||||
* The function is idempotent and it does nothing if the process isn't
|
||||
* alive.
|
||||
*
|
||||
* @param immediate Requests an immediate operation.
|
||||
* @param immediately Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediate = false) {
|
||||
void abort(const bool immediately = false) {
|
||||
if(alive()) {
|
||||
current = state::aborted;
|
||||
|
||||
if(immediate) {
|
||||
if(immediately) {
|
||||
tick({});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,17 @@
|
||||
#ifndef ENTT_PROCESS_SCHEDULER_HPP
|
||||
#define ENTT_PROCESS_SCHEDULER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "../config/config.h"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "fwd.hpp"
|
||||
#include "process.hpp"
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
namespace internal {
|
||||
|
||||
template<typename Delta>
|
||||
struct basic_process_handler {
|
||||
virtual ~basic_process_handler() = default;
|
||||
|
||||
virtual bool update(const Delta, void *) = 0;
|
||||
virtual void abort(const bool) = 0;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
std::shared_ptr<basic_process_handler> next;
|
||||
};
|
||||
|
||||
template<typename Delta, typename Type>
|
||||
struct process_handler final: basic_process_handler<Delta> {
|
||||
template<typename... Args>
|
||||
process_handler(Args &&...args)
|
||||
: process{std::forward<Args>(args)...} {}
|
||||
|
||||
bool update(const Delta delta, void *data) override {
|
||||
if(process.tick(delta, data); process.rejected()) {
|
||||
this->next.reset();
|
||||
}
|
||||
|
||||
return (process.rejected() || process.finished());
|
||||
}
|
||||
|
||||
void abort(const bool immediate) override {
|
||||
process.abort(immediate);
|
||||
}
|
||||
|
||||
Type process;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Cooperative scheduler for processes.
|
||||
*
|
||||
@@ -76,90 +37,95 @@ struct process_handler final: basic_process_handler<Delta> {
|
||||
* @sa process
|
||||
*
|
||||
* @tparam Delta Type to use to provide elapsed time.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Delta, typename Allocator>
|
||||
template<typename Delta>
|
||||
class basic_scheduler {
|
||||
template<typename Type>
|
||||
using handler_type = internal::process_handler<Delta, Type>;
|
||||
struct process_handler {
|
||||
using instance_type = std::unique_ptr<void, void (*)(void *)>;
|
||||
using update_fn_type = bool(basic_scheduler &, std::size_t, Delta, void *);
|
||||
using abort_fn_type = void(basic_scheduler &, std::size_t, bool);
|
||||
using next_type = std::unique_ptr<process_handler>;
|
||||
|
||||
// std::shared_ptr because of its type erased allocator which is useful here
|
||||
using process_type = std::shared_ptr<internal::basic_process_handler<Delta>>;
|
||||
instance_type instance;
|
||||
update_fn_type *update;
|
||||
abort_fn_type *abort;
|
||||
next_type next;
|
||||
};
|
||||
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
using container_allocator = typename alloc_traits::template rebind_alloc<process_type>;
|
||||
using container_type = std::vector<process_type, container_allocator>;
|
||||
struct continuation {
|
||||
continuation(process_handler *ref) noexcept
|
||||
: handler{ref} {}
|
||||
|
||||
template<typename Proc, typename... Args>
|
||||
continuation then(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &basic_scheduler::deleter<Proc>};
|
||||
handler->next.reset(new process_handler{std::move(proc), &basic_scheduler::update<Proc>, &basic_scheduler::abort<Proc>, nullptr});
|
||||
handler = handler->next.get();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
continuation then(Func &&func) {
|
||||
return then<process_adaptor<std::decay_t<Func>, Delta>>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
private:
|
||||
process_handler *handler;
|
||||
};
|
||||
|
||||
template<typename Proc>
|
||||
[[nodiscard]] static bool update(basic_scheduler &owner, std::size_t pos, const Delta delta, void *data) {
|
||||
auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get());
|
||||
process->tick(delta, data);
|
||||
|
||||
if(process->rejected()) {
|
||||
return true;
|
||||
} else if(process->finished()) {
|
||||
if(auto &&handler = owner.handlers[pos]; handler.next) {
|
||||
handler = std::move(*handler.next);
|
||||
// forces the process to exit the uninitialized state
|
||||
return handler.update(owner, pos, {}, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
static void abort(basic_scheduler &owner, std::size_t pos, const bool immediately) {
|
||||
static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately);
|
||||
}
|
||||
|
||||
template<typename Proc>
|
||||
static void deleter(void *proc) {
|
||||
delete static_cast<Proc *>(proc);
|
||||
}
|
||||
|
||||
public:
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using delta_type = Delta;
|
||||
/*! @brief Unsigned integer type. */
|
||||
using size_type = std::size_t;
|
||||
|
||||
/*! @brief Default constructor. */
|
||||
basic_scheduler()
|
||||
: basic_scheduler{allocator_type{}} {}
|
||||
: handlers{} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a scheduler with a given allocator.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
explicit basic_scheduler(const allocator_type &allocator)
|
||||
: handlers{allocator, allocator} {}
|
||||
/*! @brief Default move constructor. */
|
||||
basic_scheduler(basic_scheduler &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Move constructor.
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_scheduler(basic_scheduler &&other) noexcept
|
||||
: handlers{std::move(other.handlers)} {}
|
||||
|
||||
/**
|
||||
* @brief Allocator-extended move constructor.
|
||||
* @param other The instance to move from.
|
||||
* @param allocator The allocator to use.
|
||||
*/
|
||||
basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept
|
||||
: handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Move assignment operator.
|
||||
* @param other The instance to move from.
|
||||
* @return This scheduler.
|
||||
*/
|
||||
basic_scheduler &operator=(basic_scheduler &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed");
|
||||
handlers = std::move(other.handlers);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Exchanges the contents with those of a given scheduler.
|
||||
* @param other Scheduler to exchange the content with.
|
||||
*/
|
||||
void swap(basic_scheduler &other) {
|
||||
using std::swap;
|
||||
swap(handlers, other.handlers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the associated allocator.
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const noexcept {
|
||||
return handlers.second();
|
||||
}
|
||||
/*! @brief Default move assignment operator. @return This scheduler. */
|
||||
basic_scheduler &operator=(basic_scheduler &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Number of processes currently scheduled.
|
||||
* @return Number of processes currently scheduled.
|
||||
*/
|
||||
[[nodiscard]] size_type size() const noexcept {
|
||||
return handlers.first().size();
|
||||
return handlers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -167,7 +133,7 @@ public:
|
||||
* @return True if there are scheduled processes, false otherwise.
|
||||
*/
|
||||
[[nodiscard]] bool empty() const noexcept {
|
||||
return handlers.first().empty();
|
||||
return handlers.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,15 +143,15 @@ public:
|
||||
* and never executed again.
|
||||
*/
|
||||
void clear() {
|
||||
handlers.first().clear();
|
||||
handlers.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Schedules a process for the next tick.
|
||||
*
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
* Returned value is an opaque object that can be used to attach a child to
|
||||
* the given process. The child is automatically scheduled when the process
|
||||
* terminates and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
@@ -203,15 +169,16 @@ public:
|
||||
* @tparam Proc Type of process to schedule.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
* @return An opaque object to use to concatenate processes.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &attach(Args &&...args) {
|
||||
auto attach(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
auto &ref = handlers.first().emplace_back(std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...));
|
||||
auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &basic_scheduler::deleter<Proc>};
|
||||
auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &basic_scheduler::update<Proc>, &basic_scheduler::abort<Proc>, nullptr});
|
||||
// forces the process to exit the uninitialized state
|
||||
ref->update({}, nullptr);
|
||||
return *this;
|
||||
ref.update(*this, handlers.size() - 1u, {}, nullptr);
|
||||
return continuation{&handlers.back()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,9 +207,9 @@ public:
|
||||
* void();
|
||||
* @endcode
|
||||
*
|
||||
* Returned value can be used to attach a continuation for the last process.
|
||||
* The continutation is scheduled automatically when the process terminates
|
||||
* and only if the process returns with success.
|
||||
* Returned value is an opaque object that can be used to attach a child to
|
||||
* the given process. The child is automatically scheduled when the process
|
||||
* terminates and only if the process returns with success.
|
||||
*
|
||||
* Example of use (pseudocode):
|
||||
*
|
||||
@@ -263,43 +230,14 @@ public:
|
||||
*
|
||||
* @tparam Func Type of process to schedule.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
* @return An opaque object to use to concatenate processes.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &attach(Func &&func) {
|
||||
auto attach(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return attach<Proc>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Proc Type of process to use as a continuation.
|
||||
* @tparam Args Types of arguments to use to initialize the process.
|
||||
* @param args Parameters to use to initialize the process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Proc, typename... Args>
|
||||
basic_scheduler &then(Args &&...args) {
|
||||
static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type");
|
||||
ENTT_ASSERT(!handlers.first().empty(), "Process not available");
|
||||
auto *curr = handlers.first().back().get();
|
||||
for(; curr->next; curr = curr->next.get()) {}
|
||||
curr->next = std::allocate_shared<handler_type<Proc>>(handlers.second(), std::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets a process as a continuation of the last scheduled process.
|
||||
* @tparam Func Type of process to use as a continuation.
|
||||
* @param func Either a lambda or a functor to use as a process.
|
||||
* @return This process scheduler.
|
||||
*/
|
||||
template<typename Func>
|
||||
basic_scheduler &then(Func &&func) {
|
||||
using Proc = process_adaptor<std::decay_t<Func>, Delta>;
|
||||
return then<Proc>(std::forward<Func>(func));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates all scheduled processes.
|
||||
*
|
||||
@@ -312,17 +250,12 @@ public:
|
||||
* @param data Optional data.
|
||||
*/
|
||||
void update(const delta_type delta, void *data = nullptr) {
|
||||
for(auto next = handlers.first().size(); next; --next) {
|
||||
if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) {
|
||||
// updating might spawn/reallocate, cannot hold refs until here
|
||||
if(auto &curr = handlers.first()[pos]; curr->next) {
|
||||
curr = std::move(curr->next);
|
||||
// forces the process to exit the uninitialized state
|
||||
curr->update({}, nullptr);
|
||||
} else {
|
||||
curr = std::move(handlers.first().back());
|
||||
handlers.first().pop_back();
|
||||
}
|
||||
for(auto pos = handlers.size(); pos; --pos) {
|
||||
const auto curr = pos - 1u;
|
||||
|
||||
if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) {
|
||||
std::swap(handlers[curr], handlers.back());
|
||||
handlers.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,16 +268,17 @@ public:
|
||||
* Once a process is fully aborted and thus finished, it's discarded along
|
||||
* with its child, if any.
|
||||
*
|
||||
* @param immediate Requests an immediate operation.
|
||||
* @param immediately Requests an immediate operation.
|
||||
*/
|
||||
void abort(const bool immediate = false) {
|
||||
for(auto &&curr: handlers.first()) {
|
||||
curr->abort(immediate);
|
||||
void abort(const bool immediately = false) {
|
||||
for(auto pos = handlers.size(); pos; --pos) {
|
||||
const auto curr = pos - 1u;
|
||||
handlers[curr].abort(*this, curr, immediately);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
compressed_pair<container_type, allocator_type> handlers;
|
||||
std::vector<process_handler> handlers{};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
@@ -19,7 +19,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Type, typename It>
|
||||
@@ -33,7 +37,6 @@ 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 resource_cache_iterator() noexcept = default;
|
||||
|
||||
@@ -141,7 +144,11 @@ template<typename... Lhs, typename... Rhs>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic cache for resources of any type.
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace entt {
|
||||
*/
|
||||
template<typename Type>
|
||||
class resource {
|
||||
/*! @brief Resource handles are friends with each other. */
|
||||
template<typename>
|
||||
friend class resource;
|
||||
|
||||
|
||||
@@ -12,7 +12,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
@@ -39,7 +43,11 @@ template<typename... Class, typename Ret, typename... Args>
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic delegate implementation.
|
||||
|
||||
@@ -17,7 +17,11 @@
|
||||
|
||||
namespace entt {
|
||||
|
||||
/*! @cond TURN_OFF_DOXYGEN */
|
||||
/**
|
||||
* @cond TURN_OFF_DOXYGEN
|
||||
* Internal details not to be documented.
|
||||
*/
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct basic_dispatcher_handler {
|
||||
@@ -78,7 +82,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t size() const noexcept override {
|
||||
std::size_t size() const noexcept override {
|
||||
return events.size();
|
||||
}
|
||||
|
||||
@@ -88,7 +92,11 @@ private:
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* Internal details not to be documented.
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Basic dispatcher implementation.
|
||||
@@ -182,6 +190,7 @@ public:
|
||||
*/
|
||||
basic_dispatcher &operator=(basic_dispatcher &&other) noexcept {
|
||||
ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed");
|
||||
|
||||
pools = std::move(other.pools);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@ class sigh;
|
||||
*/
|
||||
template<typename Ret, typename... Args, typename Allocator>
|
||||
class sigh<Ret(Args...), Allocator> {
|
||||
/*! @brief A sink is allowed to modify a signal. */
|
||||
friend class sink<sigh<Ret(Args...), Allocator>>;
|
||||
|
||||
using alloc_traits = std::allocator_traits<Allocator>;
|
||||
@@ -224,6 +225,7 @@ private:
|
||||
* the sink that generated it.
|
||||
*/
|
||||
class connection {
|
||||
/*! @brief A sink is allowed to create connection objects. */
|
||||
template<typename>
|
||||
friend class sink;
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import "%workspace%/../.bazelrc"
|
||||
@@ -1,4 +1,6 @@
|
||||
#
|
||||
# Tests configuration
|
||||
#
|
||||
|
||||
include(FetchContent)
|
||||
include(CheckCXXSourceCompiles)
|
||||
@@ -32,16 +34,9 @@ else()
|
||||
add_library(GTest::Main ALIAS gtest_main)
|
||||
|
||||
target_compile_features(gtest PUBLIC cxx_std_17)
|
||||
set_target_properties(gtest PROPERTIES CXX_CLANG_TIDY "")
|
||||
|
||||
target_compile_features(gtest_main PUBLIC cxx_std_17)
|
||||
set_target_properties(gtest_main PROPERTIES CXX_CLANG_TIDY "")
|
||||
|
||||
target_compile_features(gmock PUBLIC cxx_std_17)
|
||||
set_target_properties(gmock PROPERTIES CXX_CLANG_TIDY "")
|
||||
|
||||
target_compile_features(gmock_main PUBLIC cxx_std_17)
|
||||
set_target_properties(gmock_main PROPERTIES CXX_CLANG_TIDY "")
|
||||
endif()
|
||||
|
||||
include_directories($<TARGET_PROPERTY:EnTT,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
@@ -69,20 +64,10 @@ function(SETUP_TARGET TARGET_NAME)
|
||||
>
|
||||
# documentation diagnostic turned on for clang-cl only
|
||||
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wdocumentation>
|
||||
# warnings from compilers that think I don't know what I'm doing
|
||||
$<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wcomma>
|
||||
/EHsc /wd4324 /wd4996
|
||||
# disabling INCREMENTAL is required by SizeBench
|
||||
$<$<CONFIG:Debug>:/Od /INCREMENTAL:NO>
|
||||
$<$<CONFIG:Debug>:/Od>
|
||||
$<$<CONFIG:Release>:/O2>
|
||||
)
|
||||
|
||||
target_link_options(
|
||||
${TARGET_NAME}
|
||||
PRIVATE
|
||||
# disabling INCREMENTAL is required by SizeBench
|
||||
$<$<CONFIG:Debug>:/INCREMENTAL:NO>
|
||||
)
|
||||
else()
|
||||
target_compile_options(
|
||||
${TARGET_NAME}
|
||||
@@ -120,7 +105,6 @@ function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES)
|
||||
target_link_libraries(${TEST_NAME} PRIVATE GTest::Main Threads::Threads)
|
||||
SETUP_TARGET(${TEST_NAME} ${ARGN})
|
||||
add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME})
|
||||
set_tests_properties(${TEST_NAME} PROPERTIES TIMEOUT 60)
|
||||
endfunction()
|
||||
|
||||
function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH)
|
||||
@@ -128,7 +112,6 @@ function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH)
|
||||
add_library(_${TARGET_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/lib.cpp)
|
||||
SETUP_TARGET(_${TARGET_NAME} ENTT_API_EXPORT)
|
||||
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp ENTT_API_IMPORT)
|
||||
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_link_libraries(lib_${TARGET_NAME} PRIVATE _${TARGET_NAME})
|
||||
endfunction()
|
||||
|
||||
@@ -137,8 +120,6 @@ function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH)
|
||||
add_library(_${TARGET_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/plugin.cpp)
|
||||
SETUP_TARGET(_${TARGET_NAME} ${ARGVN})
|
||||
SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN})
|
||||
set_target_properties(_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
set_target_properties(lib_${TARGET_NAME} PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_include_directories(_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_include_directories(lib_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR})
|
||||
target_link_libraries(lib_${TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS})
|
||||
@@ -149,7 +130,6 @@ endfunction()
|
||||
|
||||
if(ENTT_BUILD_BENCHMARK)
|
||||
SETUP_BASIC_TEST(benchmark benchmark/benchmark.cpp)
|
||||
set_target_properties(benchmark PROPERTIES CXX_CLANG_TIDY "")
|
||||
endif()
|
||||
|
||||
# Test example
|
||||
@@ -157,7 +137,6 @@ endif()
|
||||
if(ENTT_BUILD_EXAMPLE)
|
||||
SETUP_BASIC_TEST(custom_identifier example/custom_identifier.cpp)
|
||||
SETUP_BASIC_TEST(entity_copy example/entity_copy.cpp)
|
||||
SETUP_BASIC_TEST(reserved_bits example/reserved_bits.cpp)
|
||||
SETUP_BASIC_TEST(signal_less example/signal_less.cpp)
|
||||
endif()
|
||||
|
||||
@@ -201,7 +180,7 @@ if(ENTT_BUILD_SNAPSHOT)
|
||||
FetchContent_Declare(
|
||||
cereal
|
||||
GIT_REPOSITORY https://github.com/USCiLab/cereal.git
|
||||
GIT_TAG v1.3.2
|
||||
GIT_TAG v1.2.2
|
||||
GIT_SHALLOW 1
|
||||
)
|
||||
|
||||
@@ -213,8 +192,6 @@ if(ENTT_BUILD_SNAPSHOT)
|
||||
endif()
|
||||
|
||||
SETUP_BASIC_TEST(cereal snapshot/snapshot.cpp)
|
||||
|
||||
set_target_properties(cereal PROPERTIES CXX_CLANG_TIDY "")
|
||||
target_include_directories(cereal PRIVATE ${cereal_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
@@ -260,8 +237,6 @@ SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp)
|
||||
SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp)
|
||||
SETUP_BASIC_TEST(storage entt/entity/storage.cpp)
|
||||
SETUP_BASIC_TEST(storage_entity entt/entity/storage_entity.cpp)
|
||||
SETUP_BASIC_TEST(storage_no_instance entt/entity/storage_no_instance.cpp)
|
||||
SETUP_BASIC_TEST(storage_utility entt/entity/storage_utility.cpp)
|
||||
SETUP_BASIC_TEST(view entt/entity/view.cpp)
|
||||
|
||||
# Test graph
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
module(name = "entt_test")
|
||||
|
||||
bazel_dep(name = "rules_cc", version = "0.0.8")
|
||||
bazel_dep(name = "bazel_skylib", version = "1.4.2")
|
||||
bazel_dep(name = "googletest", version = "1.14.0")
|
||||
bazel_dep(name = "entt")
|
||||
|
||||
local_path_override(
|
||||
module_name = "entt",
|
||||
path = "..",
|
||||
)
|
||||
@@ -1 +0,0 @@
|
||||
# SEE: MODULE.bazel
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,10 +0,0 @@
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
load("@entt//bazel:copts.bzl", "COPTS")
|
||||
|
||||
package(default_visibility = ["//:__subpackages__"])
|
||||
|
||||
cc_library(
|
||||
name = "common",
|
||||
copts = COPTS,
|
||||
hdrs = glob(["*.h", "*.hpp"]),
|
||||
)
|
||||
@@ -1,25 +0,0 @@
|
||||
#ifndef ENTT_COMMON_AGGREGATE_H
|
||||
#define ENTT_COMMON_AGGREGATE_H
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace test {
|
||||
|
||||
struct aggregate {
|
||||
int value{};
|
||||
};
|
||||
|
||||
inline bool operator==(const aggregate &lhs, const aggregate &rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
|
||||
inline bool operator<(const aggregate &lhs, const aggregate &rhs) {
|
||||
return lhs.value < rhs.value;
|
||||
}
|
||||
|
||||
// ensure aggregate-ness :)
|
||||
static_assert(std::is_aggregate_v<test::aggregate>, "Not an aggregate type");
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -16,11 +16,6 @@ struct basic_test_allocator: std::allocator<Type> {
|
||||
|
||||
using std::allocator<Type>::allocator;
|
||||
|
||||
// necessary to avoid a warning by clang-cl :)
|
||||
basic_test_allocator(const basic_test_allocator &other)
|
||||
: base{other} {
|
||||
}
|
||||
|
||||
basic_test_allocator &operator=(const basic_test_allocator &other) {
|
||||
// necessary to avoid call suppression
|
||||
base::operator=(other);
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
#ifndef ENTT_COMMON_BOXED_INT_H
|
||||
#define ENTT_COMMON_BOXED_INT_H
|
||||
|
||||
namespace test {
|
||||
|
||||
struct boxed_int {
|
||||
int value{};
|
||||
};
|
||||
|
||||
inline bool operator==(const boxed_int &lhs, const boxed_int &rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -5,12 +5,10 @@ namespace test {
|
||||
|
||||
#ifdef NDEBUG
|
||||
# define ENTT_DEBUG_TEST(Case, Test) TEST(Case, DISABLED_##Test)
|
||||
# define ENTT_DEBUG_TEST_P(Case, Test) TEST_P(Case, DISABLED_##Test)
|
||||
# define ENTT_DEBUG_TEST_F(Case, Test) TEST_F(Case, DISABLED_##Test)
|
||||
# define ENTT_DEBUG_TYPED_TEST(Case, Test) TYPED_TEST(Case, DISABLED_##Test)
|
||||
#else
|
||||
# define ENTT_DEBUG_TEST(Case, Test) TEST(Case, Test)
|
||||
# define ENTT_DEBUG_TEST_P(Case, Test) TEST_P(Case, Test)
|
||||
# define ENTT_DEBUG_TEST_F(Case, Test) TEST_F(Case, Test)
|
||||
# define ENTT_DEBUG_TYPED_TEST(Case, Test) TYPED_TEST(Case, Test)
|
||||
#endif
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef ENTT_COMMON_CUSTOM_ENTITY_H
|
||||
#define ENTT_COMMON_CUSTOM_ENTITY_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace test {
|
||||
|
||||
enum custom_entity : std::uint32_t {};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef ENTT_COMMON_EMPTY_H
|
||||
#define ENTT_COMMON_EMPTY_H
|
||||
|
||||
namespace test {
|
||||
|
||||
struct empty {};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -1,11 +0,0 @@
|
||||
#ifndef ENTT_COMMON_LINTER_HPP
|
||||
#define ENTT_COMMON_LINTER_HPP
|
||||
|
||||
namespace test {
|
||||
|
||||
template<typename Type>
|
||||
void is_initialized(Type &) {}
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef ENTT_COMMON_NON_COMPARABLE_H
|
||||
#define ENTT_COMMON_NON_COMPARABLE_H
|
||||
|
||||
namespace test {
|
||||
|
||||
struct non_comparable {
|
||||
bool operator==(const non_comparable &) const = delete;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -1,17 +0,0 @@
|
||||
#ifndef ENTT_COMMON_NON_DEFAULT_CONSTRUCTIBLE_H
|
||||
#define ENTT_COMMON_NON_DEFAULT_CONSTRUCTIBLE_H
|
||||
|
||||
namespace test {
|
||||
|
||||
struct non_default_constructible {
|
||||
non_default_constructible() = delete;
|
||||
|
||||
non_default_constructible(int v)
|
||||
: value{v} {}
|
||||
|
||||
int value;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -1,20 +0,0 @@
|
||||
#ifndef ENTT_COMMON_NON_MOVABLE_H
|
||||
#define ENTT_COMMON_NON_MOVABLE_H
|
||||
|
||||
namespace test {
|
||||
|
||||
struct non_movable {
|
||||
non_movable() = default;
|
||||
|
||||
non_movable(const non_movable &) = default;
|
||||
non_movable(non_movable &&) = delete;
|
||||
|
||||
non_movable &operator=(const non_movable &) = default;
|
||||
non_movable &operator=(non_movable &&) = delete;
|
||||
|
||||
int value{};
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef ENTT_COMMON_POINTER_STABLE_H
|
||||
#define ENTT_COMMON_POINTER_STABLE_H
|
||||
|
||||
namespace test {
|
||||
|
||||
struct pointer_stable {
|
||||
static constexpr auto in_place_delete = true;
|
||||
int value{};
|
||||
};
|
||||
|
||||
inline bool operator==(const pointer_stable &lhs, const pointer_stable &rhs) {
|
||||
return lhs.value == rhs.value;
|
||||
}
|
||||
|
||||
inline bool operator<(const pointer_stable &lhs, const pointer_stable &rhs) {
|
||||
return lhs.value < rhs.value;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
@@ -2,22 +2,19 @@
|
||||
#define ENTT_COMMON_THROWING_ALLOCATOR_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <entt/container/dense_map.hpp>
|
||||
#include <entt/core/fwd.hpp>
|
||||
#include <entt/core/type_info.hpp>
|
||||
|
||||
namespace test {
|
||||
|
||||
struct throwing_allocator_exception {};
|
||||
|
||||
template<typename Type>
|
||||
class throwing_allocator {
|
||||
class throwing_allocator: std::allocator<Type> {
|
||||
template<typename Other>
|
||||
friend class throwing_allocator;
|
||||
|
||||
using base = std::allocator<Type>;
|
||||
struct test_exception {};
|
||||
|
||||
public:
|
||||
using value_type = Type;
|
||||
using pointer = value_type *;
|
||||
@@ -26,42 +23,33 @@ public:
|
||||
using const_void_pointer = const void *;
|
||||
using propagate_on_container_move_assignment = std::true_type;
|
||||
using propagate_on_container_swap = std::true_type;
|
||||
using container_type = entt::dense_map<entt::id_type, std::size_t>;
|
||||
using exception_type = test_exception;
|
||||
|
||||
template<typename Other>
|
||||
struct rebind {
|
||||
using other = throwing_allocator<Other>;
|
||||
};
|
||||
|
||||
throwing_allocator()
|
||||
: allocator{},
|
||||
config{std::allocate_shared<container_type>(allocator)} {}
|
||||
throwing_allocator() = default;
|
||||
|
||||
template<typename Other>
|
||||
throwing_allocator(const throwing_allocator<Other> &other)
|
||||
: allocator{other.allocator},
|
||||
config{other.config} {}
|
||||
: base{other} {}
|
||||
|
||||
pointer allocate(std::size_t length) {
|
||||
if(const auto hash = entt::type_id<Type>().hash(); config->contains(hash)) {
|
||||
if(auto &elem = (*config)[hash]; elem == 0u) {
|
||||
config->erase(hash);
|
||||
throw throwing_allocator_exception{};
|
||||
} else {
|
||||
--elem;
|
||||
}
|
||||
if(trigger_on_allocate) {
|
||||
trigger_on_allocate = false;
|
||||
throw test_exception{};
|
||||
}
|
||||
|
||||
return allocator.allocate(length);
|
||||
trigger_on_allocate = trigger_after_allocate;
|
||||
trigger_after_allocate = false;
|
||||
|
||||
return base::allocate(length);
|
||||
}
|
||||
|
||||
void deallocate(pointer mem, std::size_t length) {
|
||||
allocator.deallocate(mem, length);
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
void throw_counter(const std::size_t len) {
|
||||
(*config)[entt::type_id<Other>().hash()] = len;
|
||||
base::deallocate(mem, length);
|
||||
}
|
||||
|
||||
bool operator==(const throwing_allocator<Type> &) const {
|
||||
@@ -72,9 +60,8 @@ public:
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
private:
|
||||
std::allocator<Type> allocator;
|
||||
std::shared_ptr<container_type> config;
|
||||
static inline bool trigger_on_allocate{};
|
||||
static inline bool trigger_after_allocate{};
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -3,40 +3,44 @@
|
||||
|
||||
namespace test {
|
||||
|
||||
struct throwing_type_exception {};
|
||||
class throwing_type {
|
||||
struct test_exception {};
|
||||
|
||||
struct throwing_type {
|
||||
throwing_type(bool mode)
|
||||
: trigger{mode} {}
|
||||
public:
|
||||
using exception_type = test_exception;
|
||||
static constexpr auto moved_from_value = -1;
|
||||
|
||||
throwing_type(int value)
|
||||
: data{value} {}
|
||||
|
||||
throwing_type(const throwing_type &other)
|
||||
: trigger{other.trigger} {
|
||||
if(trigger) {
|
||||
throw throwing_type_exception{};
|
||||
: data{other.data} {
|
||||
if(data == trigger_on_value) {
|
||||
data = moved_from_value;
|
||||
throw exception_type{};
|
||||
}
|
||||
}
|
||||
|
||||
throwing_type &operator=(const throwing_type &other) {
|
||||
trigger = other.trigger;
|
||||
if(other.data == trigger_on_value) {
|
||||
data = moved_from_value;
|
||||
throw exception_type{};
|
||||
}
|
||||
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void throw_on_copy(const bool mode) noexcept {
|
||||
trigger = mode;
|
||||
operator int() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
bool throw_on_copy() const noexcept {
|
||||
return trigger;
|
||||
}
|
||||
static inline int trigger_on_value{};
|
||||
|
||||
private:
|
||||
bool trigger{};
|
||||
int data{};
|
||||
};
|
||||
|
||||
inline bool operator==(const throwing_type &lhs, const throwing_type &rhs) {
|
||||
return lhs.throw_on_copy() == rhs.throw_on_copy();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user