Compare commits

..

7 Commits

Author SHA1 Message Date
Michele Caini
344e03ac64 update single include file to v3.12.2 2023-06-23 10:36:30 +02:00
Michele Caini
da56665b03 registry: make ::valid backward compatible 2023-06-22 10:29:42 +02:00
Michele Caini
f6f01ef1bc snapshot: avoid warnings due to deprecated functions 2023-06-21 13:31:21 +02:00
Michele Caini
0ed514628c now working on v3.12.2 2023-06-21 11:48:04 +02:00
Michele Caini
a41421d867 update single include file to v3.12.1 2023-06-19 16:22:38 +02:00
Michele Caini
c1f6b11f7d snapshot: reintroduce support to storage listeners 2023-06-17 22:45:02 +02:00
Michele Caini
b2233064a0 now working on version v3.12.1 2023-06-15 16:44:43 +02:00
188 changed files with 16407 additions and 19052 deletions

View File

@@ -1 +0,0 @@
test

View File

@@ -1 +0,0 @@
USE_BAZEL_VERSION=6.x

View File

@@ -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

View File

@@ -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

View File

@@ -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: |

View File

@@ -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 }}

View File

@@ -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

View File

@@ -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

View File

@@ -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: |

View File

@@ -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
View File

@@ -11,5 +11,3 @@ cpp.hint
# Bazel
/bazel-*
/test/bazel-*
/user.bazelrc

View File

@@ -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,
}),
)

View File

@@ -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)

View File

@@ -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")

View File

@@ -6,11 +6,11 @@
[![Build Status](https://github.com/skypjack/entt/workflows/build/badge.svg)](https://github.com/skypjack/entt/actions)
[![Coverage](https://codecov.io/gh/skypjack/entt/branch/master/graph/badge.svg)](https://codecov.io/gh/skypjack/entt)
[![Try online](https://img.shields.io/badge/try-online-brightgreen)](https://godbolt.org/z/zxW73f)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue)](https://skypjack.github.io/entt/)
[![Vcpkg port](https://img.shields.io/vcpkg/v/entt)](https://vcpkg.link/ports/entt)
[![Conan Center](https://img.shields.io/conan/v/entt)](https://conan.io/center/recipes/entt)
[![Documentation](https://img.shields.io/badge/docs-doxygen-blue)](https://skypjack.github.io/entt/)
[![Gitter chat](https://badges.gitter.im/skypjack/entt.png)](https://gitter.im/skypjack/entt)
[![Discord channel](https://img.shields.io/discord/707607951396962417?logo=discord)](https://discord.gg/5BjPWBd)
[![Donate](https://img.shields.io/badge/donate-paypal-blue.svg)](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
View File

@@ -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

View File

@@ -0,0 +1 @@
workspace(name = "com_github_skypjack_entt")

View File

@@ -1 +0,0 @@
# SEE MODULE.bazel

View File

View File

@@ -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",
],
})

View File

@@ -1,6 +1,6 @@
#
# Doxygen configuration (documentation)
include(FetchContent)
#
FetchContent_Declare(
doxygen-awesome-css

View File

@@ -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 =

View File

@@ -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.

View File

@@ -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

View File

@@ -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([&registry](auto entity) {
if(registry.orphan(entity)) {
registry.release(entity);
}
}
});
```
In general, iterating all entities can result in poor performance. It should not

View File

@@ -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.

View File

@@ -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.

View File

@@ -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:

View File

@@ -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

View File

@@ -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

View File

@@ -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" ] }
]

View File

@@ -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&lt;*&gt;">
<Intrinsic Name="to_entity" Expression="*((traits_type::entity_type *)&amp;entity) &amp; traits_type::entity_mask">
<Parameter Name="entity" Type="traits_type::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ pools={ pools.size() } }}</DisplayString>
<Expand>
<Item Name="[entities]">entities</Item>
@@ -30,12 +33,10 @@
</Expand>
</Type>
<Type Name="entt::basic_sparse_set&lt;*&gt;">
<Intrinsic Name="cap" Expression="(traits_type::version_mask &lt;&lt; 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 &amp; (traits_type::page_size - 1)</Exec>
<If Condition="sparse[page] &amp;&amp; (*((traits_type::entity_type *)&amp;sparse[page][offset]) &lt; cap())">
<If Condition="sparse[page] &amp;&amp; (*((traits_type::entity_type *)&amp;sparse[page][offset]) &lt; ~traits_type::entity_mask)">
<Item Name="[{ pos }]">*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; 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 *)&amp;packed[pos]) &lt; cap()">
<If Condition="*((traits_type::entity_type *)&amp;packed[pos]) &lt; ~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&lt;*&gt;">
<Intrinsic Name="cap" Expression="(base_type::traits_type::version_mask &lt;&lt; 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 *)&amp;base_type::packed[pos]) &lt; cap()">
<If Condition="*((base_type::traits_type::entity_type *)&amp;base_type::packed[pos]) &lt; ~base_type::traits_type::entity_mask">
<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos &amp; (traits_type::page_size - 1)]</Item>
</If>
<Exec>++pos</Exec>
@@ -99,12 +100,10 @@
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<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&lt;*&gt;">

File diff suppressed because it is too large Load Diff

View File

@@ -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,
)

View File

@@ -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

View File

@@ -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) \

View File

@@ -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

View File

@@ -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) {

View File

@@ -3,7 +3,6 @@
#include <functional>
#include <memory>
#include <utility>
namespace entt {

View File

@@ -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;
};
/**

View File

@@ -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.

View File

@@ -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;
}
/**

View File

@@ -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).

View File

@@ -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

View File

@@ -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.

View File

@@ -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

View File

@@ -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.

View File

@@ -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 {

View File

@@ -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.

View File

@@ -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.

View File

@@ -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.

View File

@@ -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 &reg, 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 &reg, 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...> &reg, const Component &instance) {
template<typename Registry, typename Component>
typename Registry::entity_type to_entity(const Registry &reg, 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;

View File

@@ -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 &reg = 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);

View File

@@ -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.

View File

@@ -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>());
}
/**

View File

@@ -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.

View File

@@ -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 &registry) {
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.
*

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -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. */

View File

@@ -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

View File

@@ -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

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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
*/
};
/**

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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:

View File

@@ -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. */

View File

@@ -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({});
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -20,6 +20,7 @@ namespace entt {
*/
template<typename Type>
class resource {
/*! @brief Resource handles are friends with each other. */
template<typename>
friend class resource;

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -1 +0,0 @@
import "%workspace%/../.bazelrc"

View File

View File

@@ -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

View File

@@ -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 = "..",
)

View File

@@ -1 +0,0 @@
# SEE: MODULE.bazel

File diff suppressed because it is too large Load Diff

View File

@@ -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"]),
)

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -1,10 +0,0 @@
#ifndef ENTT_COMMON_EMPTY_H
#define ENTT_COMMON_EMPTY_H
namespace test {
struct empty {};
} // namespace test
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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