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
255 changed files with 22578 additions and 29509 deletions

View File

@@ -1 +0,0 @@
test

View File

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

View File

@@ -1,16 +0,0 @@
common --enable_bzlmod
build --enable_platform_specific_config
build --incompatible_enable_cc_toolchain_resolution
build --enable_runfiles
build --incompatible_strict_action_env
# required for googletest
build:linux --cxxopt=-std=c++17
build:macos --cxxopt=-std=c++17
common:ci --announce_rc
common:ci --verbose_failures
common:ci --keep_going
test:ci --test_output=errors
try-import %workspace%/user.bazelrc

View File

@@ -2,11 +2,10 @@ BasedOnStyle: llvm
---
AccessModifierOffset: -4
AlignEscapedNewlines: DontAlign
AllowShortBlocksOnASingleLine: Always
AllowShortBlocksOnASingleLine: Empty
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: WithoutElse
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBinaryOperators: NonAssignment
@@ -24,7 +23,6 @@ IncludeCategories:
Priority: 4
- Regex: '.*'
Priority: 5
IncludeIsMainRegex: "^$"
IndentPPDirectives: AfterHash
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: false

View File

@@ -1,48 +0,0 @@
Checks: >
bugprone-*,
concurrency-*,
cppcoreguidelines-*,
-cppcoreguidelines-owning-memory,
-cppcoreguidelines-pro-bounds-constant-array-index,
-cppcoreguidelines-pro-type-const-cast,
-cppcoreguidelines-pro-type-member-init,
-cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-union-access,
misc-*,
-misc-include-cleaner,
-misc-no-recursion,
modernize-*,
-modernize-use-trailing-return-type,
performance-*,
portability-*,
readability-*,
-readability-function-cognitive-complexity,
-readability-named-parameter,
-readability-uppercase-literal-suffix,
CheckOptions:
- key: cppcoreguidelines-avoid-magic-numbers.IgnoreAllFloatingPointValues
value: true
- key: cppcoreguidelines-avoid-magic-numbers.IgnorePowersOf2IntegerValues
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions
value: true
- key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctionsWhenCopyIsDeleted
value: true
- key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor
value: true
- key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
value: true
- key: misc-non-private-member-variables-in-classes.IgnorePublicMemberVariables
value: true
- key: modernize-avoid-c-arrays.AllowStringArrays
value: true
- key: readability-function-cognitive-complexity.IgnoreMacros
value: true
- key: readability-identifier-length.MinimumParameterNameLength
value: 2
- key: readability-identifier-length.MinimumVariableNameLength
value: 2
- key: readability-magic-numbers.IgnoreAllFloatingPointValues
value: true
- key: readability-magic-numbers.IgnorePowersOf2IntegerValues
value: true

View File

@@ -1,24 +1,25 @@
name: tools
name: analyzer
on:
push:
branches:
- tools
- master
- wip
jobs:
iwyu:
timeout-minutes: 60
timeout-minutes: 30
env:
IWYU: "0.22"
LLVM: "18"
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: |
@@ -58,24 +59,3 @@ jobs:
-DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" \
..
make -j4
clang-tidy:
timeout-minutes: 60
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- name: Compile tests
working-directory: build
env:
CXX: clang++
run: |
cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON -DENTT_USE_CLANG_TIDY=ON ..
make -j4
- name: Run tests
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest -C Debug -j4

View File

@@ -1,23 +0,0 @@
name: Bazel Release
on:
release:
types: [published]
jobs:
# A release archive is required for bzlmod
# See: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
bazel-release-archive:
runs-on: ubuntu-latest
continue-on-error: true
permissions:
contents: write
steps:
- uses: actions/setup-go@v5
- run: go install github.com/bazelbuild/buildtools/buildozer@latest
- uses: actions/checkout@v4
- run: ./scripts/sync_bzlmod_version.sh
- run: git archive $GITHUB_REF -o "entt-${GITHUB_REF:10}.tar.gz"
- run: gh release upload ${GITHUB_REF:10} "entt-${GITHUB_REF:10}.tar.gz"
env:
GH_TOKEN: ${{ github.token }}

View File

@@ -1,23 +0,0 @@
name: bazel
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
runs-on: ${{ matrix.os }}
continue-on-error: true
steps:
- uses: actions/checkout@v4
- run: bazelisk test --config=ci ...
working-directory: test
env:
USE_BAZEL_VERSION: 6.x

View File

@@ -9,50 +9,53 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04]
os: [ubuntu-latest, ubuntu-20.04]
compiler:
- { pkg: g++, exe: 'g++', version: 7 }
- { pkg: g++, exe: 'g++', version: 8 }
- { pkg: g++, exe: 'g++', version: 9 }
- { pkg: g++, exe: 'g++', version: 10 }
- { pkg: g++, exe: 'g++', version: 11 }
- { pkg: g++, exe: 'g++', version: 12 }
- { pkg: g++, exe: 'g++', version: 13 }
- { pkg: g++, exe: 'g++', version: 14 }
- { pkg: clang, exe: 'clang++', version: 8 }
- { pkg: clang, exe: 'clang++', version: 9 }
- { pkg: clang, exe: 'clang++', version: 10 }
- { pkg: clang, exe: 'clang++', version: 11 }
- { pkg: clang, exe: 'clang++', version: 12 }
- { pkg: clang, exe: 'clang++', version: 13 }
- { pkg: clang, exe: 'clang++', version: 14 }
- { pkg: clang, exe: 'clang++', version: 15 }
- { pkg: clang, exe: 'clang++', version: 16 }
- { pkg: clang, exe: 'clang++', version: 17 }
- { pkg: clang, exe: 'clang++', version: 18 }
exclude:
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 12 }
compiler: { pkg: g++, exe: 'g++', version: 7 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 13 }
compiler: { pkg: g++, exe: 'g++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: g++, exe: 'g++', version: 14 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 16 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 17 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 18 }
- os: ubuntu-24.04
compiler: { pkg: g++, exe: 'g++', version: 9 }
- os: ubuntu-24.04
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 8 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 9 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 10 }
- os: ubuntu-latest
compiler: { pkg: clang, exe: 'clang++', version: 11 }
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 10 }
- os: ubuntu-24.04
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 11 }
- os: ubuntu-24.04
- os: ubuntu-20.04
compiler: { pkg: g++, exe: 'g++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 12 }
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 13 }
- os: ubuntu-24.04
- os: ubuntu-20.04
compiler: { pkg: clang, exe: 'clang++', version: 14 }
- os: ubuntu-24.04
compiler: { pkg: clang, exe: 'clang++', version: 15 }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Install compiler
run: |
sudo apt update
@@ -68,15 +71,17 @@ 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
strategy:
matrix:
toolset: [default, v142, clang-cl]
toolset: [default, v141, v142, clang-cl]
include:
- toolset: v141
toolset_option: -T"v141"
- toolset: v142
toolset_option: -T"v142"
- toolset: clang-cl
@@ -85,7 +90,7 @@ jobs:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
run: |
@@ -95,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: |
@@ -112,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
@@ -126,7 +131,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
run: |
@@ -136,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,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v3
- name: Compile tests
working-directory: build
env:
@@ -28,4 +28,4 @@ jobs:
working-directory: build
env:
CTEST_OUTPUT_ON_FAILURE: 1
run: ctest -C Debug -j4
run: ctest --timeout 30 -C Debug -j4

3
.gitignore vendored
View File

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

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
@@ -25,18 +31,21 @@ endif()
message(VERBOSE "*")
message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})")
message(VERBOSE "* Copyright (c) 2017-2024 Michele Caini <michele.caini@gmail.com>")
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)
@@ -118,12 +121,10 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/config/version.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_map.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/dense_set.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/table.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/container/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/algorithm.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/any.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/attribute.h>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/bit.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/compressed_pair.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/enum.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/family.hpp>
@@ -133,7 +134,6 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/iterator.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/memory.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/monostate.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/ranges.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/tuple.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_info.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/core/type_traits.hpp>
@@ -147,7 +147,6 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/ranges.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/registry.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/runtime_view.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp>
@@ -173,6 +172,7 @@ if(ENTT_INCLUDE_HEADERS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/template.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/type_traits.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/meta/utility.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/platform/android-ndk-r17.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/fwd.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/poly/poly.hpp>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/process/fwd.hpp>
@@ -203,6 +203,7 @@ if(ENTT_HAS_NATVIS)
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/graph.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/locator.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/meta.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/platform.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/poly.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/process.natvis>
$<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/natvis/entt/resource.natvis>
@@ -215,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/.*")
endif()
if(ENTT_HAS_LIBCPP)
target_compile_options(EnTT BEFORE INTERFACE -stdlib=libc++)
endif()
#
# Install pkg-config file
#
include(JoinPaths)
@@ -242,7 +241,9 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
)
#
# Install EnTT
#
include(CMakePackageConfigHelpers)
@@ -284,17 +285,13 @@ install(
DESTINATION ${CMAKE_INSTALL_LIBDIR}/EnTT/cmake
)
install(
DIRECTORY src/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
FILES_MATCHING
PATTERN "*.h"
PATTERN "*.hpp"
)
install(DIRECTORY src/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
export(PACKAGE EnTT)
#
# Tests
#
option(ENTT_BUILD_TESTING "Enable building tests." OFF)
@@ -313,20 +310,14 @@ 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)
if(ENTT_BUILD_DOCS)
find_package(Doxygen 1.10)
find_package(Doxygen 1.8)
if(DOXYGEN_FOUND)
add_subdirectory(docs)

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017-2024 Michele Caini, author of EnTT
Copyright (c) 2017-2023 Michele Caini, author of EnTT
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,4 +0,0 @@
module(name = "entt")
bazel_dep(name = "rules_cc", version = "0.0.8")
bazel_dep(name = "bazel_skylib", version = "1.4.2")

View File

@@ -1,13 +1,16 @@
![EnTT: Gaming meets modern C++](https://user-images.githubusercontent.com/1812216/103550016-90752280-4ea8-11eb-8667-12ed2219e137.png)
<!--
@cond TURN_OFF_DOXYGEN
-->
[![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
@@ -21,7 +24,7 @@ in [**Minecraft**](https://minecraft.net/en-us/attribution/) by Mojang, the
[**ArcGIS Runtime SDKs**](https://developers.arcgis.com/arcgis-runtime/) by Esri
and the amazing [**Ragdoll**](https://ragdolldynamics.com/).<br/>
If you don't see your project in the list, please open an issue, submit a PR or
add the [\#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
add the [#entt](https://github.com/topics/entt) tag to your _topics_! :+1:
---
@@ -36,8 +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:
@@ -49,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)
@@ -61,6 +63,9 @@ Many thanks to [these people](https://skypjack.github.io/sponsorship/) and
* [EnTT in Action](#entt-in-action)
* [Contributors](#contributors)
* [License](#license)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -170,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
@@ -236,9 +250,9 @@ To use `EnTT` from a `CMake` project, just link an existing target to the
The library offers everything you need for locating (as in `find_package`),
embedding (as in `add_subdirectory`), fetching (as in `FetchContent`) or using
it in many of the ways that you can think of and that involve `CMake`.<br/>
Covering all possible cases would require a treatise and not a simple README
file, but I'm confident that anyone reading this section also knows what it's
about and can use `EnTT` from a `CMake` project without problems.
Covering all possible cases would require a treaty and not a simple README file,
but I'm confident that anyone reading this section also knows what it's about
and can use `EnTT` from a `CMake` project without problems.
## Natvis support
@@ -313,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
@@ -348,10 +350,16 @@ To navigate it with your favorite browser:
$ cd build
$ your_favorite_browser docs/html/index.html
<!--
@cond TURN_OFF_DOXYGEN
-->
The same version is also available [online](https://skypjack.github.io/entt/)
for the latest release, that is the last stable tag.<br/>
Moreover, there exists a [wiki](https://github.com/skypjack/entt/wiki) dedicated
to the project where users can find all related documentation pages.
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Tests
@@ -368,6 +376,9 @@ To build the most basic set of tests:
Note that benchmarks are not part of this set.
<!--
@cond TURN_OFF_DOXYGEN
-->
# EnTT in Action
`EnTT` is widely used in private and commercial applications. I cannot even
@@ -385,7 +396,7 @@ open an issue or a PR and I'll be glad to add them to the list.
# Contributors
Requests for features, PRs, suggestions and feedback are highly appreciated.
Requests for features, PRs, suggestions ad feedback are highly appreciated.
If you find you can help and want to contribute to the project with your
experience or you do want to get part of the project for some other reason, feel
@@ -395,15 +406,18 @@ I can't promise that each and every contribution will be accepted, but I can
assure that I'll do my best to take them all as soon as possible.
If you decide to participate, please see the guidelines for
[contributing](https://github.com/skypjack/entt/blob/master/CONTRIBUTING.md)
before to create issues or pull requests.<br/>
[contributing](CONTRIBUTING.md) before to create issues or pull
requests.<br/>
Take also a look at the
[contributors list](https://github.com/skypjack/entt/blob/master/AUTHORS) to
know who has participated so far.
<!--
@endcond TURN_OFF_DOXYGEN
-->
# License
Code and documentation Copyright (c) 2017-2024 Michele Caini.<br/>
Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/>
Colorful logo Copyright (c) 2018-2021 Richard Caseres.
Code released under

46
TODO
View File

@@ -4,41 +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 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
* self contained entity traits to avoid explicit specializations (ie enum constants)
* auto type info data from types if present
* test: push sharing types further
* after non-continuous generation for entity storage:
- get/reset placeholder to position after saving/loading (avoid long lookup)
- allow skipping/reserving entity identifiers
- documentation for reserved entities
* storage entity: no emplace/insert, rename and add a fast range-push from above
* view: propagate tombstone check request to iterator
* table: pop back to support swap and pop, single column access, empty type optimization
* checkout tools workflow
* improve front (no multiple checks) and back (ie no contains) for multi-type view
* cleanup common view from tricks to handle single swap-only and in-place, if constexpr branches
* entity based component_traits
* review cmake warning about FetchContent_Populate (need .28 and EXCLUDE_FROM_ALL for FetchContent)
* after removing meta prop vectors, copy meta objects in their handles directly
* suppress -Wself-move on CI with g++13
* view and view iterator specializations for multi, single and filtered elements
* organizer support to groups
* meta range: move id to meta objects and return plain types (?), then remove id from meta base and meta ctor too
* refine the storage fallback mechanism for views (ie alloc?)
* sigh_mixin: automatic signal registration
* sigh_mixin: change cb signature from reg/entt to storage/entt (breaking for the good)
* don't pass reactive storage by default to callback
* runtime types support for meta for types that aren't backed by C++ types

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

@@ -15,7 +15,7 @@ void update(entt::registry &registry) {
auto view = registry.view<position, velocity>();
for(auto entity: view) {
// gets only the elements that are going to be used ...
// gets only the components that are going to be used ...
auto &vel = view.get<velocity>(entity);
@@ -28,7 +28,7 @@ void update(entt::registry &registry) {
void update(std::uint64_t dt, entt::registry &registry) {
registry.view<position, velocity>().each([dt](auto &pos, auto &vel) {
// gets all the elements of the view at once ...
// gets all the components of the view at once ...
pos.x += vel.dx * dt;
pos.y += vel.dy * dt;

View File

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

View File

@@ -1,4 +1,4 @@
# Doxyfile 1.10.0
# Doxyfile 1.9.6
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -63,12 +63,6 @@ PROJECT_BRIEF =
PROJECT_LOGO =
# With the PROJECT_ICON tag one can specify an icon that is included in the tabs
# when the HTML document is shown. Doxygen will copy the logo to the output
# directory.
PROJECT_ICON =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is
# entered, it will be relative to the location where doxygen was started. If
@@ -359,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 punctuation characters removed.
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = GITHUB
# 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
@@ -494,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
#---------------------------------------------------------------------------
@@ -887,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
@@ -974,12 +942,12 @@ INPUT_FILE_ENCODING =
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl,
# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d,
# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to
# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.hpp \
@@ -1022,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 =
@@ -1135,8 +1106,7 @@ FORTRAN_COMMENT_AFTER = 72
SOURCE_BROWSER = YES
# Setting the INLINE_SOURCES tag to YES will include the body of functions,
# multi-line macros, enums or list initialized variables directly into the
# documentation.
# classes and enums directly into the documentation.
# The default value is: NO.
INLINE_SOURCES = NO
@@ -1405,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
@@ -1424,33 +1403,6 @@ HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
# dynamically folded and expanded in the generated HTML source code.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_CODE_FOLDING = YES
# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in
# the top right corner of code and text fragments that allows the user to copy
# its content to the clipboard. Note this only works if supported by the browser
# and the web page is served via a secure context (see:
# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file:
# protocol.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COPY_CLIPBOARD = YES
# Doxygen stores a couple of settings persistently in the browser (via e.g.
# cookies). By default these settings apply to all HTML pages generated by
# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store
# the settings under a project specific key, such that the user preferences will
# be stored separately.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_PROJECT_COOKIE =
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -1581,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
@@ -2079,16 +2021,9 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
# The LATEX_BATCHMODE tag signals 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.
@@ -2109,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
@@ -2274,39 +2217,13 @@ 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.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
# database with symbols found by doxygen stored in tables.
# The default value is: NO.
GENERATE_SQLITE3 = NO
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
# in front of it.
# The default directory is: sqlite3.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_OUTPUT = sqlite3
# The SQLITE3_RECREATE_DB tag is set to YES, the existing doxygen_sqlite3.db
# database file will be recreated with each doxygen run. If set to NO, doxygen
# will warn if a database file is already found and not modify it.
# The default value is: YES.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_RECREATE_DB = YES
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2449,15 +2366,15 @@ TAGFILES =
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
# will be listed in the class and namespace index. If set to NO, only the
# inherited external classes will be listed.
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the topic index. If set to NO, only the current project's groups will be
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
@@ -2471,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.
@@ -2482,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.
@@ -2535,19 +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. Explicit enabling an inheritance
# graph or choosing a different representation for an inheritance graph of a
# specific class, can be accomplished by means of the command \inheritancegraph.
# Disabling an inheritance graph can be accomplished by means of the command
# \hideinheritancegraph.
# 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
@@ -2555,21 +2473,15 @@ CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes. Explicit enabling a collaboration graph,
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
# command \collaborationgraph. Disabling a collaboration graph can be
# accomplished by means of the command \hidecollaborationgraph.
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. Explicit enabling a group
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
# of the command \groupgraph. Disabling a directory graph can be accomplished by
# means of the command \hidegroupgraph. See also the chapter Grouping in the
# manual.
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2611,8 +2523,8 @@ DOT_UML_DETAILS = NO
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
# significantly it will be wrapped across multiple lines. Some heuristics are
# applied to avoid ugly line breaks.
# significantly it will wrapped across multiple lines. Some heuristics are apply
# to avoid ugly line breaks.
# Minimum value: 0, maximum value: 1000, default value: 17.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2629,9 +2541,7 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2640,10 +2550,7 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
# an included by graph can be accomplished by means of the command
# \hideincludedbygraph.
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2683,10 +2590,7 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories. Explicit enabling a directory graph, when
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
# \directorygraph. Disabling a directory graph can be accomplished by means of
# the command \hidedirectorygraph.
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2702,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).
@@ -2739,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
@@ -2821,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

@@ -1,5 +1,8 @@
# Crash Course: configuration
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -14,6 +17,9 @@
* [ENTT_DISABLE_ASSERT](#entt_disable_assert)
* [ENTT_NO_ETO](#entt_no_eto)
* [ENTT_STANDARD_CPP](#entt_standard_cpp)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,29 +1,32 @@
# Crash Course: containers
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Containers](#containers)
* [Dense map](#dense-map)
* [Dense set](#dense-set)
* [Adaptors](#adaptors)
* [Table](#table)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
The standard C++ library offers a wide range of containers and adaptors already.
It's really difficult to do better (although it's very easy to do worse, as many
examples available online demonstrate).<br/>
The standard C++ library offers a wide range of containers and it's really
difficult to do better (although it's very easy to do worse, as many examples
available online demonstrate).<br/>
`EnTT` doesn't try in any way to replace what is offered by the standard. Quite
the opposite, given the widespread use that is made of standard containers.<br/>
However, the library also tries to fill a gap in features and functionalities by
making available some containers and adaptors initially developed for internal
use.
making available some containers initially developed for internal use.
This section of the library is likely to grow larger over time. However, for the
moment it's quite small and mainly aimed at satisfying some internal needs.<br/>
For all containers and adaptors made available, full test coverage and stability
over time is guaranteed as usual.
For all containers made available, full test coverage and stability over time is
guaranteed as usual.
# Containers
@@ -60,25 +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`).
# Adaptors
## Table
The `basic_table` class is a container adaptor which manages multiple sequential
containers together, treating them as different columns of the same table.<br/>
The `table` alias allows users to provide only the types to handle, using
`std::vector` as the default sequential container.
Only a small set of functions is provided, although very close to what the API
of the `std::vector` class offers.<br/>
The internal implementation is purposely supported by a tuple of containers
rather than a container of tuples. The purpose is to allow efficient access to
single columns and not just access to the entire data set of the table.
When a row is accessed, all data are returned in the form of a tuple containing
(possibly const) references to the elements of the row itself.<br/>
Similarly, when a table is iterated, tuples of references to table elements are
returned for each row.
Therefore, there is no need to go into the API description.

View File

@@ -1,12 +1,14 @@
# Crash Course: core functionalities
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Any as in any type](#any-as-in-any-type)
* [Small buffer optimization](#small-buffer-optimization)
* [Alignment requirement](#alignment-requirement)
* [Bit](#bit)
* [Compressed pair](#compressed-pair)
* [Enum as bitmask](#enum-as-bitmask)
* [Hashed strings](#hashed-strings)
@@ -17,6 +19,7 @@
* [Iota iterator](#iota-iterator)
* [Iterable adaptor](#iterable-adaptor)
* [Memory](#memory)
* [Power of two and fast modulus](#power-of-two-and-fast-modulus)
* [Allocator aware unique pointers](#allocator-aware-unique-pointers)
* [Monostate](#monostate)
* [Type support](#type-support)
@@ -36,6 +39,9 @@
* [Compile-time generator](#compile-time-generator)
* [Runtime generator](#runtime-generator)
* [Utilities](#utilities)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -196,22 +202,6 @@ The `basic_any` class template inspects the alignment requirements in each case,
even when not provided and may decide not to use the small buffer optimization
in order to meet them.
# Bit
Finding out the population count of an unsigned integral value (`popcount`),
whether a number is a power of two or not (`has_single_bit`) as well as the next
power of two given a random value (`next_power_of_two`) can be useful.<br/>
For example, it helps to allocate memory in pages having a size suitable for the
fast modulus:
```cpp
const std::size_t result = entt::fast_mod(value, modulus);
```
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
this type of operation is far superior in terms of performance to the basic
modulus and for this reason preferred in many areas.
# Compressed pair
Primarily designed for internal use and far from being feature complete, the
@@ -452,6 +442,22 @@ acronyms like _POCCA_, _POCMA_ or _POCS_.<br/>
I won't describe them here in detail. Instead, I recommend reading the inline
documentation to those interested in the subject.
## Power of two and fast modulus
Finding out if a number is a power of two (`is_power_of_two`) or what the next
power of two is given a random value (`next_power_of_two`) is very useful at
times.<br/>
For example, it helps to allocate memory in pages having a size suitable for the
fast modulus:
```cpp
const std::size_t result = entt::fast_mod(value, modulus);
```
Where `modulus` is necessarily a power of two. Perhaps not everyone knows that
this type of operation is far superior in terms of performance to the basic
modulus and for this reason preferred in many areas.
## Allocator aware unique pointers
A nasty thing in C++ (at least up to C++20) is the fact that shared pointers
@@ -767,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

@@ -1,5 +1,8 @@
# Crash Course: entity-component system
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -13,7 +16,7 @@
* [Observe changes](#observe-changes)
* [Entity lifecycle](#entity-lifecycle)
* [Listeners disconnection](#listeners-disconnection)
* [They call me reactive storage](#they-call-me-reactive-storage)
* [They call me Reactive System](#they-call-me-reactive-system)
* [Sorting: is it possible?](#sorting-is-it-possible)
* [Helpers](#helpers)
* [Null entity](#null-entity)
@@ -62,6 +65,9 @@
* [Iterators](#iterators)
* [Const registry](#const-registry)
* [Beyond this document](#beyond-this-document)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -376,17 +382,19 @@ In all cases, the function type of a listener is equivalent to the following:
void(entt::registry &, entt::entity);
```
All listeners are provided with the registry that triggered the notification and
the involved entity. Note also that:
In all cases, listeners are provided with the registry that triggered the
notification and the involved entity.
* Listeners for construction signals are invoked **after** components have been
created.
Note also that:
* Listeners for the construction signals are invoked **after** components have
been assigned to entities.
* Listeners designed to observe changes are invoked **after** components have
been updated.
* Listeners for destruction signals are invoked **before** components have been
destroyed.
* Listeners for the destruction signals are invoked **before** components have
been removed from entities.
There are also some limitations on what a listener can and cannot do:
@@ -433,11 +441,7 @@ registry.patch<entt::entity>(entity);
```
Destroying an entity and then updating the version of an identifier **does not**
give rise to these types of signals under any circumstances instead.<br/>
Finally, note that listeners that _observe_ the destruction of an entity are
invoked **after** all components have been removed, not **before**. This is
because the entity would be invalidated before deleting its elements otherwise,
making it difficult for the user to write component listeners.
give rise to these types of signals under any circumstances instead.
### Listeners disconnection
@@ -455,11 +459,11 @@ As a result, a listener that wants to access components, entities, or pools can
safely do so against a still valid registry, while checking for the existence of
the various elements as appropriate.
## They call me reactive storage
### They call me Reactive System
Signals are the basic tools to construct reactive systems, even if they aren't
enough on their own. `EnTT` tries to take another step in that direction with
its _reactive mixin_.<br/>
the `observer` class template.<br/>
In order to explain what reactive systems are, this is a slightly revised quote
from the documentation of the library that first introduced this tool,
[Entitas](https://github.com/sschmid/Entitas-CSharp):
@@ -475,168 +479,100 @@ On these words, however, the similarities with the proposal of `Entitas` also
end. The rules of the language and the design of the library obviously impose
and allow different things.
A reactive mixin can be used on a standalone storage with any value type
(perhaps using an alias to simplify its use):
An `observer` is initialized with an instance of a registry and a set of _rules_
that describes what are the entities to intercept. As an example:
```cpp
using reactive_storage = entt::reactive_mixin<entt::storage<void>>;
entt::registry registry{};
reactive_storage storage{};
storage.bind(registry);
entt::observer observer{registry, entt::collector.update<sprite>()};
```
In this case, it must be provided with a reference registry for subsequent
operations.<br/>
Alternatively, when using the value type provided by `EnTT`, it's also possible
to create a reactive storage directly inside a registry:
The class is default constructible and is reconfigured at any time by means of
the `connect` member function. Moreover, an observer is disconnected from the
underlying registry through the `disconnect` member function.<br/>
The `observer` offers also what is needed to query its _internal state_ and to
know if it's empty or how many entities it contains. Moreover, it can return a
raw pointer to the list of entities it contains.
However, the most important features of this class are that:
* It's iterable and therefore users can easily walk through the list of entities
by means of a range-for loop or the `each` member function.
* It's clearable and therefore users can consume the entities and literally
reset the observer after each iteration.
These aspects make the observer an incredibly powerful tool to know at any time
what are the entities that matched the given rules since the last time one
asked:
```cpp
entt::registry registry{};
auto &storage = registry.storage<entt::reactive>("observer"_hs);
for(const auto entity: observer) {
// ...
}
observer.clear();
```
In the latter case there is the advantage that, in the event of destruction of
an entity, this storage is also automatically cleaned up.<br/>
Also note that, unlike all other storage, these classes don't support signals by
default (although they can be enabled if necessary).
Once it has been created and associated with a registry, the reactive mixin
needs to be informed about what it should _observe_.<br/>
Here the choice boils down to three main events affecting all elements (entities
or components), namely creation, update or destruction:
The snippet above is equivalent to the following:
```cpp
storage
// observe position component construction
.on_construct<position>()
// observe velocity component update
.on_update<velocity>()
// observe renderable component destruction
.on_destroy<renderable>();
observer.each([](const auto entity) {
// ...
});
```
It goes without saying that it's possible to observe multiple events of the same
type or of different types with the same storage.<br/>
For example, to know which entities have been assigned or updated a component of
a certain type:
At least as long as the `observer` isn't const. This means that the non-const
overload of `each` does also reset the underlying data structure before to
return to the caller, while the const overload does not for obvious reasons.
```cpp
storage
.on_construct<my_type>()
.on_update<my_type>();
```
A `collector` is a utility aimed to generate a list of `matcher`s (the actual
rules) to use with an `observer`.<br/>
There are two types of `matcher`s:
Note that all configurations are in _or_ and never in _and_. Therefore, to track
entities that have been assigned two different components, there are a couple of
options:
* Create two reactive storage, then combine them in a view:
* Observing matcher: an observer returns at least the entities for which one or
more of the given components have been updated and not yet destroyed.
```cpp
first_storage.on_construct<position>();
second_storage.on_construct<velocity>();
for(auto entity: entt::basic_view{first_storage, second_storage}) {
// ...
}
entt::collector.update<sprite>();
```
* Use a reactive storage with a non-`void` value type and a custom tracking
function for the purpose:
Where _updated_ means that all listeners attached to `on_update` are invoked.
In order for this to happen, specific functions such as `patch` must be used.
Refer to the specific documentation for more details.
* Grouping matcher: an observer returns at least the entities that would have
entered the given group if it existed and that would have not yet left it.
```cpp
using my_reactive_storage = entt::reactive_mixin<entt::storage<bool>>;
void callback(my_reactive_storage &storage, const entt::registry &, const entt::entity entity) {
storage.contains(entity) ? (storage.get(entity) = true) : storage.emplace(entity, false);
}
// ...
my_reactive_storage storage{};
storage
.on_construct<position, &callback>()
.on_construct<velocity, &callback>();
// ...
for(auto [entity, both_were_added]: storage.each()) {
if(both_were_added) {
// ...
}
}
entt::collector.group<position, velocity>(entt::exclude<destroyed>);
```
As highlighted in the last example, the reactive mixin tracks the entities that
match the given conditions and saves them aside. However, this behavior can be
changed.<br/>
For example, it's possible to _capture_ all and only the entities for which a
certain component has been updated but only if a specific value is within a
given range:
A grouping matcher supports also exclusion lists as well as single components.
Roughly speaking, an observing matcher intercepts the entities for which the
given components are updated while a grouping matcher tracks the entities that
have assigned the given components since the last time one asked.<br/>
If an entity already has all the components except one and the missing type is
assigned to it, the entity is intercepted by a grouping matcher.
In addition, matchers support filtering by means of a `where` clause:
```cpp
void callback(reactive_storage &storage, const entt::registry &registry, const entt::entity entity) {
storage.remove(entity);
if(const auto x = registry.get<position>(entity).x; x >= min_x && x <= max_x) {
storage.emplace(entity);
}
}
// ...
storage.on_update<position, &callback>();
entt::collector.update<sprite>().where<position>(entt::exclude<velocity>);
```
This makes reactive storage extremely flexible and usable in a large number of
cases.<br/>
Finally, once the entities of interest have been collected, it's possible to
_visit_ the storage like any other:
This clause introduces a way to intercept entities if and only if they are
already part of a hypothetical group. If they are not, they aren't returned by
the observer, no matter if they matched the given rule.<br/>
In the example above, whenever the component `sprite` of an entity is updated,
the observer checks the entity itself to verify that it has at least `position`
and has not `velocity`. If one of the two conditions isn't satisfied, the entity
is discarded, no matter what.
```cpp
for(auto entity: storage) {
// ...
}
```
Wrapping it in a view and combining it with other views is another option:
```cpp
for(auto [entity, pos]: (entt:.basic_view{storage} | registry.view<position>(entt::exclude<velocity>)).each()) {
// ...
}
```
In order to simplify this last use case, the reactive mixin also provides a
specific function that returns a view of the storage already filtered according
to the provided requirements:
```cpp
for(auto [entity, pos]: storage.view<position>(entt::exclude<velocity>).each()) {
// ...
}
```
The registry used in this case is the one associated with the storage and also
available via the `registry` function.
It should be noted that a reactive storage never deletes its entities (and
elements, if any). To process and then discard entities at regular intervals,
refer to the `clear` function available by default for each storage type.<br/>
Similarly, the reactive mixin doesn't disconnect itself from observed storages
upon destruction. Therefore, users have to do this themselves:
```cpp
entt::registry = storage.registry();
registry.on_construct<position>().disconnect(&storage);
registry.on_construct<velocity>().disconnect(&storage);
```
Destroying a reactive storage without disconnecting it from observed pools will
result in undefined behavior.
A `where` clause accepts a theoretically unlimited number of types as well as
multiple elements in the exclusion list. Moreover, every matcher can have its
own clause and multiple clauses for the same matcher are combined in a single
one.
## Sorting: is it possible?
@@ -647,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;
});
```
@@ -756,15 +692,14 @@ to create tombstones.
### To entity
This function accepts a storage and an instance of a component of the storage
type, then it returns the entity associated with the latter:
This function accepts a registry and an instance of a component and returns the
entity associated with the latter:
```cpp
const auto entity = entt::to_entity(registry.storage<position>(), instance);
const auto entity = entt::to_entity(registry, position);
```
Where `instance` is a component of type `position`. A null entity is returned in
case the instance doesn't belong to the registry.
A null entity is returned in case the component doesn't belong to the registry.
### Dependencies
@@ -984,7 +919,7 @@ accessible by both type and _name_ for convenience. The _name_ isn't really a
name though. In fact, it's a numeric id of type `id_type` used as a key for the
variable. Any value is accepted, even runtime ones.<br/>
The context is returned via the `ctx` functions and offers a minimal set of
features including the following:
feature including the following:
```cpp
// creates a new context variable by type and returns it
@@ -1012,9 +947,9 @@ registry.ctx().erase<my_type>();
registry.ctx().erase<my_type>("my_variable"_hs);
```
A context variable must be both default constructible and movable. If the
supplied type doesn't match that of the variable when using a _name_, the
operation fails.<br/>
Context variable must be both default constructible and movable. If the supplied
type doesn't match that of the variable when using a _name_, the operation
fails.<br/>
For all users who want to use the context but don't want to create elements, the
`contains` and `find` functions are also available:
@@ -1385,6 +1320,16 @@ fact, entities are subject to different rules with respect to components
marked as _ready for reuse_. To iterate all the entities it's necessary to
iterate the underlying sparse set instead.
Moreover, the entity storage offers a couple of additional utilities such as:
* The `in_use` function which is used to know how many entities are still
_in use_. When combined with `size`, it also makes it possible to know how
many entities are available for recycling.
* The `pack` function which is used to make a given set of entities contiguous.
This is particularly useful to pass valid lists of entities via iterators
(with access usually optimized within the library).
This kind of storage is designed to be used where any other storage is fine and
can therefore be combined with views, groups and so on.
@@ -1427,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()) {
// ...
}
```
@@ -1541,11 +1486,11 @@ the mixins. The latter can then make use of any information, which is set via
`bind`:
```cpp
base.bind(registry);
base.bind(entt::forward_as_any(registry));
```
The `bind` function accepts any element by reference or by value and forwards it
to derived classes.<br/>
The `bind` function accepts an `entt::any` object, that is a _typed type-erased_
value.<br/>
This is how a registry _passes_ itself to all pools that support signals and
also why a storage keeps sending events without requiring the registry to be
passed to it every time.
@@ -1565,7 +1510,7 @@ pointer and behaves differently depending on the case:
* When the pointer is null, the function tries to default-construct an instance
of the object to bind to the entity and returns true on success.
* When the pointer is not null, the function tries to copy-construct an instance
* When the pointer is non-null, the function tries to copy-construct an instance
of the object to bind to the entity and returns true on success.
This means that, starting from a reference to the base, it's possible to bind
@@ -1604,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
@@ -1872,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>()) {
// ...
}
```
@@ -2163,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

@@ -1,5 +1,8 @@
# Frequently Asked Questions
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -7,10 +10,13 @@
* [Why is my debug build on Windows so slow?](#why-is-my-debug-build-on-windows-so-slow)
* [How can I represent hierarchies with my components?](#how-can-i-represent-hierarchies-with-my-components)
* [Custom entity identifiers: yay or nay?](#custom-entity-identifiers-yay-or-nay)
* [Warning C4003: the min, the max and the macro](#warning-c4003-the-min-the-max-and-the-macro)
* [Warning C4307: integral constant overflow](#warning-C4307-integral-constant-overflow)
* [Warning C4003: the min, the max and the macro](#warning-C4003-the-min-the-max-and-the-macro)
* [The standard and the non-copyable types](#the-standard-and-the-non-copyable-types)
* [Which functions trigger which signals](#which-functions-trigger-which-signals)
* [Duplicate storage for the same component](#duplicate-storage-for-the-same-component)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -98,6 +104,32 @@ enum class entity: std::uint32_t {};
There is no limit to the number of identifiers that can be defined.
## Warning C4307: integral constant overflow
According to [this](https://github.com/skypjack/entt/issues/121) issue, using a
hashed string under VS (toolset v141) could generate a warning.<br/>
First of all, I want to reassure you: it's expected and harmless. However, it
can be annoying.
To suppress it and if you don't want to suppress all the other warnings as well,
here is a workaround in the form of a macro:
```cpp
#if defined(_MSC_VER)
#define HS(str) __pragma(warning(suppress:4307)) entt::hashed_string{str}
#else
#define HS(str) entt::hashed_string{str}
#endif
```
With an example of use included:
```cpp
constexpr auto identifier = HS("my/resource/identifier");
```
Thanks to [huwpascoe](https://github.com/huwpascoe) for the courtesy.
## Warning C4003: the min, the max and the macro
On Windows, a header file defines two macros `min` and `max` which may result in
@@ -181,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

@@ -1,5 +1,8 @@
# Crash Course: graph
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -11,6 +14,9 @@
* [Fake resources and order of execution](#fake-resources-and-order-of-execution)
* [Sync points](#sync-points)
* [Execution graph](#execution-graph)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,11 +1,17 @@
# Push EnTT across boundaries
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Working across boundaries](#working-across-boundaries)
* [Smooth until proven otherwise](#smooth-until-proven-otherwise)
* [Meta context](#meta-context)
* [Memory management](#memory-management)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Working across boundaries

View File

@@ -1,5 +1,8 @@
# EnTT in Action
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -8,6 +11,9 @@
* [Engines and the like](#engines-and-the-like)
* [Articles, videos and blog posts](#articles-videos-and-blog-posts)
* [Any Other Business](#any-other-business)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -33,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.
@@ -52,7 +55,7 @@ I hope the following lists can grow much more in the future.
* Apparently [D&D Dark Alliance](https://darkalliance.wizards.com) by
[Wizards of the Coast](https://company.wizards.com): your party, their
funeral.
* [TiltedEvolution](https://github.com/tiltedphoques/TiltedEvolution) by
* [TiltedOnline](https://github.com/tiltedphoques/TiltedOnline) by
[Tilted Phoques](https://github.com/tiltedphoques): Skyrim and Fallout 4 mod
to play online.
* [Antkeeper](https://github.com/antkeeper/antkeeper-source): an ant colony
@@ -121,30 +124,9 @@ 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`.
* [PalmRide: After Flight](https://store.steampowered.com/app/2812540/PalmRide_After_Flight/):
an on-rails shooter with retro outrun aesthetics.
* [Exhibition of Speed](https://store.steampowered.com/app/2947450/Exhibition_of_Speed/):
build your own car and go racing.
* [Lichgate](https://buas.itch.io/lichgate): top-down action rogue-like where
users unlock abilities to fight horde of enemies in an endless world.
* [Letalka](https://github.com/dviglo2d-learn/mini_games/tree/main/letalka):
small demo game with ships and bullets flying everywhere on the screen.
## Engines and the like:
* [Hazel Engine](https://github.com/TheCherno/Hazel): a work in progress
engine created by [The Cherno](https://github.com/TheCherno/Hazel) during
one of his most famous video series.
* [Aether Engine](https://hadean.com/spatial-simulation/)
[v1.1+](https://docs.hadean.com/v1.1/Licenses/) by
[Hadean](https://hadean.com/): a library designed for spatially partitioning
@@ -218,22 +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.
* [Core SDK of Trollworks engine](https://github.com/trollworks/sdk-core): 2D
game engine based on procrastination.
* [Rocky](https://github.com/pelicanmapping/rocky): 3D geospatial application
engine.
* [Donner](https://github.com/jwmcglynn/donner): a modern C++20 SVG2 rendering
API with CSS3.
* [Coral Engine](https://github.com/GuusKemperman/CoralEngine): open-source
student engine with the tools to make games in C++ and Visual scripting.
engine entirely implemented as an entity-component-ystem.
## Articles, videos and blog posts:
@@ -260,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

@@ -1,10 +1,16 @@
# Crash Course: service locator
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Service locator](#service-locator)
* [Opaque handles](#opaque-handles)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,5 +1,8 @@
# Crash Course: runtime reflection system
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -15,11 +18,12 @@
* [From void to any](#from-void-to-any)
* [Policies: the more, the less](#policies-the-more-the-less)
* [Named constants and enums](#named-constants-and-enums)
* [User defined data](#user-defined-data)
* [Traits](#traits)
* [Custom data](#custom-data)
* [Properties and meta objects](#properties-and-meta-objects)
* [Unregister types](#unregister-types)
* [Meta context](#meta-context)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -381,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:
@@ -472,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:
@@ -837,91 +833,48 @@ auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>();
All this happens behind the scenes without any allocation because of the small
object optimization performed by the `meta_any` class.
## User defined data
## Properties and meta objects
Sometimes (for example, when it comes to creating an editor) it might be useful
to attach _traits_ or arbitrary _custom data_ to the meta objects created.
The main difference between them is that:
* Traits are simple user-defined flags with much higher access performance. The
library reserves up to 16 bits for traits, that is 16 flags for a bitmask or
2^16 values otherwise.
* Custom data are stored in a generic quick access area reserved for the user
and which the library will never use under any circumstances.
In all cases, this support is currently available only for meta types, meta data
and meta functions.
### Traits
User-defined traits are set via a meta factory:
to attach properties to the meta objects created. Fortunately, this is possible
for most of them:
```cpp
entt::meta<my_type>().traits(my_traits::required | my_traits::hidden);
entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message");
```
In the example above, `EnTT` bitmask enum support is used but any integral value
is fine, as long as it doesn't exceed 16 bits.<br/>
It's not possible to assign traits at different times. Therefore, multiple calls
to the `traits` function overwrite previous values. However, traits can be read
from meta objects and used to update existing data with a factory, effectively
extending them as needed.<br/>
Likewise, users can also set traits on meta objects later if needed, as long as
the factory is reset to the meta object of interest:
Properties are always in the key/value form. The key is a numeric identifier,
mostly similar to the identifier used to register meta objects. There are no
restrictions on the type of the value instead, as long as it's movable.<br/>
Key only properties are also supported out of the box:
```cpp
entt::meta<my_type>()
.data<&my_type::data_member, entt::as_ref_t>("member"_hs)
.traits(my_traits::internal);
entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only);
```
Once created, all meta objects offer a member function named `traits` to get the
currently set value:
To attach multiple properties to a meta object, just invoke `prop` more than
once.<br/>
It's also possible to call `prop` at different times, as long as the factory is
reset to the meta object of interest.
The meta objects for which properties are supported are currently meta types,
meta data and meta functions.<br/>
These types also offer a couple of member functions named `prop` to iterate all
properties at once or to search a specific property by key:
```cpp
auto value = entt::resolve<my_type>().traits<my_traits>();
// iterate all properties of a meta type
for(auto &&[id, prop]: entt::resolve<my_type>().prop()) {
// ...
}
// search for a given property by name
auto prop = entt::resolve<my_type>().prop("tooltip"_hs);
```
Note that the type is erased upon registration and must therefore be repeated
when traits are _extracted_, so as to allow the library to _reconstruct_ them
correctly.
### Custom data
Custom arbitrary data are set via a meta factory:
```cpp
entt::meta<my_type>().custom<type_data>("name");
```
The way to do this is by specifying the data type to the `custom` function and
passing the necessary arguments to construct it correctly.<br/>
It's not possible to assign custom data at different times. Therefore, multiple
calls to the `custom` function overwrite previous values. However, this value
can be read from meta objects and used to update existing data with a factory,
effectively updating them as needed.<br/>
Likewise, users can also set custom data on meta objects later if needed, as
long as the factory is reset to the meta object of interest:
```cpp
entt::meta<my_type>()
.func<&my_type::member_function>("member"_hs)
.custom<function_data>("tooltip");
```
Once created, all meta objects offer a member function named `custom` to get the
currently set value as a reference or as a pointer to an element:
```cpp
const type_data &value = entt::resolve<my_type>().custom();
```
Note that the returned object performs an extra check in debug before converting
to the requested type, so as to avoid subtle bugs.<br/>
Only in the case of conversion to a pointer is this check safe and such that a
null pointer is returned to inform the user of the failed attempt.
Meta properties are objects having a fairly poor interface, all in all. They
only provide the `value` member function to retrieve the contained value in the
form of a `meta_any` object.
## Unregister types

View File

@@ -1,5 +1,8 @@
# Crash Course: poly
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -11,6 +14,9 @@
* [Inheritance](#inheritance)
* [Static polymorphism in the wild](#static-polymorphism-in-the-wild)
* [Storage size and alignment requirement](#storage-size-and-alignment-requirement)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -230,8 +236,8 @@ For a deduced concept, inheritance is achieved in a few steps:
```cpp
struct DrawableAndErasable: entt::type_list<> {
template<typename Base>
struct type: typename Drawable::type<Base> {
static constexpr auto base = Drawable::impl<Drawable::type<entt::poly_inspector>>::size;
struct type: typename Drawable::template type<Base> {
static constexpr auto base = std::tuple_size_v<typename entt::poly_vtable<Drawable>::type>;
void erase() { entt::poly_call<base + 0>(*this); }
};
@@ -267,7 +273,8 @@ a `decltype` as it follows:
```cpp
struct DrawableAndErasable: entt::type_list_cat_t<
decltype(as_type_list(std::declval<Drawable>())),
entt::type_list<void()>> {
entt::type_list<void()>
> {
// ...
};
```

View File

@@ -1,11 +1,17 @@
# Crash Course: cooperative scheduler
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The process](#the-process)
* [Adaptor](#adaptor)
* [The scheduler](#the-scheduler)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -171,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

@@ -1,9 +1,15 @@
# Similar projects
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [Similar projects](#similar-projects)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction
@@ -49,12 +55,8 @@ details.
inspired archetype ECS with optional multithreading.
* [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for
C# and Unity, where _reactive systems_ were invented.
* [Friflo Engine ECS](https://github.com/friflo/Friflo.Json.Fliox/blob/main/Engine/README.md):
an archetype ECS with focus on performance, cache locality and DX.
* [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity
Component System framework.
* [Massive ECS](https://github.com/nilpunch/massive): sparse set based ECS
featuring rollbacks.
* [Svelto.ECS](https://github.com/sebas77/Svelto.ECS): a very interesting
platform agnostic and table based ECS framework.
@@ -77,6 +79,7 @@ details.
entity registry for ECS designs inspired by `EnTT`.
* Rust:
* [Legion](https://github.com/TomGillen/legion): a chunk based archetype ECS.
* [Shipyard](https://github.com/leudz/shipyard): it borrows some ideas from
`EnTT` and offers a sparse sets based ECS with grouping functionalities.
* [Sparsey](https://github.com/LechintanTudor/sparsey): sparse set based ECS

View File

@@ -1,12 +1,18 @@
# Crash Course: resource management
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
* [The resource, the loader and the cache](#the-resource-the-loader-and-the-cache)
* [Resource handle](#resource-handle)
* [Loaders](#loaders)
* [The cache class](#the-cache-class)
* [Loaders](#loader)
* [The cache class](#the-cache)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,5 +1,8 @@
# Crash Course: events, signals and everything in between
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Introduction](#introduction)
@@ -11,6 +14,9 @@
* [Event dispatcher](#event-dispatcher)
* [Named queues](#named-queues)
* [Event emitter](#event-emitter)
<!--
@endcond TURN_OFF_DOXYGEN
-->
# Introduction

View File

@@ -1,15 +1,19 @@
# EnTT and Unreal Engine
<!--
@cond TURN_OFF_DOXYGEN
-->
# Table of Contents
* [Enable Cpp17](#enable-cpp17)
* [EnTT as a third party module](#entt-as-a-third-party-module)
* [Include EnTT](#include-entt)
<!--
@endcond TURN_OFF_DOXYGEN
-->
## Enable Cpp17
> Skip this part if you are working with UE5, Since UE5 uses cpp17 by default.
As of writing (Unreal Engine v4.25), the default C++ standard of Unreal Engine
is C++14.<br/>
On the other hand, note that `EnTT` requires C++17 to compile. To enable it, in

View File

@@ -1,53 +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": [ "@[\"<].*/container/fwd\\.hpp[\">]", "private", "<entt/container/table.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/any.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/compressed_pair.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/family.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/hashed_string.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/ident.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/monostate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_info.hpp>", "public" ] },
{ "include": [ "@[\"<].*/core/fwd\\.hpp[\">]", "private", "<entt/core/type_traits.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/component.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/entity.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/group.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/handle.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/helper.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/mixin.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/observer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/organizer.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/ranges.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/registry.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/runtime_view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/snapshot.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/sparse_set.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/storage.hpp>", "public" ] },
{ "include": [ "@[\"<].*/entity/fwd\\.hpp[\">]", "private", "<entt/entity/view.hpp>", "public" ] },
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/adjacency_matrix.hpp>", "public" ] },
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/dot.hpp>", "public" ] },
{ "include": [ "@[\"<].*/graph/fwd\\.hpp[\">]", "private", "<entt/graph/flow.hpp>", "public" ] },
{ "include": [ "@[\"<].*/meta/fwd\\.hpp[\">]", "private", "<entt/meta/meta.hpp>", "public" ] },
{ "include": [ "@[\"<].*/poly/fwd\\.hpp[\">]", "private", "<entt/poly/poly.hpp>", "public" ] },
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/process.hpp>", "public" ] },
{ "include": [ "@[\"<].*/process/fwd\\.hpp[\">]", "private", "<entt/process/scheduler.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/cache.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/loader.hpp>", "public" ] },
{ "include": [ "@[\"<].*/resource/fwd\\.hpp[\">]", "private", "<entt/resource/resource.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/delegate.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/dispatcher.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/emitter.hpp>", "public" ] },
{ "include": [ "@[\"<].*/signal/fwd\\.hpp[\">]", "private", "<entt/signal/sigh.hpp>", "public" ] },
# symbols
{ symbol: [ "std::allocator", private, "<entt/container/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/entity/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/graph/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/process/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/resource/fwd.hpp>", public ] },
{ symbol: [ "std::allocator", private, "<entt/signal/fwd.hpp>", public ] }
{ "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

@@ -30,10 +30,4 @@
</IndexListItems>
</Expand>
</Type>
<Type Name="entt::basic_table&lt;*&gt;">
<DisplayString>{ payload }</DisplayString>
<Expand>
<ExpandedItem>payload</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -8,7 +8,7 @@
<Intrinsic Name="first" Optional="true" Expression="*(first_base::base_type*)this"/>
<Intrinsic Name="second" Optional="true" Expression="((second_base*)this)->value"/>
<Intrinsic Name="second" Optional="true" Expression="*(second_base::base_type*)this"/>
<DisplayString>({ first() }, { second() })</DisplayString>
<DisplayString >({ first() }, { second() })</DisplayString>
<Expand>
<Item Name="[first]">first()</Item>
<Item Name="[second]">second()</Item>

View File

@@ -1,61 +1,47 @@
<?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>
<Synthetic Name="[pools]">
<DisplayString>{ pools.size() }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="pools.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ pools.packed.first_base::value[pos].element.first }]">
*pools.packed.first_base::value[pos].element.second,view(simple)
</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
<IndexListItems ExcludeView="simple">
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<IndexListItems IncludeView="simple">
<Size>pools.size()</Size>
<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
<Item Name="[groups]">groups.size()</Item>
<Item Name="[groups]" ExcludeView="simple">groups.size()</Item>
<Synthetic Name="[vars]">
<DisplayString>{ vars.ctx.size() }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="vars.ctx.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ vars.ctx.packed.first_base::value[pos].element.first }]">
vars.ctx.packed.first_base::value[pos].element.second
</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
<IndexListItems>
<Size>vars.ctx.size()</Size>
<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::basic_sparse_set&lt;*&gt;">
<Intrinsic Name="is_valid_position" Expression="sparse[page] &amp;&amp; ((*((traits_type::entity_type *)&amp;sparse[page][offset]) &amp; traits_type::entity_mask) != traits_type::entity_mask)">
<Parameter Name="page" Type="traits_type::entity_type"/>
<Parameter Name="offset" Type="traits_type::entity_type"/>
</Intrinsic>
<Intrinsic Name="is_valid_entity" Expression="!traits_type::version_mask || (*((traits_type::entity_type *)&amp;entity) &lt; (traits_type::version_mask &lt;&lt; traits_type::length))">
<Parameter Name="entity" Type="const traits_type::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ size={ packed.size() }, type={ info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item>
<Item Name="[policy]" ExcludeView="simple">mode,en</Item>
<Item Name="[free_list]" ExcludeView="simple">head</Item>
<Item Name="[policy]">mode,en</Item>
<Synthetic Name="[sparse]">
<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString>
<Expand>
<CustomListItems>
<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<Variable Name="pos" InitialValue="0"/>
<Variable Name="page" InitialValue="0"/>
<Variable Name="offset" InitialValue="0"/>
@@ -64,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="is_valid_position(page, offset)">
<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>
@@ -75,12 +61,13 @@
<Synthetic Name="[packed]">
<DisplayString>{ packed.size() }</DisplayString>
<Expand>
<CustomListItems>
<ExpandedItem IncludeView="simple">packed,view(simple)</ExpandedItem>
<CustomListItems ExcludeView="simple">
<Variable Name="pos" InitialValue="0"/>
<Variable Name="last" InitialValue="packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="is_valid_entity(packed[pos])">
<If Condition="*((traits_type::entity_type *)&amp;packed[pos]) &lt; ~traits_type::entity_mask">
<Item Name="[{ pos }]">packed[pos]</Item>
</If>
<Exec>++pos</Exec>
@@ -91,22 +78,20 @@
</Expand>
</Type>
<Type Name="entt::basic_storage&lt;*&gt;">
<Intrinsic Name="is_valid_entity" Expression="!base_type::traits_type::version_mask || (*((base_type::traits_type::entity_type *)&amp;entity) &lt; (base_type::traits_type::version_mask &lt;&lt; base_type::traits_type::length))">
<Parameter Name="entity" Type="const base_type::traits_type::value_type &amp;"/>
</Intrinsic>
<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString>
<Expand>
<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item>
<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item>
<Item Name="[placeholder]" Optional="true" ExcludeView="simple">placeholder</Item>
<Item Name="[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 :) -->
<CustomListItems Condition="payload.size() != 0" Optional="true">
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="base_type::packed.size()"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="is_valid_entity(base_type::packed[pos])">
<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>
@@ -114,26 +99,11 @@
</CustomListItems>
</Expand>
</Type>
<Type Name="entt::basic_common_view&lt;*,*,*&gt;">
<DisplayString Condition="index != $T2">{{ size_hint={ pools[index]->packed.size() } }}</DisplayString>
<DisplayString>{{ size_hint=0 }}</DisplayString>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString>{{ size_hint={ view->packed.size() } }}</DisplayString>
<Expand>
<Item Name="[pools]">pools,na</Item>
<Item Name="[filter]">filter,na</Item>
<Item Name="[handle]" Condition="index != $T2">pools[index],na</Item>
</Expand>
</Type>
<Type Name="entt::basic_storage_view&lt;*&gt;">
<DisplayString Condition="leading != nullptr">{{ size={ leading->packed.size() } }}</DisplayString>
<DisplayString>{{ size=0 }}</DisplayString>
<Expand>
<Item Name="[handle]" Condition="leading != nullptr">leading,na</Item>
</Expand>
</Type>
<Type Name="entt::basic_view&lt;*&gt;">
<DisplayString>{ *(base_type*)this }</DisplayString>
<Expand>
<ExpandedItem>*(base_type*)this</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::basic_runtime_view&lt;*&gt;">
@@ -144,34 +114,6 @@
<Item Name="[filter]">filter,na</Item>
</Expand>
</Type>
<Type Name="entt::basic_handle&lt;*&gt;">
<Intrinsic Name="pool_at" Expression="owner->pools.packed.first_base::value[index].element.second._Ptr">
<Parameter Name="index" Type="unsigned int"/>
</Intrinsic>
<DisplayString>{{ entity={ entt } }}</DisplayString>
<Expand>
<Item Name="[entity]">entt</Item>
<Item Name="[registry]" Condition="owner != nullptr">owner,na</Item>
<Synthetic Name="[components]" Condition="owner != nullptr">
<Expand>
<CustomListItems>
<Variable Name="entity_mask" InitialValue="traits_type::entity_mask"/>
<Variable Name="page" InitialValue="((*((traits_type::entity_type *)&amp;entt)) &amp; entity_mask) / traits_type::page_size"/>
<Variable Name="offset" InitialValue="(*((traits_type::entity_type *)&amp;entt)) &amp; (traits_type::page_size - 1u)"/>
<Variable Name="last" InitialValue="owner->pools.packed.first_base::value.size()"/>
<Variable Name="pos" InitialValue="0u"/>
<Loop>
<Break Condition="pos == last"/>
<If Condition="pool_at(pos)->sparse.size() &gt; page &amp;&amp; pool_at(pos)->sparse[page] != nullptr &amp;&amp; ((*((traits_type::entity_type *)&amp;pool_at(pos)->sparse[page][offset])) &amp; entity_mask) != entity_mask">
<Item Name="[{ pool_at(pos)->info->alias,na }:{ ((*((traits_type::entity_type *)&amp;pool_at(pos)->sparse[page][offset])) &amp; entity_mask) != entity_mask }]">pool_at(pos),view(simple)nanr</Item>
</If>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::null_t">
<DisplayString>&lt;null&gt;</DisplayString>
</Type>

View File

@@ -1,98 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="entt::internal::meta_base_node">
<DisplayString Condition="resolve != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_conv_node">
<DisplayString Condition="conv != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_ctor_node">
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_custom_node">
<DisplayString Condition="value != nullptr">{{ type={ type } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[type]">type</Item>
<Item Name="[value]">value</Item>
</Expand>
<DisplayString>{{ arity={ arity } }}</DisplayString>
</Type>
<Type Name="entt::internal::meta_data_node">
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="get != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<DisplayString>{{ arity={ arity } }}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
<Item Name="[prop]">prop,view(simple)</Item>
<Item Name="[custom]">custom</Item>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Item Name="[prop]">prop</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_dtor_node">
<DisplayString>{{}}</DisplayString>
<Expand/>
</Type>
<Type Name="entt::internal::meta_func_node" >
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="invoke != nullptr">{{ id={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<DisplayString>{{ arity={ arity } }}</DisplayString>
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[arity]">arity</Item>
<Item Name="[is_const]">has_trait(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_trait(entt::internal::meta_traits::is_static)</Item>
<Item Name="[is_const]">has_property(entt::internal::meta_traits::is_const)</Item>
<Item Name="[is_static]">has_property(entt::internal::meta_traits::is_static)</Item>
<Item Name="[next]" Condition="next != nullptr">*next</Item>
<Item Name="[prop]">prop,view(simple)</Item>
<Item Name="[custom]">custom</Item>
<Item Name="[prop]">prop</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_prop_node">
<DisplayString Condition="value != nullptr">{{ key={ id } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[key]">id</Item>
<Item Name="[value]">value</Item>
</Expand>
<DisplayString>{ value }</DisplayString>
</Type>
<Type Name="entt::internal::meta_template_node">
<DisplayString Condition="arity != 0u">{{ arity={ arity } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<Item Name="[arity]">arity</Item>
</Expand>
</Type>
<Type Name="entt::internal::meta_type_descriptor">
<DisplayString/>
<Expand>
<Item Name="[ctor]">ctor,view(simple)</Item>
<Item Name="[base]">base,view(simple)</Item>
<Item Name="[conv]">conv,view(simple)</Item>
<Item Name="[data]">data,view(simple)</Item>
<Item Name="[func]">func,view(simple)</Item>
<Item Name="[prop]">prop,view(simple)</Item>
</Expand>
<DisplayString>{{ arity={ arity } }}</DisplayString>
</Type>
<Type Name="entt::internal::meta_type_node">
<Intrinsic Name="has_trait" Expression="!!(traits &amp; property)">
<Intrinsic Name="has_property" Expression="!!(traits &amp; property)">
<Parameter Name="property" Type="int"/>
</Intrinsic>
<DisplayString Condition="info != nullptr">{{ type={ info->alias,na } }}</DisplayString>
@@ -100,22 +48,20 @@
<Expand>
<Item Name="[id]">id</Item>
<Item Name="[sizeof]">size_of</Item>
<Item Name="[is_arithmetic]">has_trait(entt::internal::meta_traits::is_arithmetic)</Item>
<Item Name="[is_integral]">has_trait(entt::internal::meta_traits::is_integral)</Item>
<Item Name="[is_signed]">has_trait(entt::internal::meta_traits::is_signed)</Item>
<Item Name="[is_array]">has_trait(entt::internal::meta_traits::is_array)</Item>
<Item Name="[is_enum]">has_trait(entt::internal::meta_traits::is_enum)</Item>
<Item Name="[is_class]">has_trait(entt::internal::meta_traits::is_class)</Item>
<Item Name="[is_pointer]">has_trait(entt::internal::meta_traits::is_pointer)</Item>
<Item Name="[is_meta_pointer_like]">has_trait(entt::internal::meta_traits::is_meta_pointer_like)</Item>
<Item Name="[is_meta_sequence_container]">has_trait(entt::internal::meta_traits::is_meta_sequence_container)</Item>
<Item Name="[is_meta_associative_container]">has_trait(entt::internal::meta_traits::is_meta_associative_container)</Item>
<Item Name="[is_arithmetic]">has_property(entt::internal::meta_traits::is_arithmetic)</Item>
<Item Name="[is_integral]">has_property(entt::internal::meta_traits::is_integral)</Item>
<Item Name="[is_signed]">has_property(entt::internal::meta_traits::is_signed)</Item>
<Item Name="[is_array]">has_property(entt::internal::meta_traits::is_array)</Item>
<Item Name="[is_enum]">has_property(entt::internal::meta_traits::is_enum)</Item>
<Item Name="[is_class]">has_property(entt::internal::meta_traits::is_class)</Item>
<Item Name="[is_meta_pointer_like]">has_property(entt::internal::meta_traits::is_meta_pointer_like)</Item>
<Item Name="[is_meta_sequence_container]">has_property(entt::internal::meta_traits::is_meta_sequence_container)</Item>
<Item Name="[is_meta_associative_container]">has_property(entt::internal::meta_traits::is_meta_associative_container)</Item>
<Item Name="[default_constructor]">default_constructor != nullptr</Item>
<Item Name="[conversion_helper]">conversion_helper != nullptr</Item>
<Item Name="[from_void]">from_void != nullptr</Item>
<Item Name="[template_info]">templ</Item>
<Item Name="[custom]">custom</Item>
<Item Name="[details]" Condition="!(details == nullptr)">*details</Item>
<Item Name="[details]" Condition="details != nullptr">*details</Item>
</Expand>
</Type>
<Type Name="entt::meta_any">
@@ -128,26 +74,17 @@
</Type>
<Type Name="entt::meta_handle">
<DisplayString>{ any }</DisplayString>
<Expand>
<ExpandedItem>any</ExpandedItem>
</Expand>
</Type>
<Type Name="entt::meta_associative_container">
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<DisplayString>{ storage }</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<Item Name="[const]">const_only</Item>
<Item Name="[data]">data</Item>
</Expand>
</Type>
<Type Name="entt::meta_sequence_container">
<DisplayString Condition="data != nullptr">{{ const={ const_only } }}</DisplayString>
<DisplayString>{{}}</DisplayString>
<DisplayString>{ storage }</DisplayString>
<Expand>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
<Item Name="[const]">const_only</Item>
<Item Name="[data]">data</Item>
</Expand>
</Type>
<Type Name="entt::meta_data">
@@ -167,10 +104,10 @@
</Expand>
</Type>
<Type Name="entt::meta_prop">
<DisplayString Condition="node.type != nullptr">{ node }</DisplayString>
<DisplayString Condition="node != nullptr">{ *node }</DisplayString>
<DisplayString>{{}}</DisplayString>
<Expand>
<ExpandedItem Condition="node.type != nullptr">node</ExpandedItem>
<ExpandedItem Condition="node != nullptr">node</ExpandedItem>
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
@@ -181,21 +118,4 @@
<Item Name="[context]" Condition="ctx != nullptr">ctx->value</Item>
</Expand>
</Type>
<Type Name="entt::meta_ctx">
<Intrinsic Name="element_at" Expression="value.packed.first_base::value[pos].element">
<Parameter Name="pos" Type="int"/>
</Intrinsic>
<DisplayString>{ value }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0"/>
<Variable Name="last" InitialValue="value.size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ element_at(pos).first }]">element_at(pos).second</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
</AutoVisualizer>

View File

@@ -7,20 +7,9 @@
</Expand>
</Type>
<Type Name="entt::resource_cache&lt;*&gt;">
<Intrinsic Name="size" Expression="pool.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<DisplayString>{ pool.first_base::value }</DisplayString>
<Expand>
<CustomListItems>
<Variable Name="pos" InitialValue="0" />
<Variable Name="last" InitialValue="size()"/>
<Loop>
<Break Condition="pos == last"/>
<Item Name="[{ pool.first_base::value.packed.first_base::value[pos].element.first }]">
*pool.first_base::value.packed.first_base::value[pos].element.second
</Item>
<Exec>++pos</Exec>
</Loop>
</CustomListItems>
<ExpandedItem>pool.first_base::value</ExpandedItem>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -11,10 +11,15 @@
<Intrinsic Name="size" Expression="pools.first_base::value.size()"/>
<DisplayString>{{ size={ size() } }}</DisplayString>
<Expand>
<IndexListItems>
<Size>size()</Size>
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
<Synthetic Name="[pools]">
<DisplayString>{ size() }</DisplayString>
<Expand>
<IndexListItems>
<Size>size()</Size>
<ValueNode>*pools.first_base::value.packed.first_base::value[$i].element.second</ValueNode>
</IndexListItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="entt::internal::dispatcher_handler&lt;*&gt;">
@@ -24,7 +29,7 @@
</Expand>
</Type>
<Type Name="entt::emitter&lt;*&gt;">
<DisplayString>{{ size={ handlers.first_base::value.size() } }}</DisplayString>
<DisplayString>{{ size={ handlers.first_base::value.packed.first_base::value.size() } }}</DisplayString>
</Type>
<Type Name="entt::connection">
<DisplayString>{{ bound={ signal != nullptr } }}</DisplayString>

View File

@@ -1,28 +0,0 @@
#!/usr/bin/env bash
set -e
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
VERSION_HEADER=$(realpath "$SCRIPT_DIR/../src/entt/config/version.h" --relative-to=$(pwd))
BAZEL_MODULE=$(realpath "$SCRIPT_DIR/../MODULE.bazel" --relative-to=$(pwd))
if [[ -z "${VERSION_HEADER}" ]]; then
echo "Cannot find version header"
exit 1
fi
echo "Getting version from $VERSION_HEADER ..."
ENTT_MAJOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MAJOR ([0-9]+)/\1/p' $VERSION_HEADER)
ENTT_MINOR_VERSION=$(sed -nr 's/#define ENTT_VERSION_MINOR ([0-9]+)/\1/p' $VERSION_HEADER)
ENTT_PATCH_VERSION=$(sed -nr 's/#define ENTT_VERSION_PATCH ([0-9]+)/\1/p' $VERSION_HEADER)
VERSION="$ENTT_MAJOR_VERSION.$ENTT_MINOR_VERSION.$ENTT_PATCH_VERSION"
echo "Found $VERSION"
buildozer "set version $VERSION" //MODULE.bazel:%module
# a commit is needed for 'git archive'
git add $BAZEL_MODULE
git commit -m "chore: update MODULE.bazel version to $VERSION"

File diff suppressed because it is too large Load Diff

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

@@ -3,8 +3,6 @@
#include "version.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION)
# define ENTT_CONSTEXPR
# define ENTT_THROW throw
@@ -27,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
@@ -44,7 +40,7 @@
# define ENTT_ASSERT(condition, msg) (void(0))
#elif !defined ENTT_ASSERT
# include <cassert>
# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg)))
# define ENTT_ASSERT(condition, msg) assert(condition)
#endif
#ifdef ENTT_DISABLE_ASSERT
@@ -62,12 +58,6 @@
# define ENTT_ETO_TYPE(Type) Type
#endif
#ifdef ENTT_NO_MIXIN
# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__
#else
# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__>
#endif
#ifdef ENTT_STANDARD_CPP
# define ENTT_NONSTD false
#else
@@ -90,6 +80,4 @@
# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD))
#endif
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -1,11 +1,7 @@
#ifndef ENTT_CONFIG_MACRO_H
#define ENTT_CONFIG_MACRO_H
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define ENTT_STR(arg) #arg
#define ENTT_XSTR(arg) ENTT_STR(arg)
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -3,16 +3,12 @@
#include "macro.h"
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define ENTT_VERSION_MAJOR 3
#define ENTT_VERSION_MINOR 14
#define ENTT_VERSION_PATCH 0
#define ENTT_VERSION_MINOR 12
#define ENTT_VERSION_PATCH 2
#define ENTT_VERSION \
ENTT_XSTR(ENTT_VERSION_MAJOR) \
"." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH)
// NOLINTEND(cppcoreguidelines-macro-usage)
#endif

View File

@@ -12,7 +12,6 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
@@ -21,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>
@@ -66,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{} {}
@@ -123,7 +125,7 @@ public:
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
return {it->element.first, it->element.second};
}
template<typename Lhs, typename Rhs>
@@ -188,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{},
@@ -240,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.
@@ -268,7 +273,6 @@ class dense_map {
template<typename Other>
[[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
return fast_mod(static_cast<size_type>(sparse.second()(key)), bucket_count());
}
@@ -327,7 +331,7 @@ class dense_map {
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)];
size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first);
packed.first()[pos] = std::move(packed.first().back());
for(; *curr != last; curr = &packed.first()[*curr].next) {}
*curr = pos;
@@ -343,8 +347,6 @@ class dense_map {
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Key type of the container. */
using key_type = Key;
/*! @brief Mapped type of the container. */
@@ -357,6 +359,8 @@ public:
using hasher = Hash;
/*! @brief Type of function to use to compare the keys for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Input iterator type. */
using iterator = internal::dense_map_iterator<typename packed_container_type::iterator>;
/*! @brief Constant input iterator type. */
@@ -406,7 +410,8 @@ public:
*/
explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal} {
packed{allocator, equal},
threshold{default_threshold} {
rehash(cnt);
}
@@ -424,7 +429,7 @@ public:
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_map(dense_map &&) noexcept = default;
dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
/**
* @brief Allocator-extended move constructor.
@@ -436,9 +441,6 @@ public:
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_map() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
@@ -449,7 +451,7 @@ public:
* @brief Default move assignment operator.
* @return This container.
*/
dense_map &operator=(dense_map &&) noexcept = default;
dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
/**
* @brief Returns the associated allocator.
@@ -681,7 +683,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const key_type &key) {
for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].next) {
if(packed.second()(packed.first()[*curr].element.first, key)) {
const auto index = *curr;
*curr = packed.first()[*curr].next;
@@ -697,7 +699,7 @@ public:
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_map &other) noexcept {
void swap(dense_map &other) {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
@@ -987,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) {
@@ -1026,12 +1028,16 @@ public:
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold{default_threshold};
float threshold;
};
} // namespace entt
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace std {
template<typename Key, typename Value, typename Allocator>
@@ -1039,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

@@ -12,14 +12,18 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/compressed_pair.hpp"
#include "../core/memory.hpp"
#include "../core/type_traits.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename It>
@@ -85,11 +89,11 @@ public:
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(operator[](0));
return std::addressof(it->second);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
return *operator->();
}
template<typename Lhs, typename Rhs>
@@ -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.
@@ -271,7 +279,7 @@ class dense_set {
void move_and_pop(const std::size_t pos) {
if(const auto last = size() - 1u; pos != last) {
size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)];
size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second);
packed.first()[pos] = std::move(packed.first().back());
for(; *curr != last; curr = &packed.first()[*curr].first) {}
*curr = pos;
@@ -287,8 +295,6 @@ class dense_set {
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Key type of the container. */
using key_type = Type;
/*! @brief Value type of the container. */
@@ -299,14 +305,12 @@ public:
using hasher = Hash;
/*! @brief Type of function to use to compare the elements for equality. */
using key_equal = KeyEqual;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = internal::dense_set_iterator<typename packed_container_type::iterator>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_iterator>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
/*! @brief Forward iterator type. */
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::iterator>;
/*! @brief Constant forward iterator type. */
@@ -352,7 +356,8 @@ public:
*/
explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{})
: sparse{allocator, hash},
packed{allocator, equal} {
packed{allocator, equal},
threshold{default_threshold} {
rehash(cnt);
}
@@ -370,7 +375,7 @@ public:
threshold{other.threshold} {}
/*! @brief Default move constructor. */
dense_set(dense_set &&) noexcept = default;
dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_constructible_v<compressed_pair<packed_container_type, key_equal>>) = default;
/**
* @brief Allocator-extended move constructor.
@@ -382,9 +387,6 @@ public:
packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))},
threshold{other.threshold} {}
/*! @brief Default destructor. */
~dense_set() = default;
/**
* @brief Default copy assignment operator.
* @return This container.
@@ -395,7 +397,7 @@ public:
* @brief Default move assignment operator.
* @return This container.
*/
dense_set &operator=(dense_set &&) noexcept = default;
dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v<compressed_pair<sparse_container_type, hasher>> &&std::is_nothrow_move_assignable_v<compressed_pair<packed_container_type, key_equal>>) = default;
/**
* @brief Returns the associated allocator.
@@ -445,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.
@@ -611,7 +573,7 @@ public:
* @return Number of elements removed (either 0 or 1).
*/
size_type erase(const value_type &value) {
for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits<size_type>::max)(); curr = &packed.first()[*curr].first) {
if(packed.second()(packed.first()[*curr].second, value)) {
const auto index = *curr;
*curr = packed.first()[*curr].first;
@@ -627,7 +589,7 @@ public:
* @brief Exchanges the contents with those of a given container.
* @param other Container to exchange the content with.
*/
void swap(dense_set &other) noexcept {
void swap(dense_set &other) {
using std::swap;
swap(sparse, other.sparse);
swap(packed, other.packed);
@@ -880,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) {
@@ -919,7 +881,7 @@ public:
private:
compressed_pair<sparse_container_type, hasher> sparse;
compressed_pair<packed_container_type, key_equal> packed;
float threshold{default_threshold};
float threshold;
};
} // namespace entt

View File

@@ -3,8 +3,6 @@
#include <functional>
#include <memory>
#include <utility>
#include <vector>
namespace entt {
@@ -12,27 +10,17 @@ template<
typename Key,
typename Type,
typename = std::hash<Key>,
typename = std::equal_to<>,
typename = std::equal_to<Key>,
typename = std::allocator<std::pair<const Key, Type>>>
class dense_map;
template<
typename Type,
typename = std::hash<Type>,
typename = std::equal_to<>,
typename = std::equal_to<Type>,
typename = std::allocator<Type>>
class dense_set;
template<typename...>
class basic_table;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Element types.
*/
template<typename... Type>
using table = basic_table<std::vector<Type>...>;
} // namespace entt
#endif

View File

@@ -1,460 +0,0 @@
#ifndef ENTT_CONTAINER_TABLE_HPP
#define ENTT_CONTAINER_TABLE_HPP
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename... It>
class table_iterator {
template<typename...>
friend class table_iterator;
public:
using value_type = decltype(std::forward_as_tuple(*std::declval<It>()...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::random_access_iterator_tag;
constexpr table_iterator() noexcept
: it{} {}
constexpr table_iterator(It... from) noexcept
: it{from...} {}
template<typename... Other, typename = std::enable_if_t<(std::is_constructible_v<It, Other> && ...)>>
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
: table_iterator{std::get<Other>(other.it)...} {}
constexpr table_iterator &operator++() noexcept {
return (++std::get<It>(it), ...), *this;
}
constexpr table_iterator operator++(int) noexcept {
table_iterator orig = *this;
return ++(*this), orig;
}
constexpr table_iterator &operator--() noexcept {
return (--std::get<It>(it), ...), *this;
}
constexpr table_iterator operator--(int) noexcept {
table_iterator orig = *this;
return operator--(), orig;
}
constexpr table_iterator &operator+=(const difference_type value) noexcept {
return ((std::get<It>(it) += value), ...), *this;
}
constexpr table_iterator operator+(const difference_type value) const noexcept {
table_iterator copy = *this;
return (copy += value);
}
constexpr table_iterator &operator-=(const difference_type value) noexcept {
return (*this += -value);
}
constexpr table_iterator operator-(const difference_type value) const noexcept {
return (*this + -value);
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return std::forward_as_tuple(std::get<It>(it)[value]...);
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return {operator[](0)};
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
}
template<typename... Lhs, typename... Rhs>
friend constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator==(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
template<typename... Lhs, typename... Rhs>
friend constexpr bool operator<(const table_iterator<Lhs...> &, const table_iterator<Rhs...> &) noexcept;
private:
std::tuple<It...> it;
};
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) - std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator==(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) == std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator!=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return std::get<0>(lhs.it) < std::get<0>(rhs.it);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return rhs < lhs;
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator<=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename... Lhs, typename... Rhs>
[[nodiscard]] constexpr bool operator>=(const table_iterator<Lhs...> &lhs, const table_iterator<Rhs...> &rhs) noexcept {
return !(lhs < rhs);
}
} // namespace internal
/*! @endcond */
/**
* @brief Basic table implementation.
*
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
* a table. Do not make assumption on the order in any case.
*
* @tparam Container Sequence container row types.
*/
template<typename... Container>
class basic_table {
using container_type = std::tuple<Container...>;
public:
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Input iterator type. */
using iterator = internal::table_iterator<typename Container::iterator...>;
/*! @brief Constant input iterator type. */
using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
/*! @brief Reverse iterator type. */
using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
/*! @brief Constant reverse iterator type. */
using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_iterator...>;
/*! @brief Default constructor. */
basic_table()
: payload{} {
}
/**
* @brief Copy constructs the underlying containers.
* @param container The containers to copy from.
*/
explicit basic_table(const Container &...container) noexcept
: payload{container...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Move constructs the underlying containers.
* @param container The containers to move from.
*/
explicit basic_table(Container &&...container) noexcept
: payload{std::move(container)...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_table(const basic_table &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_table(basic_table &&other) noexcept
: payload{std::move(other.payload)} {}
/**
* @brief Constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param allocator A valid allocator.
*/
template<typename Allocator>
explicit basic_table(const Allocator &allocator)
: payload{Container{allocator}...} {}
/**
* @brief Copy constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param container The containers to copy from.
* @param allocator A valid allocator.
*/
template<class Allocator>
basic_table(const Container &...container, const Allocator &allocator) noexcept
: payload{Container{container, allocator}...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Move constructs the underlying containers using a given allocator.
* @tparam Allocator Type of allocator.
* @param container The containers to move from.
* @param allocator A valid allocator.
*/
template<class Allocator>
basic_table(Container &&...container, const Allocator &allocator) noexcept
: payload{Container{std::move(container), allocator}...} {
ENTT_ASSERT((((std::get<Container>(payload).size() * sizeof...(Container)) == (std::get<Container>(payload).size() + ...)) && ...), "Unexpected container size");
}
/**
* @brief Allocator-extended move constructor.
* @tparam Allocator Type of allocator.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
template<class Allocator>
basic_table(basic_table &&other, const Allocator &allocator)
: payload{Container{std::move(std::get<Container>(other.payload)), allocator}...} {}
/*! @brief Default destructor. */
~basic_table() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This container.
*/
basic_table &operator=(const basic_table &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
basic_table &operator=(basic_table &&other) noexcept {
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given table.
* @param other Table to exchange the content with.
*/
void swap(basic_table &other) noexcept {
using std::swap;
swap(payload, other.payload);
}
/**
* @brief Increases the capacity of a table.
*
* If the new capacity is greater than the current capacity, new storage is
* allocated, otherwise the method does nothing.
*
* @param cap Desired capacity.
*/
void reserve(const size_type cap) {
(std::get<Container>(payload).reserve(cap), ...);
}
/**
* @brief Returns the number of rows that a table has currently allocated
* space for.
* @return Capacity of the table.
*/
[[nodiscard]] size_type capacity() const noexcept {
return std::get<0>(payload).capacity();
}
/*! @brief Requests the removal of unused capacity. */
void shrink_to_fit() {
(std::get<Container>(payload).shrink_to_fit(), ...);
}
/**
* @brief Returns the number of rows in a table.
* @return Number of rows.
*/
[[nodiscard]] size_type size() const noexcept {
return std::get<0>(payload).size();
}
/**
* @brief Checks whether a table is empty.
* @return True if the table is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return std::get<0>(payload).empty();
}
/**
* @brief Returns an iterator to the beginning.
*
* If the table is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first row of the table.
*/
[[nodiscard]] const_iterator cbegin() const noexcept {
return {std::get<Container>(payload).cbegin()...};
}
/*! @copydoc cbegin */
[[nodiscard]] const_iterator begin() const noexcept {
return cbegin();
}
/*! @copydoc begin */
[[nodiscard]] iterator begin() noexcept {
return {std::get<Container>(payload).begin()...};
}
/**
* @brief Returns an iterator to the end.
* @return An iterator to the element following the last row of the table.
*/
[[nodiscard]] const_iterator cend() const noexcept {
return {std::get<Container>(payload).cend()...};
}
/*! @copydoc cend */
[[nodiscard]] const_iterator end() const noexcept {
return cend();
}
/*! @copydoc end */
[[nodiscard]] iterator end() noexcept {
return {std::get<Container>(payload).end()...};
}
/**
* @brief Returns a reverse iterator to the beginning.
*
* If the table is empty, the returned iterator will be equal to `rend()`.
*
* @return An iterator to the first row of the reversed table.
*/
[[nodiscard]] const_reverse_iterator crbegin() const noexcept {
return {std::get<Container>(payload).crbegin()...};
}
/*! @copydoc crbegin */
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
return crbegin();
}
/*! @copydoc rbegin */
[[nodiscard]] reverse_iterator rbegin() noexcept {
return {std::get<Container>(payload).rbegin()...};
}
/**
* @brief Returns a reverse iterator to the end.
* @return An iterator to the element following the last row of the reversed
* table.
*/
[[nodiscard]] const_reverse_iterator crend() const noexcept {
return {std::get<Container>(payload).crend()...};
}
/*! @copydoc crend */
[[nodiscard]] const_reverse_iterator rend() const noexcept {
return crend();
}
/*! @copydoc rend */
[[nodiscard]] reverse_iterator rend() noexcept {
return {std::get<Container>(payload).rend()...};
}
/**
* @brief Appends a row to the end of a table.
* @tparam Args Types of arguments to use to construct the row data.
* @param args Parameters to use to construct the row data.
* @return A reference to the newly created row data.
*/
template<typename... Args>
std::tuple<typename Container::value_type &...> emplace(Args &&...args) {
if constexpr(sizeof...(Args) == 0u) {
return std::forward_as_tuple(std::get<Container>(payload).emplace_back()...);
} else {
return std::forward_as_tuple(std::get<Container>(payload).emplace_back(std::forward<Args>(args))...);
}
}
/**
* @brief Removes a row from a table.
* @param pos An iterator to the row to remove.
* @return An iterator following the removed row.
*/
iterator erase(const_iterator pos) {
const auto diff = pos - begin();
return {std::get<Container>(payload).erase(std::get<Container>(payload).begin() + diff)...};
}
/**
* @brief Removes a row from a table.
* @param pos Index of the row to remove.
*/
void erase(const size_type pos) {
ENTT_ASSERT(pos < size(), "Index out of bounds");
erase(begin() + static_cast<typename const_iterator::difference_type>(pos));
}
/**
* @brief Returns the row data at specified location.
* @param pos The row for which to return the data.
* @return The row data at specified location.
*/
[[nodiscard]] std::tuple<const typename Container::value_type &...> operator[](const size_type pos) const {
ENTT_ASSERT(pos < size(), "Index out of bounds");
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
}
/*! @copydoc operator[] */
[[nodiscard]] std::tuple<typename Container::value_type &...> operator[](const size_type pos) {
ENTT_ASSERT(pos < size(), "Index out of bounds");
return std::forward_as_tuple(std::get<Container>(payload)[pos]...);
}
/*! @brief Clears a table. */
void clear() {
(std::get<Container>(payload).clear(), ...);
}
private:
container_type payload;
};
} // namespace entt
/*! @cond TURN_OFF_DOXYGEN */
namespace std {
template<typename... Container, typename Allocator>
struct uses_allocator<entt::basic_table<Container...>, Allocator>
: std::bool_constant<(std::uses_allocator_v<Container, Allocator> && ...)> {};
} // namespace std
/*! @endcond */
#endif

View File

@@ -58,11 +58,9 @@ struct insertion_sort {
auto value = std::move(*it);
auto pre = it;
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic)
for(; pre > first && compare(value, *(pre - 1)); --pre) {
*pre = std::move(*(pre - 1));
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
*pre = std::move(value);
}
@@ -106,16 +104,13 @@ struct radix_sort {
constexpr auto mask = (1 << Bit) - 1;
constexpr auto buckets = 1 << Bit;
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
std::size_t index[buckets]{};
std::size_t count[buckets]{};
for(auto it = from; it != to; ++it) {
++count[(getter(*it) >> start) & mask];
}
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
std::size_t index[buckets]{};
for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) {
index[pos + 1u] = index[pos] + count[pos];
}

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,70 +51,70 @@ 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 {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
alignas(Align) std::byte data[Len + static_cast<std::size_t>(Len == 0u)];
alignas(Align) std::byte data[Len + !Len];
};
template<typename Type>
static constexpr bool in_situ = (Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
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");
const Type *elem = nullptr;
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>) {
elem = (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 {
elem = static_cast<const Type *>(value.instance);
element = static_cast<const Type *>(value.instance);
}
switch(op) {
case operation::copy:
if constexpr(std::is_copy_constructible_v<Type>) {
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
}
break;
case operation::move:
if constexpr(in_situ<Type>) {
if(value.mode == any_policy::owner) {
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(elem))};
if(value.owner()) {
return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
}
}
return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
case operation::transfer:
if constexpr(std::is_move_assignable_v<Type>) {
*const_cast<Type *>(elem) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
*const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
return other;
}
[[fallthrough]];
case operation::assign:
if constexpr(std::is_copy_assignable_v<Type>) {
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
*const_cast<Type *>(element) = *static_cast<const Type *>(other);
return other;
}
break;
case operation::destroy:
if constexpr(in_situ<Type>) {
elem->~Type();
element->~Type();
} else if constexpr(std::is_array_v<Type>) {
delete[] elem;
delete[] element;
} else {
delete elem;
delete element;
}
break;
case operation::compare:
if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
return *elem == *static_cast<const Type *>(other) ? other : nullptr;
return *element == *static_cast<const Type *>(other) ? other : nullptr;
} else {
return (elem == other) ? other : nullptr;
return (element == other) ? other : nullptr;
}
case operation::get:
return elem;
return element;
}
return nullptr;
@@ -118,37 +122,32 @@ class basic_any {
template<typename Type, typename... Args>
void initialize([[maybe_unused]] Args &&...args) {
using plain_type = std::remove_cv_t<std::remove_reference_t<Type>>;
info = &type_id<plain_type>();
info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
if constexpr(!std::is_void_v<Type>) {
vtable = basic_vtable<plain_type>;
vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
if constexpr(std::is_lvalue_reference_v<Type>) {
static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
instance = (std::addressof(args), ...);
} else if constexpr(in_situ<plain_type>) {
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
::new(&storage) plain_type{std::forward<Args>(args)...};
} else 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>>>)) {
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
::new(&storage) plain_type(std::forward<Args>(args)...);
new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
} else {
if constexpr(std::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<plain_type>)) {
instance = new plain_type{std::forward<Args>(args)...};
} else if constexpr(std::is_array_v<plain_type>) {
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
instance = new plain_type[std::extent_v<plain_type>]();
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>>>)) {
instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
} else {
instance = new plain_type(std::forward<Args>(args)...);
instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
}
}
}
}
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},
@@ -175,7 +174,7 @@ public:
: instance{},
info{},
vtable{},
mode{any_policy::owner} {
mode{policy::owner} {
initialize<Type>(std::forward<Args>(args)...);
}
@@ -215,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);
}
}
@@ -226,12 +225,10 @@ public:
* @return This any object.
*/
basic_any &operator=(const basic_any &other) {
if(this != &other) {
reset();
reset();
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
if(other.vtable) {
other.vtable(operation::copy, other, this);
}
return *this;
@@ -243,8 +240,6 @@ public:
* @return This any object.
*/
basic_any &operator=(basic_any &&other) noexcept {
ENTT_ASSERT(this != &other, "Self move assignment");
reset();
if(other.vtable) {
@@ -263,8 +258,9 @@ public:
* @param value An instance of an object to use to initialize the wrapper.
* @return This any object.
*/
template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>>>
basic_any &operator=(Type &&value) {
template<typename Type>
std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
operator=(Type &&value) {
emplace<std::decay_t<Type>>(std::forward<Type>(value));
return *this;
}
@@ -299,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());
}
/**
@@ -308,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));
}
/**
@@ -329,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);
}
@@ -338,12 +334,12 @@ 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 {
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
}
return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
}
return false;
@@ -351,7 +347,7 @@ public:
/*! @brief Destroys contained object */
void reset() {
if(vtable && (mode == any_policy::owner)) {
if(vtable && owner()) {
vtable(operation::destroy, *this, nullptr);
}
@@ -359,7 +355,7 @@ public:
ENTT_ASSERT((instance = nullptr) == nullptr, "");
info = &type_id<void>();
vtable = nullptr;
mode = any_policy::owner;
mode = policy::owner;
}
/**
@@ -397,20 +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 the current mode of an any object.
* @return The current mode of the any object.
* @brief Returns true if a wrapper owns its object, false otherwise.
* @return True if the wrapper owns its object, false otherwise.
*/
[[nodiscard]] any_policy policy() const noexcept {
return mode;
[[nodiscard]] bool owner() const noexcept {
return (mode == policy::owner);
}
private:
@@ -420,7 +416,7 @@ private:
};
const type_info *info;
vtable_type *vtable;
any_policy mode;
policy mode;
};
/**
@@ -432,7 +428,7 @@ private:
* @return The element converted to the requested type.
*/
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] std::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
[[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
@@ -440,7 +436,7 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
[[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
// forces const on non-reference types to make them work also with wrappers for const references
auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");
@@ -449,13 +445,13 @@ template<typename Type, std::size_t Len, std::size_t Align>
/*! @copydoc any_cast */
template<typename Type, std::size_t Len, std::size_t Align>
[[nodiscard]] std::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
[[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
return static_cast<Type>(std::move(*instance));
} else {
return any_cast<Type>(data);
}
return any_cast<Type>(data);
} else {
auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
ENTT_ASSERT(instance, "Invalid instance");

View File

@@ -1,69 +0,0 @@
#ifndef ENTT_CORE_BIT_HPP
#define ENTT_CORE_BIT_HPP
#include <cstddef>
#include <limits>
#include <type_traits>
#include "../config/config.h"
namespace entt {
/**
* @brief Returns the number of set bits in a value (waiting for C++20 and
* `std::popcount`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return The number of set bits in the value.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, int> popcount(const Type value) noexcept {
return value ? (int(value & 1) + popcount(static_cast<Type>(value >> 1))) : 0;
}
/**
* @brief Checks whether a value is a power of two or not (waiting for C++20 and
* `std::has_single_bit`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return True if the value is a power of two, false otherwise.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, bool> has_single_bit(const Type value) noexcept {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value
* (waiting for C++20 and `std::bit_ceil`).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @return The smallest power of two greater than or equal to the given value.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> next_power_of_two(const Type value) noexcept {
ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits<Type>::digits - 1)), "Numeric limits exceeded");
Type curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<Type>::digits; next = next * 2) {
curr |= (curr >> next);
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @tparam Type Unsigned integer type.
* @param value A value of unsigned integer type.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
template<typename Type>
[[nodiscard]] constexpr std::enable_if_t<std::is_unsigned_v<Type>, Type> fast_mod(const Type value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two");
return value & (mod - 1u);
}
} // namespace entt
#endif

View File

@@ -5,12 +5,15 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include "fwd.hpp"
#include "type_traits.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Type, std::size_t, typename = void>
@@ -18,9 +21,9 @@ struct compressed_pair_element {
using reference = Type &;
using const_reference = const Type &;
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
// NOLINTNEXTLINE(modernize-use-equals-default)
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>) {}
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<Type>>>
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<Type>)
: value{} {}
template<typename Arg, typename = std::enable_if_t<!std::is_same_v<std::remove_cv_t<std::remove_reference_t<Arg>>, compressed_pair_element>>>
constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v<Type, Arg>)
@@ -39,7 +42,7 @@ struct compressed_pair_element {
}
private:
Type value{};
Type value;
};
template<typename Type, std::size_t Tag>
@@ -48,7 +51,7 @@ struct compressed_pair_element<Type, Tag, std::enable_if_t<is_ebco_eligible_v<Ty
using const_reference = const Type &;
using base_type = Type;
template<typename Dummy = Type, typename = std::enable_if_t<std::is_default_constructible_v<Dummy>>>
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<base_type>>>
constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v<base_type>)
: base_type{} {}
@@ -70,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.
@@ -103,7 +110,7 @@ public:
* @tparam Dummy Dummy template parameter used for internal purposes.
*/
template<bool Dummy = true, typename = std::enable_if_t<Dummy && std::is_default_constructible_v<first_type> && std::is_default_constructible_v<second_type>>>
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> && std::is_nothrow_default_constructible_v<second_base>)
constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v<first_base> &&std::is_nothrow_default_constructible_v<second_base>)
: first_base{},
second_base{} {}
@@ -111,13 +118,13 @@ public:
* @brief Copy constructor.
* @param other The instance to copy from.
*/
constexpr compressed_pair(const compressed_pair &other) = default;
constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v<first_base> &&std::is_nothrow_copy_constructible_v<second_base>) = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
constexpr compressed_pair(compressed_pair &&other) noexcept = default;
constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v<first_base> &&std::is_nothrow_move_constructible_v<second_base>) = default;
/**
* @brief Constructs a pair from its values.
@@ -127,7 +134,7 @@ public:
* @param other Value to use to initialize the second element.
*/
template<typename Arg, typename Other>
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> && std::is_nothrow_constructible_v<second_base, Other>)
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v<first_base, Arg> &&std::is_nothrow_constructible_v<second_base, Other>)
: first_base{std::forward<Arg>(arg)},
second_base{std::forward<Other>(other)} {}
@@ -139,26 +146,23 @@ public:
* @param other Arguments to use to initialize the second element.
*/
template<typename... Args, typename... Other>
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> && std::is_nothrow_constructible_v<second_base, Other...>)
constexpr compressed_pair(std::piecewise_construct_t, std::tuple<Args...> args, std::tuple<Other...> other) noexcept(std::is_nothrow_constructible_v<first_base, Args...> &&std::is_nothrow_constructible_v<second_base, Other...>)
: first_base{std::move(args), std::index_sequence_for<Args...>{}},
second_base{std::move(other), std::index_sequence_for<Other...>{}} {}
/*! @brief Default destructor. */
~compressed_pair() = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(const compressed_pair &other) = default;
constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v<first_base> &&std::is_nothrow_copy_assignable_v<second_base>) = default;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This compressed pair object.
*/
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default;
constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v<first_base> &&std::is_nothrow_move_assignable_v<second_base>) = default;
/**
* @brief Returns the first element that a pair stores.
@@ -190,7 +194,7 @@ public:
* @brief Swaps two compressed pair objects.
* @param other The compressed pair to swap with.
*/
constexpr void swap(compressed_pair &other) noexcept {
constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v<first_type> &&std::is_nothrow_swappable_v<second_type>) {
using std::swap;
swap(first(), other.first());
swap(second(), other.second());
@@ -203,7 +207,7 @@ public:
* reference to the second element if `Index` is 1.
*/
template<std::size_t Index>
[[nodiscard]] constexpr decltype(auto) get() noexcept {
constexpr decltype(auto) get() noexcept {
if constexpr(Index == 0u) {
return first();
} else {
@@ -214,7 +218,7 @@ public:
/*! @copydoc get */
template<std::size_t Index>
[[nodiscard]] constexpr decltype(auto) get() const noexcept {
constexpr decltype(auto) get() const noexcept {
if constexpr(Index == 0u) {
return first();
} else {
@@ -246,6 +250,8 @@ inline constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<
} // namespace entt
// disable structured binding support for clang 6, it messes when specializing tuple_size
#if !defined __clang_major__ || __clang_major__ > 6
namespace std {
/**
@@ -268,5 +274,6 @@ struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<I
};
} // namespace std
#endif
#endif

View File

@@ -15,7 +15,6 @@ namespace entt {
*/
template<typename...>
class family {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static ENTT_MAYBE_ATOMIC(id_type) identifier{};
public:

View File

@@ -6,7 +6,6 @@
namespace entt {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
template<std::size_t Len = sizeof(double[2]), std::size_t = alignof(double[2])>
class basic_any;
@@ -16,21 +15,6 @@ using id_type = ENTT_ID_TYPE;
/*! @brief Alias declaration for the most common use case. */
using any = basic_any<>;
template<typename, typename>
class compressed_pair;
template<typename>
class basic_hashed_string;
/*! @brief Aliases for common character types. */
using hashed_string = basic_hashed_string<char>;
/*! @brief Aliases for common character types. */
using hashed_wstring = basic_hashed_string<wchar_t>;
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
struct type_info;
} // namespace entt
#endif

View File

@@ -3,27 +3,32 @@
#include <cstddef>
#include <cstdint>
#include <string_view>
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename = id_type>
struct fnv_1a_params;
template<typename>
struct fnv1a_traits;
template<>
struct fnv_1a_params<std::uint32_t> {
static constexpr auto offset = 2166136261;
static constexpr auto prime = 16777619;
struct fnv1a_traits<std::uint32_t> {
using type = std::uint32_t;
static constexpr std::uint32_t offset = 2166136261;
static constexpr std::uint32_t prime = 16777619;
};
template<>
struct fnv_1a_params<std::uint64_t> {
static constexpr auto offset = 14695981039346656037ull;
static constexpr auto prime = 1099511628211ull;
struct fnv1a_traits<std::uint64_t> {
using type = std::uint64_t;
static constexpr std::uint64_t offset = 14695981039346656037ull;
static constexpr std::uint64_t prime = 1099511628211ull;
};
template<typename Char>
@@ -38,7 +43,11 @@ struct basic_hashed_string {
};
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Zero overhead unique identifier.
@@ -58,7 +67,7 @@ struct basic_hashed_string {
template<typename Char>
class basic_hashed_string: internal::basic_hashed_string<Char> {
using base_type = internal::basic_hashed_string<Char>;
using params = internal::fnv_1a_params<>;
using traits_type = internal::fnv1a_traits<id_type>;
struct const_wrapper {
// non-explicit constructor on purpose
@@ -69,11 +78,22 @@ class basic_hashed_string: internal::basic_hashed_string<Char> {
};
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const std::basic_string_view<Char> view) noexcept {
base_type base{view.data(), view.size(), params::offset};
[[nodiscard]] static constexpr auto helper(const Char *str) noexcept {
base_type base{str, 0u, traits_type::offset};
for(auto &&curr: view) {
base.hash = (base.hash ^ static_cast<id_type>(curr)) * params::prime;
for(; str[base.length]; ++base.length) {
base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime;
}
return base;
}
// FowlerNollVo hash function v. 1a - the good
[[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept {
base_type base{str, len, traits_type::offset};
for(size_type pos{}; pos < len; ++pos) {
base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime;
}
return base;
@@ -104,7 +124,6 @@ public:
* @return The numeric representation of the string.
*/
template<std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
[[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept {
return basic_hashed_string{str};
}
@@ -120,7 +139,7 @@ public:
/*! @brief Constructs an empty hashed string. */
constexpr basic_hashed_string() noexcept
: basic_hashed_string{nullptr, 0u} {}
: base_type{} {}
/**
* @brief Constructs a hashed string from a string view.
@@ -128,7 +147,7 @@ public:
* @param len Length of the string to hash.
*/
constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept
: base_type{helper({str, len})} {}
: base_type{helper(str, len)} {}
/**
* @brief Constructs a hashed string from an array of const characters.
@@ -136,9 +155,8 @@ public:
* @param str Human-readable identifier.
*/
template<std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
constexpr basic_hashed_string(const value_type (&str)[N]) noexcept
: base_type{helper({static_cast<const value_type *>(str)})} {}
: base_type{helper(str)} {}
/**
* @brief Explicit constructor on purpose to avoid constructing a hashed
@@ -150,7 +168,7 @@ public:
* @param wrapper Helps achieving the purpose by relying on overloading.
*/
explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept
: base_type{helper({wrapper.repr})} {}
: base_type{helper(wrapper.repr)} {}
/**
* @brief Returns the size a hashed string.
@@ -197,7 +215,7 @@ public:
* @param len Length of the string to hash.
*/
template<typename Char>
basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Char>;
basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string<Char>;
/**
* @brief Deduction guide.
@@ -206,7 +224,6 @@ basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string<Cha
* @param str Human-readable identifier.
*/
template<typename Char, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
/**
@@ -284,6 +301,12 @@ template<typename Char>
return !(lhs < rhs);
}
/*! @brief Aliases for common character types. */
using hashed_string = basic_hashed_string<char>;
/*! @brief Aliases for common character types. */
using hashed_wstring = basic_hashed_string<wchar_t>;
inline namespace literals {
/**

View File

@@ -147,7 +147,7 @@ struct iterable_adaptor final {
using sentinel = Sentinel;
/*! @brief Default constructor. */
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> && std::is_nothrow_default_constructible_v<sentinel>)
constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v<iterator> &&std::is_nothrow_default_constructible_v<sentinel>)
: first{},
last{} {}
@@ -156,7 +156,7 @@ struct iterable_adaptor final {
* @param from Begin iterator.
* @param to End iterator.
*/
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> && std::is_nothrow_move_constructible_v<sentinel>)
constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v<iterator> &&std::is_nothrow_move_constructible_v<sentinel>)
: first{std::move(from)},
last{std::move(to)} {}

View File

@@ -2,6 +2,7 @@
#define ENTT_CORE_MEMORY_HPP
#include <cstddef>
#include <limits>
#include <memory>
#include <tuple>
#include <type_traits>
@@ -10,6 +11,42 @@
namespace entt {
/**
* @brief Checks whether a value is a power of two or not.
* @param value A value that may or may not be a power of two.
* @return True if the value is a power of two, false otherwise.
*/
[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept {
return value && ((value & (value - 1)) == 0);
}
/**
* @brief Computes the smallest power of two greater than or equal to a value.
* @param value The value to use.
* @return The smallest power of two greater than or equal to the given value.
*/
[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept {
ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits<std::size_t>::digits - 1)), "Numeric limits exceeded");
std::size_t curr = value - (value != 0u);
for(int next = 1; next < std::numeric_limits<std::size_t>::digits; next = next * 2) {
curr |= curr >> next;
}
return ++curr;
}
/**
* @brief Fast module utility function (powers of two only).
* @param value A value for which to calculate the modulus.
* @param mod _Modulus_, it must be a power of two.
* @return The common remainder.
*/
[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept {
ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two");
return value & (mod - 1u);
}
/**
* @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20).
* @tparam Type Pointer type.
@@ -126,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>
@@ -182,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).
@@ -236,7 +281,7 @@ constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...ar
*/
template<typename Type, typename Allocator, typename... Args>
constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) {
return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
return std::apply([value](auto &&...curr) { return new(value) Type(std::forward<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, std::forward<Args>(args)...));
}
} // namespace entt

View File

@@ -23,12 +23,10 @@ struct monostate {
* @brief Assigns a value of a specific type to a given key.
* @tparam Type Type of the value to assign.
* @param val User data to assign to the given key.
* @return This monostate object.
*/
template<typename Type>
monostate &operator=(Type val) noexcept {
void operator=(Type val) const noexcept {
value<Type> = val;
return *this;
}
/**
@@ -43,7 +41,6 @@ struct monostate {
private:
template<typename Type>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static ENTT_MAYBE_ATOMIC(Type) value{};
};
@@ -52,8 +49,7 @@ private:
* @tparam Value Value used to differentiate between different variables.
*/
template<id_type Value>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline monostate<Value> monostate_v{};
inline monostate<Value> monostate_v = {};
} // namespace entt

View File

@@ -1,20 +0,0 @@
#ifndef ENTT_CORE_RANGES_HPP
#define ENTT_CORE_RANGES_HPP
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# include <ranges>
# include "iterator.hpp"
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::iterable_adaptor<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::iterable_adaptor<Args...>>{true};
# endif
#endif
#endif

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 {
@@ -24,7 +28,7 @@ struct ENTT_API type_index final {
template<typename Type>
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
#if defined ENTT_PRETTY_FUNCTION
std::string_view pretty_function{static_cast<const char *>(ENTT_PRETTY_FUNCTION)};
std::string_view pretty_function{ENTT_PRETTY_FUNCTION};
auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1);
auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first);
return value;
@@ -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.
@@ -136,12 +144,10 @@ struct type_info final {
* @tparam Type Type for which to construct a type info object.
*/
template<typename Type>
// NOLINTBEGIN(modernize-use-transparent-functors)
constexpr type_info(std::in_place_type_t<Type>) noexcept
: seq{type_index<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
identifier{type_hash<std::remove_cv_t<std::remove_reference_t<Type>>>::value()},
alias{type_name<std::remove_cv_t<std::remove_reference_t<Type>>>::value()} {}
// NOLINTEND(modernize-use-transparent-functors)
/**
* @brief Type index.
@@ -199,7 +205,7 @@ private:
* @param rhs A valid type info object.
* @return True if the first element is less than the second, false otherwise.
*/
[[nodiscard]] inline constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept {
return lhs.index() < rhs.index();
}
@@ -210,7 +216,7 @@ private:
* @return True if the first element is less than or equal to the second, false
* otherwise.
*/
[[nodiscard]] inline constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept {
return !(rhs < lhs);
}
@@ -221,7 +227,7 @@ private:
* @return True if the first element is greater than the second, false
* otherwise.
*/
[[nodiscard]] inline constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept {
return rhs < lhs;
}
@@ -232,7 +238,7 @@ private:
* @return True if the first element is greater than or equal to the second,
* false otherwise.
*/
[[nodiscard]] inline constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept {
return !(lhs < rhs);
}

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
@@ -574,7 +571,7 @@ inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::
/*! @brief Primary template isn't defined on purpose. */
template<typename...>
struct value_list_diff;
class value_list_diff;
/**
* @brief Computes the difference between two value lists.
@@ -582,9 +579,12 @@ struct value_list_diff;
* @tparam Other Values provided by the second value list.
*/
template<auto... Value, auto... Other>
struct value_list_diff<value_list<Value...>, value_list<Other...>> {
class value_list_diff<value_list<Value...>, value_list<Other...>> {
using v141_toolset_workaround = value_list<Other...>;
public:
/*! @brief A value list that is the difference between the two value lists. */
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>;
using type = value_list_cat_t<std::conditional_t<value_list_contains_v<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>;
};
/**
@@ -675,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>
@@ -685,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> {};
/**
@@ -734,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>
@@ -743,67 +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() {
// NOLINTBEGIN(modernize-use-transparent-functors)
if constexpr(std::is_array_v<Type>) {
return false;
} else if constexpr(!is_iterator_v<Type> && has_value_type<Type>::value) {
if constexpr(std::is_same_v<typename Type::value_type, Type> || dispatch_is_equality_comparable<typename Type::value_type>()) {
return maybe_equality_comparable<Type>(0);
} else {
return false;
}
} 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>);
}
// NOLINTEND(modernize-use-transparent-functors)
}
} // namespace internal
/*! @endcond */
/**
* @brief Provides the member constant `value` to true if a given type is
* equality comparable, false otherwise.
* @tparam Type The type to test.
* Internal details not to be documented.
* @endcond
*/
template<typename Type>
struct is_equality_comparable: std::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {};
/*! @copydoc is_equality_comparable */
template<typename Type>
struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
struct is_equality_comparable<Type, std::void_t<decltype(std::declval<Type>() == std::declval<Type>())>>
: std::bool_constant<internal::maybe_equality_comparable<Type>(choice<2>)> {};
/*! @copydoc is_equality_comparable */
template<typename Type, auto N>
struct is_equality_comparable<Type[N]>: std::false_type {};
/**
* @brief Helper variable template.
@@ -868,11 +872,11 @@ template<typename Member>
using member_class_t = typename member_class<Member>::type;
/**
* @brief Extracts the n-th argument of a _callable_ 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 _callable_ 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...));
@@ -886,20 +890,17 @@ class nth_argument {
template<typename Type, typename Class>
static constexpr type_list<Type> pick_up(Type Class ::*);
template<typename Type>
static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&);
public:
/*! @brief N-th argument of the _callable_ type. */
using type = type_list_element_t<Index, decltype(pick_up(std::declval<Candidate>()))>;
/*! @brief N-th argument of the given function or member function. */
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

@@ -5,14 +5,23 @@
#include <cstdint>
#include <type_traits>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
// waiting for C++20 (and std::popcount)
template<typename Type>
static constexpr int popcount(Type value) noexcept {
return value ? (int(value & 1) + popcount(value >> 1)) : 0;
}
template<typename, typename = void>
struct entt_traits;
@@ -51,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.
@@ -59,10 +72,10 @@ struct entt_traits<std::uint64_t> {
*/
template<typename Traits>
class basic_entt_traits {
static constexpr auto length = popcount(Traits::entity_mask);
static constexpr auto length = internal::popcount(Traits::entity_mask);
static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask");
static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask");
static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask");
static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask");
public:
/*! @brief Value type. */
@@ -101,11 +114,7 @@ public:
* @return The integral representation of the version part.
*/
[[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept {
if constexpr(Traits::version_mask == 0u) {
return version_type{};
} else {
return (static_cast<version_type>(to_integral(value) >> length) & version_mask);
}
return static_cast<version_type>(to_integral(value) >> length);
}
/**
@@ -115,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)));
}
/**
@@ -129,11 +138,7 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
if constexpr(Traits::version_mask == 0u) {
return value_type{entity & entity_mask};
} else {
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version & version_mask) << length)};
}
return value_type{(entity & entity_mask) | (static_cast<entity_type>(version) << length)};
}
/**
@@ -147,11 +152,8 @@ public:
* @return A properly constructed identifier.
*/
[[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept {
if constexpr(Traits::version_mask == 0u) {
return value_type{lhs & entity_mask};
} else {
return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))};
}
constexpr auto mask = (version_mask << length);
return value_type{(lhs & entity_mask) | (rhs & mask)};
}
};
@@ -168,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 {
@@ -179,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 {
@@ -190,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 {
@@ -259,25 +255,25 @@ struct null_t {
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A null object yet to be converted.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept {
return rhs.operator==(lhs);
[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept {
return other.operator==(entity);
}
/**
* @brief Compares a null object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A null object yet to be converted.
* @param entity Identifier with which to compare.
* @param other A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept {
return !(rhs == lhs);
[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept {
return !(other == entity);
}
/*! @brief Tombstone object for all identifiers. */
@@ -321,12 +317,7 @@ struct tombstone_t {
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
using traits_type = entt_traits<Entity>;
if constexpr(traits_type::version_mask == 0u) {
return false;
} else {
return (traits_type::to_version(entity) == traits_type::to_version(*this));
}
return traits_type::to_version(entity) == traits_type::to_version(*this);
}
/**
@@ -344,25 +335,25 @@ struct tombstone_t {
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A tombstone object yet to be converted.
* @param entity Identifier with which to compare.
* @param other A tombstone object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept {
return rhs.operator==(lhs);
[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept {
return other.operator==(entity);
}
/**
* @brief Compares a tombstone object and an identifier of any type.
* @tparam Entity Type of identifier.
* @param lhs Identifier with which to compare.
* @param rhs A tombstone object yet to be converted.
* @param entity Identifier with which to compare.
* @param other A tombstone object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename Entity>
[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept {
return !(rhs == lhs);
[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept {
return !(other == entity);
}
/**

View File

@@ -4,7 +4,6 @@
#include <cstdint>
#include <memory>
#include <type_traits>
#include "../config/config.h"
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
@@ -18,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>>
@@ -29,11 +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;
template<typename, typename>
class basic_reactive_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;
@@ -47,14 +79,14 @@ class basic_runtime_view;
template<typename, typename, typename>
class basic_group;
template<typename, typename = std::allocator<void>>
template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>>
class basic_observer;
template<typename>
class basic_organizer;
template<typename, typename...>
class basic_handle;
struct basic_handle;
template<typename>
class basic_snapshot;
@@ -65,30 +97,100 @@ class basic_snapshot_loader;
template<typename>
class basic_continuous_loader;
/**
* @brief Alias for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
struct exclude_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr exclude_t() {}
};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() {}
};
/**
* @brief Variable template for lists of observed components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() {}
};
/**
* @brief Variable template for lists of owned components.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
/**
* @brief Applies a given _function_ to a get list and generate a new list.
* @tparam Type Types provided by the get list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<get_t<Type...>, Op> {
/*! @brief Resulting get list after applying the transform function. */
using type = get_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an exclude list and generate a new list.
* @tparam Type Types provided by the exclude list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<exclude_t<Type...>, Op> {
/*! @brief Resulting exclude list after applying the transform function. */
using type = exclude_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an owned list and generate a new list.
* @tparam Type Types provided by the owned list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<owned_t<Type...>, Op> {
/*! @brief Resulting owned list after applying the transform function. */
using type = owned_t<typename Op<Type>::type...>;
};
/*! @brief Alias declaration for the most common use case. */
using sparse_set = basic_sparse_set<>;
/**
* @brief Alias declaration for the most common use case.
* @tparam Type Element type.
* @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.
* @tparam Type Underlying storage type.
*/
template<typename Type>
using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
/*! @brief Alias declaration for the most common use case. */
using registry = basic_registry<>;
@@ -127,148 +229,6 @@ 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.
*/
template<typename... Type>
struct exclude_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr exclude_t() = default;
};
/**
* @brief Variable template for exclusion lists.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr exclude_t<Type...> exclude{};
/**
* @brief Alias for lists of observed elements.
* @tparam Type List of types.
*/
template<typename... Type>
struct get_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr get_t() = default;
};
/**
* @brief Variable template for lists of observed elements.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr get_t<Type...> get{};
/**
* @brief Alias for lists of owned elements.
* @tparam Type List of types.
*/
template<typename... Type>
struct owned_t final: type_list<Type...> {
/*! @brief Default constructor. */
explicit constexpr owned_t() = default;
};
/**
* @brief Variable template for lists of owned elements.
* @tparam Type List of types.
*/
template<typename... Type>
inline constexpr owned_t<Type...> owned{};
/**
* @brief Applies a given _function_ to a get list and generate a new list.
* @tparam Type Types provided by the get list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<get_t<Type...>, Op> {
/*! @brief Resulting get list after applying the transform function. */
using type = get_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an exclude list and generate a new list.
* @tparam Type Types provided by the exclude list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<exclude_t<Type...>, Op> {
/*! @brief Resulting exclude list after applying the transform function. */
using type = exclude_t<typename Op<Type>::type...>;
};
/**
* @brief Applies a given _function_ to an owned list and generate a new list.
* @tparam Type Types provided by the owned list.
* @tparam Op Unary operation as template class with a type member named `type`.
*/
template<typename... Type, template<typename...> class Op>
struct type_list_transform<owned_t<Type...>, Op> {
/*! @brief Resulting owned list after applying the transform function. */
using type = owned_t<typename Op<Type>::type...>;
};
/**
* @brief Provides a common way to define storage types.
* @tparam Type Storage value type.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<Type>, typename = void>
struct storage_type {
/*! @brief Type-to-storage conversion result. */
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
};
/*! @brief Empty value type for reactive storage types. */
struct reactive final {};
/**
* @ brief Partial specialization for reactive storage types.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Entity, typename Allocator>
struct storage_type<reactive, Entity, Allocator> {
/*! @brief Type-to-storage conversion result. */
using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_type_t = typename storage_type<Args...>::type;
/**
* Type-to-storage conversion utility that preserves constness.
* @tparam Type Storage value type, eventually const.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Type, typename Entity = entity, typename Allocator = std::allocator<std::remove_const_t<Type>>>
struct storage_for {
/*! @brief Type-to-storage conversion result. */
using type = constness_as_t<storage_type_t<std::remove_const_t<Type>, Entity, Allocator>, Type>;
};
/**
* @brief Helper type.
* @tparam Args Arguments to forward.
*/
template<typename... Args>
using storage_for_t = typename storage_for<Args...>::type;
/**
* @brief Alias declaration for the most common use case.
* @tparam Get Types of storage iterated by the view.
@@ -277,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

@@ -1,24 +1,26 @@
#ifndef ENTT_ENTITY_GROUP_HPP
#define ENTT_ENTITY_GROUP_HPP
#include <array>
#include <cstddef>
#include <iterator>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
#include "fwd.hpp"
#include "sparse_set.hpp"
#include "storage.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename, typename, typename>
@@ -27,8 +29,8 @@ class extended_group_iterator;
template<typename It, typename... Owned, typename... Get>
class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
template<typename Type>
[[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(std::is_void_v<typename Type::value_type>) {
auto index_to_element([[maybe_unused]] Type &cpool) const {
if constexpr(Type::traits_type::page_size == 0u) {
return std::make_tuple();
} else {
return std::forward_as_tuple(cpool.rbegin()[it.index()]);
@@ -37,20 +39,19 @@ class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> {
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::declval<Owned>().get_as_tuple({})..., std::declval<Get>().get_as_tuple({})...));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr extended_group_iterator()
: it{},
pools{} {}
extended_group_iterator(iterator_type from, std::tuple<Owned *..., Get *...> cpools)
extended_group_iterator(It from, const std::tuple<Owned *..., Get *...> &cpools)
: it{from},
pools{std::move(cpools)} {}
pools{cpools} {}
extended_group_iterator &operator++() noexcept {
return ++it, *this;
@@ -94,93 +95,100 @@ template<typename... Lhs, typename... Rhs>
struct group_descriptor {
using size_type = std::size_t;
virtual ~group_descriptor() = default;
[[nodiscard]] virtual bool owned(const id_type) const noexcept {
return false;
virtual size_type owned(const id_type *, const size_type) const noexcept {
return 0u;
}
};
template<typename Type, std::size_t Owned, std::size_t Get, std::size_t Exclude>
class group_handler final: public group_descriptor {
using entity_type = typename Type::entity_type;
template<typename, typename, typename>
class group_handler;
template<typename... Owned, typename... Get, typename... Exclude>
class group_handler<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::bool_constant<Owned::traits_type::in_place_delete>...>, "Groups do not support in-place delete");
static_assert(!std::disjunction_v<std::is_const<Owned>..., std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
void swap_elements(const std::size_t pos, const entity_type entt) {
for(size_type next{}; next < Owned; ++next) {
pools[next]->swap_elements((*pools[next])[pos], entt);
}
std::apply([pos, entt](auto *...cpool) { (cpool->swap_elements(cpool->data()[pos], entt), ...); }, pools);
}
void push_on_construct(const entity_type entt) {
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) {
swap_elements(len++, entt);
}
}
void push_on_destroy(const entity_type entt) {
if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools)
if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools)
&& std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) {
swap_elements(len++, entt);
}
}
void remove_if(const entity_type entt) {
if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) {
if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) {
swap_elements(--len, entt);
}
}
void common_setup() {
public:
using size_type = typename base_type::size_type;
group_handler(Owned &...opool, Get &...gpool, Exclude &...epool)
: pools{&opool..., &gpool...},
filter{&epool...},
len{} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
// we cannot iterate backwards because we want to leave behind valid entities in case of owned types
for(auto first = pools[0u]->rbegin(), last = first + pools[0u]->size(); first != last; ++first) {
for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) {
push_on_construct(*first);
}
}
public:
using common_type = Type;
using size_type = typename Type::size_type;
size_type owned(const id_type *elem, const size_type length) const noexcept final {
size_type cnt = 0u;
template<typename... OGType, typename... EType>
group_handler(std::tuple<OGType &...> ogpool, std::tuple<EType &...> epool)
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, (Owned + Get)>{&cpool...}; }, ogpool)},
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)} {
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool);
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
common_setup();
}
[[nodiscard]] bool owned(const id_type hash) const noexcept override {
for(size_type pos{}; pos < Owned; ++pos) {
if(pools[pos]->type().hash() == hash) {
return true;
}
for(auto pos = 0u; pos < length; ++pos) {
cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...);
}
return false;
return cnt;
}
[[nodiscard]] size_type length() const noexcept {
return len;
}
template<std::size_t Index>
[[nodiscard]] common_type *storage() const noexcept {
if constexpr(Index < (Owned + Get)) {
return pools[Index];
} else {
return filter[Index - (Owned + Get)];
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
}
private:
std::array<common_type *, (Owned + Get)> pools;
std::array<common_type *, Exclude> filter;
std::size_t len{};
std::tuple<Owned *..., Get *...> pools;
std::tuple<Exclude *...> filter;
std::size_t len;
};
template<typename Type, std::size_t Get, std::size_t Exclude>
class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor {
using entity_type = typename Type::entity_type;
template<typename... Get, typename... Exclude>
class group_handler<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> final: public group_descriptor {
// nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here
static_assert(!std::disjunction_v<std::is_const<Get>..., std::is_const<Exclude>...>, "Const storage type not allowed");
using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>;
using entity_type = typename base_type::entity_type;
void push_on_construct(const entity_type entt) {
if(!elem.contains(entt)
@@ -202,50 +210,52 @@ class group_handler<Type, 0u, Get, Exclude> final: public group_descriptor {
elem.remove(entt);
}
void common_setup() {
for(const auto entity: *pools[0u]) {
public:
using common_type = base_type;
template<typename Alloc>
group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool)
: pools{&gpool...},
filter{&epool...},
elem{alloc} {
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools);
std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter);
for(const auto entity: static_cast<base_type &>(*std::get<0>(pools))) {
push_on_construct(entity);
}
}
public:
using common_type = Type;
template<typename Allocator, typename... GType, typename... EType>
group_handler(const Allocator &allocator, std::tuple<GType &...> gpool, std::tuple<EType &...> epool)
: pools{std::apply([](auto &&...cpool) { return std::array<common_type *, Get>{&cpool...}; }, gpool)},
filter{std::apply([](auto &&...cpool) { return std::array<common_type *, Exclude>{&cpool...}; }, epool)},
elem{allocator} {
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool);
std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool);
common_setup();
}
[[nodiscard]] common_type &handle() noexcept {
common_type &handle() noexcept {
return elem;
}
[[nodiscard]] const common_type &handle() const noexcept {
const common_type &handle() const noexcept {
return elem;
}
template<std::size_t Index>
[[nodiscard]] common_type *storage() const noexcept {
if constexpr(Index < Get) {
return pools[Index];
} else {
return filter[Index - Get];
}
template<typename Type>
Type pools_as() const noexcept {
return pools;
}
template<typename Type>
Type filter_as() const noexcept {
return filter;
}
private:
std::array<common_type *, Get> pools;
std::array<common_type *, Exclude> filter;
common_type elem;
std::tuple<Get *...> pools;
std::tuple<Exclude *...> filter;
base_type elem;
};
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Group.
@@ -268,7 +278,7 @@ class basic_group;
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, elements are added
* * The entity currently pointed is modified (for example, components are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -284,12 +294,16 @@ class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> {
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::element_type..., typename Exclude::element_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_type..., typename Exclude::value_type...>>;
template<std::size_t... Index>
[[nodiscard]] auto pools_for(std::index_sequence<Index...>) const noexcept {
auto pools() const noexcept {
using return_type = std::tuple<Get *...>;
return descriptor ? return_type{static_cast<Get *>(descriptor->template storage<Index>())...} : return_type{};
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
}
public:
@@ -301,20 +315,12 @@ public:
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
/*! @brief Reverse iterator type. */
/*! @brief Reversed iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<common_type, 0u, sizeof...(Get), sizeof...(Exclude)>;
/**
* @brief Group opaque identifier.
* @return Group opaque identifier.
*/
static id_type group_id() noexcept {
return type_hash<basic_group<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
}
using handler = internal::group_handler<owned_t<>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
@@ -336,9 +342,9 @@ public:
}
/**
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @return The storage for the given element type.
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] auto *storage() const noexcept {
@@ -352,8 +358,13 @@ public:
*/
template<std::size_t Index>
[[nodiscard]] auto *storage() const noexcept {
using type = type_list_element_t<Index, type_list<Get..., Exclude...>>;
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
constexpr auto offset = sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
}
/**
@@ -486,11 +497,16 @@ public:
}
/**
* @brief Returns the elements assigned to the given entity.
* @tparam Type Type of the element to get.
* @tparam Other Other types of elements to get.
* @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.
* @return The elements assigned to the entity.
* @return The components assigned to the entity.
*/
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
@@ -498,14 +514,19 @@ public:
}
/**
* @brief Returns the elements assigned to the given entity.
* @tparam Index Indexes of the elements to get.
* @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 elements assigned to the entity.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
@@ -517,12 +538,12 @@ public:
}
/**
* @brief Iterates entities and elements and applies the given function
* @brief Iterates entities and components and applies the given function
* object to them.
*
* The function object is invoked for each entity. It is provided with the
* entity itself and a set of references to non-empty elements. The
* _constness_ of the elements is as requested.<br/>
* entity itself and a set of references to non-empty components. The
* _constness_ of the components is as requested.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
@@ -553,8 +574,8 @@ public:
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty elements. The _constness_ of the
* elements is as requested.
* set of references to its non-empty components. The _constness_ of the
* components is as requested.
*
* @note
* Empty types aren't explicitly instantiated and therefore they are never
@@ -563,7 +584,7 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
const auto cpools = pools_for(std::index_sequence_for<Get...>{});
const auto cpools = pools();
return iterable{{begin(), cpools}, {end(), cpools}};
}
@@ -591,8 +612,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional type of element to compare.
* @tparam Other Other optional types of elements to compare.
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -610,7 +631,7 @@ public:
*
* @sa sort
*
* @tparam Index Optional indexes of elements to compare.
* @tparam Index Optional indexes of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -625,7 +646,7 @@ public:
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");
descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...);
} else {
auto comp = [&compare, cpools = pools_for(std::index_sequence_for<Get...>{})](const entity_type lhs, const entity_type rhs) {
auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) {
if constexpr(sizeof...(Index) == 1) {
return compare((std::get<Index>(cpools)->get(lhs), ...), (std::get<Index>(cpools)->get(rhs), ...));
} else {
@@ -639,19 +660,16 @@ 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.
* @param other The storage to use to impose the order.
*/
template<typename It>
void sort_as(It first, It last) const {
void sort_as(const common_type &other) const {
if(*this) {
descriptor->handle().sort_as(first, last);
descriptor->handle().sort_as(other);
}
}
@@ -667,7 +685,7 @@ private:
*
* * It's guaranteed that the entity list is tightly packed in memory for fast
* iterations.
* * It's guaranteed that all elements in the owned storage are tightly packed
* * It's guaranteed that all components in the owned storage are tightly packed
* in memory for even faster iterations and to allow direct access.
* * They stay true to the order of the owned storage and all instances have the
* same order in memory.
@@ -679,7 +697,7 @@ private:
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, elements are added
* * The entity currently pointed is modified (for example, components are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -692,18 +710,20 @@ private:
*/
template<typename... Owned, typename... Get, typename... Exclude>
class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> {
static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete");
using base_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>;
using underlying_type = typename base_type::entity_type;
template<typename Type>
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::element_type..., typename Get::element_type..., typename Exclude::element_type...>>;
static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Owned::value_type..., typename Get::value_type..., typename Exclude::value_type...>>;
template<std::size_t... Index, std::size_t... Other>
[[nodiscard]] auto pools_for(std::index_sequence<Index...>, std::index_sequence<Other...>) const noexcept {
auto pools() const noexcept {
using return_type = std::tuple<Owned *..., Get *...>;
return descriptor ? return_type{static_cast<Owned *>(descriptor->template storage<Index>())..., static_cast<Get *>(descriptor->template storage<sizeof...(Owned) + Other>())...} : return_type{};
return descriptor ? descriptor->template pools_as<return_type>() : return_type{};
}
auto filter() const noexcept {
using return_type = std::tuple<Exclude *...>;
return descriptor ? descriptor->template filter_as<return_type>() : return_type{};
}
public:
@@ -715,20 +735,12 @@ public:
using common_type = base_type;
/*! @brief Random access iterator type. */
using iterator = typename common_type::iterator;
/*! @brief Reverse iterator type. */
/*! @brief Reversed iterator type. */
using reverse_iterator = typename common_type::reverse_iterator;
/*! @brief Iterable group type. */
using iterable = iterable_adaptor<internal::extended_group_iterator<iterator, owned_t<Owned...>, get_t<Get...>>>;
/*! @brief Group handler type. */
using handler = internal::group_handler<common_type, sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)>;
/**
* @brief Group opaque identifier.
* @return Group opaque identifier.
*/
static id_type group_id() noexcept {
return type_hash<basic_group<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>>::value();
}
using handler = internal::group_handler<owned_t<std::remove_const_t<Owned>...>, get_t<std::remove_const_t<Get>...>, exclude_t<std::remove_const_t<Exclude>...>>;
/*! @brief Default constructor to use to create empty, invalid groups. */
basic_group() noexcept
@@ -750,9 +762,9 @@ public:
}
/**
* @brief Returns the storage for a given element type, if any.
* @tparam Type Type of element of which to return the storage.
* @return The storage for the given element type.
* @brief Returns the storage for a given component type, if any.
* @tparam Type Type of component of which to return the storage.
* @return The storage for the given component type.
*/
template<typename Type>
[[nodiscard]] auto *storage() const noexcept {
@@ -766,8 +778,13 @@ public:
*/
template<std::size_t Index>
[[nodiscard]] auto *storage() const noexcept {
using type = type_list_element_t<Index, type_list<Owned..., Get..., Exclude...>>;
return *this ? static_cast<type *>(descriptor->template storage<Index>()) : nullptr;
constexpr auto offset = sizeof...(Owned) + sizeof...(Get);
if constexpr(Index < offset) {
return std::get<Index>(pools());
} else {
return std::get<Index - offset>(filter());
}
}
/**
@@ -885,11 +902,16 @@ public:
}
/**
* @brief Returns the elements assigned to the given entity.
* @tparam Type Type of the element to get.
* @tparam Other Other types of elements to get.
* @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.
* @return The elements assigned to the entity.
* @return The components assigned to the entity.
*/
template<typename Type, typename... Other>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
@@ -897,14 +919,19 @@ public:
}
/**
* @brief Returns the elements assigned to the given entity.
* @tparam Index Indexes of the elements to get.
* @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 elements assigned to the entity.
* @return The components assigned to the entity.
*/
template<std::size_t... Index>
[[nodiscard]] decltype(auto) get(const entity_type entt) const {
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools);
@@ -916,12 +943,12 @@ public:
}
/**
* @brief Iterates entities and elements and applies the given function
* @brief Iterates entities and components and applies the given function
* object to them.
*
* The function object is invoked for each entity. It is provided with the
* entity itself and a set of references to non-empty elements. The
* _constness_ of the elements is as requested.<br/>
* entity itself and a set of references to non-empty components. The
* _constness_ of the components is as requested.<br/>
* The signature of the function must be equivalent to one of the following
* forms:
*
@@ -952,8 +979,8 @@ public:
* @brief Returns an iterable object to use to _visit_ a group.
*
* The iterable object returns tuples that contain the current entity and a
* set of references to its non-empty elements. The _constness_ of the
* elements is as requested.
* set of references to its non-empty components. The _constness_ of the
* components is as requested.
*
* @note
* Empty types aren't explicitly instantiated and therefore they are never
@@ -962,8 +989,8 @@ public:
* @return An iterable object to use to _visit_ the group.
*/
[[nodiscard]] iterable each() const noexcept {
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
return iterable{{begin(), cpools}, {end(), cpools}};
const auto cpools = pools();
return {{begin(), cpools}, {end(), cpools}};
}
/**
@@ -991,8 +1018,8 @@ public:
* * An iterator past the last element of the range to sort.
* * A comparison function to use to compare the elements.
*
* @tparam Type Optional type of element to compare.
* @tparam Other Other optional types of elements to compare.
* @tparam Type Optional type of component to compare.
* @tparam Other Other optional types of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1010,7 +1037,7 @@ public:
*
* @sa sort
*
* @tparam Index Optional indexes of elements to compare.
* @tparam Index Optional indexes of components to compare.
* @tparam Compare Type of comparison function object.
* @tparam Sort Type of sort function object.
* @tparam Args Types of arguments to forward to the sort function object.
@@ -1020,7 +1047,7 @@ public:
*/
template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const {
const auto cpools = pools_for(std::index_sequence_for<Owned...>{}, std::index_sequence_for<Get...>{});
const auto cpools = pools();
if constexpr(sizeof...(Index) == 0) {
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function");

View File

@@ -5,7 +5,6 @@
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/iterator.hpp"
#include "../core/type_traits.hpp"
#include "entity.hpp"
@@ -13,7 +12,11 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename It>
@@ -30,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},
@@ -84,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.
@@ -95,29 +101,19 @@ template<typename ILhs, typename IRhs>
* @tparam Scope Types to which to restrict the scope of a handle.
*/
template<typename Registry, typename... Scope>
class basic_handle {
using traits_type = entt_traits<typename Registry::entity_type>;
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<Registry &>(*owner);
}
public:
struct basic_handle {
/*! @brief Type of registry accepted by the handle. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
using entity_type = typename registry_type::entity_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
using version_type = typename registry_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Iterable handle type. */
using iterable = iterable_adaptor<internal::handle_storage_iterator<typename decltype(std::declval<registry_type>().storage())::iterator>>;
using size_type = typename registry_type::size_type;
/*! @brief Constructs an invalid handle. */
basic_handle() noexcept
: owner{},
: reg{},
entt{null} {}
/**
@@ -126,7 +122,7 @@ public:
* @param value A valid identifier.
*/
basic_handle(registry_type &ref, entity_type value) noexcept
: owner{&ref},
: reg{&ref},
entt{value} {}
/**
@@ -139,23 +135,49 @@ public:
*
* @return An iterable object to use to _visit_ the handle.
*/
[[nodiscard]] iterable storage() const noexcept {
auto underlying = owner_or_assert().storage();
return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}};
}
/*! @copydoc valid */
[[nodiscard]] explicit operator bool() const noexcept {
return owner && owner->valid(entt);
[[nodiscard]] auto storage() const noexcept {
auto iterable = reg->storage();
using iterator_type = internal::handle_storage_iterator<typename decltype(iterable)::iterator>;
return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}};
}
/**
* @brief Checks if a handle refers to a valid registry and entity.
* @return True if the handle refers to a valid registry and entity, false
* otherwise.
* @brief Constructs a const handle from a non-const one.
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
*/
template<typename Other, typename... Args>
operator basic_handle<Other, Args...>() const noexcept {
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
return reg ? basic_handle<Other, Args...>{*reg, entt} : basic_handle<Other, Args...>{};
}
/**
* @brief Converts a handle to its underlying entity.
* @return The contained identifier.
*/
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
/**
* @brief Checks if a handle refers to non-null registry pointer and entity.
* @return True if the handle refers to non-null registry and entity, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return reg && reg->valid(entt);
}
/**
* @brief Checks if a handle refers to a valid entity or not.
* @return True if the handle refers to a valid entity, false otherwise.
*/
[[nodiscard]] bool valid() const {
return static_cast<bool>(*this);
return reg->valid(entt);
}
/**
@@ -163,7 +185,7 @@ public:
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] registry_type *registry() const noexcept {
return owner;
return reg;
}
/**
@@ -174,14 +196,9 @@ public:
return entt;
}
/*! @copydoc entity */
[[nodiscard]] operator entity_type() const noexcept {
return entity();
}
/*! @brief Destroys the entity associated with a handle. */
void destroy() {
owner_or_assert().destroy(std::exchange(entt, null));
reg->destroy(std::exchange(entt, null));
}
/**
@@ -189,164 +206,148 @@ public:
* @param version A desired version upon destruction.
*/
void destroy(const version_type version) {
owner_or_assert().destroy(std::exchange(entt, null), version);
reg->destroy(std::exchange(entt, null), version);
}
/**
* @brief Assigns the given element to a handle.
* @tparam Type Type of element to create.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
* @brief Assigns the given component to a handle.
* @tparam Component Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
*/
template<typename Type, typename... Args>
// NOLINTNEXTLINE(modernize-use-nodiscard)
template<typename Component, typename... Args>
decltype(auto) emplace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template emplace<Type>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template emplace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Assigns or replaces the given element for a handle.
* @tparam Type Type of element to assign or replace.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the newly created element.
* @brief Assigns or replaces the given component for a handle.
* @tparam Component Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the newly created component.
*/
template<typename Type, typename... Args>
template<typename Component, typename... Args>
decltype(auto) emplace_or_replace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template emplace_or_replace<Type>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template emplace_or_replace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Patches the given element for a handle.
* @tparam Type Type of element to patch.
* @brief Patches the given component for a handle.
* @tparam Component Type of component to patch.
* @tparam Func Types of the function objects to invoke.
* @param func Valid function objects.
* @return A reference to the patched element.
* @return A reference to the patched component.
*/
template<typename Type, typename... Func>
template<typename Component, typename... Func>
decltype(auto) patch(Func &&...func) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template patch<Type>(entt, std::forward<Func>(func)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template patch<Component>(entt, std::forward<Func>(func)...);
}
/**
* @brief Replaces the given element for a handle.
* @tparam Type Type of element to replace.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return A reference to the element being replaced.
* @brief Replaces the given component for a handle.
* @tparam Component Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return A reference to the component being replaced.
*/
template<typename Type, typename... Args>
template<typename Component, typename... Args>
decltype(auto) replace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template replace<Type>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template replace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Removes the given elements from a handle.
* @tparam Type Types of elements to remove.
* @return The number of elements actually removed.
* @brief Removes the given components from a handle.
* @tparam Component Types of components to remove.
* @return The number of components actually removed.
*/
template<typename... Type>
// NOLINTNEXTLINE(modernize-use-nodiscard)
template<typename... Component>
size_type remove() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template remove<Type...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template remove<Component...>(entt);
}
/**
* @brief Erases the given elements from a handle.
* @tparam Type Types of elements to erase.
* @brief Erases the given components from a handle.
* @tparam Component Types of components to erase.
*/
template<typename... Type>
template<typename... Component>
void erase() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
owner_or_assert().template erase<Type...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
reg->template erase<Component...>(entt);
}
/**
* @brief Checks if a handle has all the given elements.
* @tparam Type Elements for which to perform the check.
* @return True if the handle has all the elements, false otherwise.
* @brief Checks if a handle has all the given components.
* @tparam Component Components for which to perform the check.
* @return True if the handle has all the components, false otherwise.
*/
template<typename... Type>
template<typename... Component>
[[nodiscard]] decltype(auto) all_of() const {
return owner_or_assert().template all_of<Type...>(entt);
return reg->template all_of<Component...>(entt);
}
/**
* @brief Checks if a handle has at least one of the given elements.
* @tparam Type Elements for which to perform the check.
* @return True if the handle has at least one of the given elements,
* @brief Checks if a handle has at least one of the given components.
* @tparam Component Components for which to perform the check.
* @return True if the handle has at least one of the given components,
* false otherwise.
*/
template<typename... Type>
template<typename... Component>
[[nodiscard]] decltype(auto) any_of() const {
return owner_or_assert().template any_of<Type...>(entt);
return reg->template any_of<Component...>(entt);
}
/**
* @brief Returns references to the given elements for a handle.
* @tparam Type Types of elements to get.
* @return References to the elements owned by the handle.
* @brief Returns references to the given components for a handle.
* @tparam Component Types of components to get.
* @return References to the components owned by the handle.
*/
template<typename... Type>
template<typename... Component>
[[nodiscard]] decltype(auto) get() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template get<Type...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template get<Component...>(entt);
}
/**
* @brief Returns a reference to the given element for a handle.
* @tparam Type Type of element to get.
* @tparam Args Types of arguments to use to construct the element.
* @param args Parameters to use to initialize the element.
* @return Reference to the element owned by the handle.
* @brief Returns a reference to the given component for a handle.
* @tparam Component Type of component to get.
* @tparam Args Types of arguments to use to construct the component.
* @param args Parameters to use to initialize the component.
* @return Reference to the component owned by the handle.
*/
template<typename Type, typename... Args>
template<typename Component, typename... Args>
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Type, Scope>), "Invalid type");
return owner_or_assert().template get_or_emplace<Type>(entt, std::forward<Args>(args)...);
static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v<Component, Scope>), "Invalid type");
return reg->template get_or_emplace<Component>(entt, std::forward<Args>(args)...);
}
/**
* @brief Returns pointers to the given elements for a handle.
* @tparam Type Types of elements to get.
* @return Pointers to the elements owned by the handle.
* @brief Returns pointers to the given components for a handle.
* @tparam Component Types of components to get.
* @return Pointers to the components owned by the handle.
*/
template<typename... Type>
template<typename... Component>
[[nodiscard]] auto try_get() const {
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
return owner_or_assert().template try_get<Type...>(entt);
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Component> && ...), "Invalid type");
return reg->template try_get<Component...>(entt);
}
/**
* @brief Checks if a handle has elements assigned.
* @return True if the handle has no elements assigned, false otherwise.
* @brief Checks if a handle has components assigned.
* @return True if the handle has no components assigned, false otherwise.
*/
[[nodiscard]] bool orphan() const {
return owner_or_assert().orphan(entt);
}
/**
* @brief Returns a const handle from a non-const one.
* @tparam Other A valid entity type.
* @tparam Args Scope of the handle to construct.
* @return A const handle referring to the same registry and the same
* entity.
*/
template<typename Other, typename... Args>
operator basic_handle<Other, Args...>() const noexcept {
static_assert(std::is_same_v<Other, Registry> || std::is_same_v<std::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
return owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{};
return reg->orphan(entt);
}
private:
registry_type *owner;
registry_type *reg;
entity_type entt;
};
@@ -378,54 +379,6 @@ template<typename... Args, typename... Other>
return !(lhs == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() == rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return False if the two elements differ, true otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs == lhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A valid handle.
* @param rhs A null object yet to be converted.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const basic_handle<Args...> &lhs, const null_t rhs) noexcept {
return (lhs.entity() != rhs);
}
/**
* @brief Compares a handle with the null object.
* @tparam Args Scope of the handle.
* @param lhs A null object yet to be converted.
* @param rhs A valid handle.
* @return True if the two elements differ, false otherwise.
*/
template<typename... Args>
[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle<Args...> &rhs) noexcept {
return (rhs != lhs);
}
} // namespace entt
#endif

View File

@@ -6,10 +6,9 @@
#include <utility>
#include "../core/fwd.hpp"
#include "../core/type_traits.hpp"
#include "component.hpp"
#include "../signal/delegate.hpp"
#include "fwd.hpp"
#include "group.hpp"
#include "storage.hpp"
#include "view.hpp"
namespace entt {
@@ -21,8 +20,8 @@ namespace entt {
template<typename Registry>
class as_view {
template<typename... Get, typename... Exclude>
[[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
return reg.template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
return reg.template view<constness_as_t<typename Get::value_type, Get>...>(exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
}
public:
@@ -60,11 +59,11 @@ private:
template<typename Registry>
class as_group {
template<typename... Owned, typename... Get, typename... Exclude>
[[nodiscard]] auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
if constexpr(std::is_const_v<registry_type>) {
return reg.template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_type...>{});
return reg.template group_if_exists<typename Owned::value_type...>(get_t<typename Get::value_type...>{}, exclude_t<typename Exclude::value_type...>{});
} else {
return reg.template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
return reg.template group<constness_as_t<typename Owned::value_type, Owned>...>(get_t<constness_as_t<typename Get::value_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::value_type, Exclude>...>{});
}
}
@@ -99,39 +98,43 @@ private:
/**
* @brief Helper to create a listener that directly invokes a member function.
* @tparam Member Member function to invoke on an element of the given type.
* @tparam Member Member function to invoke on a component of the given type.
* @tparam Registry Basic registry type.
* @param reg A registry that contains the given entity and its elements.
* @param entt Entity from which to get the element.
* @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");
(reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt);
delegate<void(Registry &, const typename Registry::entity_type)> func;
func.template connect<Member>(reg.template get<member_class_t<decltype(Member)>>(entt));
func(reg, entt);
}
/**
* @brief Returns the entity associated with a given element.
* @brief Returns the entity associated with a given component.
*
* @warning
* Currently, this function only works correctly with the default storage as it
* makes assumptions about how the elements are laid out.
* 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 element.
* @param instance A valid element instance.
* @return The entity associated with the given element.
* @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 basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) {
using traits_type = component_traits<typename basic_storage<Args...>::value_type>;
static_assert(traits_type::page_size != 0u, "Unexpected page size");
const typename basic_storage<Args...>::base_type &base = storage;
const auto *addr = std::addressof(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) {
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 += traits_type::page_size) {
if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(traits_type::page_size)) {
return *(it + dist);
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);
}
}
}

View File

@@ -5,54 +5,12 @@
#include <utility>
#include "../config/config.h"
#include "../core/any.hpp"
#include "../core/type_info.hpp"
#include "../signal/sigh.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
namespace internal {
template<typename, typename, typename = void>
struct has_on_construct final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_construct<Type, Registry, std::void_t<decltype(Type::on_construct(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename, typename, typename = void>
struct has_on_update final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_update<Type, Registry, std::void_t<decltype(Type::on_update(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename, typename, typename = void>
struct has_on_destroy final: std::false_type {};
template<typename Type, typename Registry>
struct has_on_destroy<Type, Registry, std::void_t<decltype(Type::on_destroy(std::declval<Registry &>(), std::declval<Registry>().create()))>>
: std::true_type {};
template<typename Type>
auto *any_to_owner(any &value) noexcept {
using base_type = basic_registry<typename Type::entity_type, typename Type::allocator_type>;
auto *reg = any_cast<base_type>(&value);
if constexpr(!std::is_same_v<Type, base_type>) {
if(!reg) {
reg = any_cast<Type>(&value);
}
}
return reg;
}
} // namespace internal
/*! @endcond */
/**
* @brief Mixin type used to add signal support to storage types.
*
@@ -64,26 +22,20 @@ auto *any_to_owner(any &value) noexcept {
*
* 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 owner_type::entity_type, typename owner_type::allocator_type>;
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
using basic_registry_type = basic_registry<typename underlying_type::entity_type, typename underlying_type::base_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");
[[nodiscard]] auto &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;
}
private:
void pop(underlying_iterator first, underlying_iterator last) final {
if(auto &reg = owner_or_assert(); destruction.empty()) {
underlying_type::pop(first, last);
@@ -99,19 +51,13 @@ private:
void pop_all() final {
if(auto &reg = owner_or_assert(); !destruction.empty()) {
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) {
destruction.publish(reg, underlying_type::base_type::operator[](pos));
}
} else {
for(auto entt: static_cast<typename underlying_type::base_type &>(*this)) {
if constexpr(underlying_type::storage_policy == deletion_policy::in_place) {
if(entt != tombstone) {
destruction.publish(reg, entt);
}
} else {
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)));
}
}
}
@@ -129,54 +75,34 @@ private:
return it;
}
void bind_any(any value) noexcept final {
owner = internal::any_to_owner<registry_type>(value);
underlying_type::bind_any(std::move(value));
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = owner_type;
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},
destruction{allocator},
update{allocator} {
if constexpr(internal::has_on_construct<typename underlying_type::element_type, Registry>::value) {
entt::sink{construction}.template connect<&underlying_type::element_type::on_construct>();
}
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
entt::sink{update}.template connect<&underlying_type::element_type::on_update>();
}
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::value) {
entt::sink{destruction}.template connect<&underlying_type::element_type::on_destroy>();
}
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_sigh_mixin(const basic_sigh_mixin &) = delete;
update{allocator} {}
/**
* @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)},
@@ -188,29 +114,24 @@ 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)
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},
destruction{std::move(other.destruction), allocator},
update{std::move(other.update), allocator} {}
/*! @brief Default destructor. */
~basic_sigh_mixin() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This mixin.
* @return This storage.
*/
basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept {
swap(other);
sigh_mixin &operator=(sigh_mixin &&other) noexcept {
underlying_type::operator=(std::move(other));
owner = other.owner;
construction = std::move(other.construction);
destruction = std::move(other.destruction);
update = std::move(other.update);
return *this;
}
@@ -218,13 +139,13 @@ 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) noexcept {
void swap(sigh_mixin &other) {
using std::swap;
underlying_type::swap(other);
swap(owner, other.owner);
swap(construction, other.construction);
swap(destruction, other.destruction);
swap(update, other.update);
underlying_type::swap(other);
}
/**
@@ -272,27 +193,6 @@ public:
return sink{destruction};
}
/**
* @brief Checks if a mixin refers to a valid registry.
* @return True if the mixin refers to a valid registry, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (owner != nullptr);
}
/**
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] const registry_type &registry() const noexcept {
return owner_or_assert();
}
/*! @copydoc registry */
[[nodiscard]] registry_type &registry() noexcept {
return owner_or_assert();
}
/**
* @brief Emplace elements into a storage.
*
@@ -300,7 +200,7 @@ public:
* (for example, components vs entities).<br/>
* Refer to the specific documentation for more details.
*
* @return Whatever the underlying storage returns.
* @return A return value as returned by the underlying storage.
*/
auto emplace() {
const auto entt = underlying_type::emplace();
@@ -318,12 +218,11 @@ public:
* @tparam Args Types of arguments to forward to the underlying storage.
* @param hint A valid identifier.
* @param args Parameters to forward to the underlying storage.
* @return Whatever the underlying storage returns.
* @return A return value as returned by the underlying storage.
*/
template<typename... Args>
std::conditional_t<std::is_same_v<typename underlying_type::element_type, entity_type>, entity_type, decltype(std::declval<underlying_type>().get({}))>
emplace(const entity_type hint, Args &&...args) {
if constexpr(std::is_same_v<typename underlying_type::element_type, entity_type>) {
decltype(auto) emplace(const entity_type hint, Args &&...args) {
if constexpr(std::is_same_v<typename underlying_type::value_type, typename underlying_type::entity_type>) {
const auto entt = underlying_type::emplace(hint, std::forward<Args>(args)...);
construction.publish(owner_or_assert(), entt);
return entt;
@@ -363,16 +262,25 @@ public:
*/
template<typename It, typename... Args>
void insert(It first, It last, Args &&...args) {
auto from = underlying_type::size();
underlying_type::insert(first, last, std::forward<Args>(args)...);
if(auto &reg = owner_or_assert(); !construction.empty()) {
for(const auto to = underlying_type::size(); from != to; ++from) {
construction.publish(reg, underlying_type::operator[](from));
for(; first != last; ++first) {
construction.publish(reg, *first);
}
}
}
/**
* @brief Forwards variables to derived classes, if any.
* @param value A variable wrapped in an opaque container.
*/
void bind(any value) noexcept final {
auto *reg = any_cast<basic_registry_type>(&value);
owner = reg ? reg : owner;
underlying_type::bind(std::move(value));
}
private:
basic_registry_type *owner;
sigh_type construction;
@@ -380,194 +288,6 @@ private:
sigh_type update;
};
/**
* @brief Mixin type used to add _reactive_ support to storage types.
* @tparam Type Underlying storage type.
* @tparam Registry Basic registry type.
*/
template<typename Type, typename Registry>
class basic_reactive_mixin final: public Type {
using underlying_type = Type;
using owner_type = Registry;
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
static_assert(std::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
[[nodiscard]] auto &owner_or_assert() const noexcept {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
return static_cast<owner_type &>(*owner);
}
void emplace_element(const Registry &, typename underlying_type::entity_type entity) {
if(!underlying_type::contains(entity)) {
underlying_type::emplace(entity);
}
}
private:
void bind_any(any value) noexcept final {
owner = internal::any_to_owner<registry_type>(value);
underlying_type::bind_any(std::move(value));
}
public:
/*! @brief Allocator type. */
using allocator_type = typename underlying_type::allocator_type;
/*! @brief Underlying entity identifier. */
using entity_type = typename underlying_type::entity_type;
/*! @brief Expected registry type. */
using registry_type = owner_type;
/*! @brief Default constructor. */
basic_reactive_mixin()
: basic_reactive_mixin{allocator_type{}} {}
/**
* @brief Constructs an empty storage with a given allocator.
* @param allocator The allocator to use.
*/
explicit basic_reactive_mixin(const allocator_type &allocator)
: underlying_type{allocator},
owner{} {
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_reactive_mixin(const basic_reactive_mixin &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
basic_reactive_mixin(basic_reactive_mixin &&other) noexcept
: underlying_type{std::move(other)},
owner{other.owner} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator)
: underlying_type{std::move(other), allocator},
owner{other.owner} {}
/*! @brief Default destructor. */
~basic_reactive_mixin() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This mixin.
*/
basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This mixin.
*/
basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept {
swap(other);
return *this;
}
/**
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_reactive_mixin &other) noexcept {
using std::swap;
swap(owner, other.owner);
underlying_type::swap(other);
}
/**
* @brief Makes storage _react_ to creation of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*this);
return *this;
}
/**
* @brief Makes storage _react_ to update of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*this);
return *this;
}
/**
* @brief Makes storage _react_ to destruction of objects of the given type.
* @tparam Clazz Type of element to _react_ to.
* @tparam Candidate Function to use to _react_ to the event.
* @param id Optional name used to map the storage within the registry.
* @return This mixin.
*/
template<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*this);
return *this;
}
/**
* @brief Checks if a mixin refers to a valid registry.
* @return True if the mixin refers to a valid registry, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (owner != nullptr);
}
/**
* @brief Returns a pointer to the underlying registry, if any.
* @return A pointer to the underlying registry, if any.
*/
[[nodiscard]] const registry_type &registry() const noexcept {
return owner_or_assert();
}
/*! @copydoc registry */
[[nodiscard]] registry_type &registry() noexcept {
return owner_or_assert();
}
/**
* @brief Returns a view that is filtered by the underlying storage.
* @tparam Get Types of elements used to construct the view.
* @tparam Exclude Types of elements used to filter the view.
* @return A newly created view.
*/
template<typename... Get, typename... Exclude>
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) const {
const owner_type &parent = owner_or_assert();
basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>> elem{};
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<std::remove_const_t<Exclude>>()..., parent.template storage<std::remove_const_t<Get>>()..., this);
return elem;
}
/*! @copydoc view */
template<typename... Get, typename... Exclude>
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
view(exclude_t<Exclude...> = exclude_t{}) {
owner_type &parent = owner_or_assert();
return {*this, parent.template storage<std::remove_const_t<Get>>()..., parent.template storage<std::remove_const_t<Exclude>>()...};
}
private:
basic_registry_type *owner;
};
} // namespace entt
#endif

View File

@@ -7,6 +7,7 @@
#include <type_traits>
#include <utility>
#include "../core/type_traits.hpp"
#include "../signal/delegate.hpp"
#include "fwd.hpp"
#include "storage.hpp"
@@ -37,8 +38,8 @@ template<>
struct basic_collector<> {
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of elements tracked by the matcher.
* @tparam NoneOf Types of elements used to filter out entities.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
@@ -48,7 +49,7 @@ struct basic_collector<> {
/**
* @brief Adds an observing matcher to the collector.
* @tparam AnyOf Type of element for which changes should be detected.
* @tparam AnyOf Type of component for which changes should be detected.
* @return The updated collector.
*/
template<typename AnyOf>
@@ -66,14 +67,14 @@ struct basic_collector<> {
* @tparam Other Other matchers.
*/
template<typename... Reject, typename... Require, typename... Rule, typename... Other>
struct [[deprecated("use reactive mixin instead")]] basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule...>, Other...> {
/*! @brief Current matcher. */
using current_type = matcher<type_list<Reject...>, type_list<Require...>, Rule...>;
/**
* @brief Adds a grouping matcher to the collector.
* @tparam AllOf Types of elements tracked by the matcher.
* @tparam NoneOf Types of elements used to filter out entities.
* @tparam AllOf Types of components tracked by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
@@ -83,7 +84,7 @@ struct [[deprecated("use reactive mixin instead")]] basic_collector<matcher<type
/**
* @brief Adds an observing matcher to the collector.
* @tparam AnyOf Type of element for which changes should be detected.
* @tparam AnyOf Type of component for which changes should be detected.
* @return The updated collector.
*/
template<typename AnyOf>
@@ -93,8 +94,8 @@ struct [[deprecated("use reactive mixin instead")]] basic_collector<matcher<type
/**
* @brief Updates the filter of the last added matcher.
* @tparam AllOf Types of elements required by the matcher.
* @tparam NoneOf Types of elements used to filter out entities.
* @tparam AllOf Types of components required by the matcher.
* @tparam NoneOf Types of components used to filter out entities.
* @return The updated collector.
*/
template<typename... AllOf, typename... NoneOf>
@@ -113,13 +114,13 @@ inline constexpr basic_collector<> collector{};
* An observer returns all the entities and only the entities that fit the
* requirements of at least one matcher. Moreover, it's guaranteed that the
* entity list is tightly packed in memory for fast iterations.<br/>
* In general, observers don't stay true to the order of any set of elements.
* In general, observers don't stay true to the order of any set of components.
*
* Observers work mainly with two types of matchers, provided through a
* collector:
*
* * Observing matcher: an observer will return at least all the living entities
* for which one or more of the given elements have been updated and not yet
* for which one or more of the given components have been updated and not yet
* destroyed.
* * Grouping matcher: an observer will return at least all the living entities
* that would have entered the given group if it existed and that would have
@@ -139,13 +140,13 @@ inline constexpr basic_collector<> collector{};
*
* Iterators aren't invalidated if:
*
* * New instances of the given elements are created and assigned to entities.
* * New instances of the given components are created and assigned to entities.
* * The entity currently pointed is modified (as an example, if one of the
* given elements is removed from the entity to which the iterator points).
* given components is removed from the entity to which the iterator points).
* * The entity currently pointed is destroyed.
*
* In all the other cases, modifying the pools of the given elements in any way
* invalidates all the iterators.
* In all the other cases, modifying the pools of the given components in any
* way invalidates all the iterators.
*
* @warning
* Lifetime of an observer doesn't necessarily have to overcome that of the
@@ -154,19 +155,12 @@ inline constexpr basic_collector<> collector{};
* pointers.
*
* @tparam Registry Basic registry type.
* @tparam Mask Mask type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
template<typename Registry, typename Allocator>
class basic_observer {
using mask_type = std::uint64_t;
using storage_type = basic_storage<mask_type, typename Registry::entity_type, typename std::allocator_traits<Allocator>::template rebind_alloc<mask_type>>;
template<std::size_t Index>
static void discard_if(storage_type &storage, Registry &, const typename Registry::entity_type entt) {
if(storage.contains(entt) && !(storage.get(entt) &= (~(1 << Index)))) {
storage.erase(entt);
}
}
template<typename Registry, typename Mask, typename Allocator>
class basic_observer: private basic_storage<Mask, typename Registry::entity_type, Allocator> {
using base_type = basic_storage<Mask, typename Registry::entity_type, Allocator>;
template<typename>
struct matcher_handler;
@@ -174,93 +168,108 @@ class basic_observer {
template<typename... Reject, typename... Require, typename AnyOf>
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, AnyOf>> {
template<std::size_t Index>
static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
if(parent.template all_of<Require...>(entt) && !parent.template any_of<Reject...>(entt)) {
if(!storage.contains(entt)) {
storage.emplace(entt);
static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
if(reg.template all_of<Require...>(entt) && !reg.template any_of<Reject...>(entt)) {
if(!obs.contains(entt)) {
obs.emplace(entt);
}
storage.get(entt) |= (1 << Index);
obs.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void connect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
parent.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(storage);
parent.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(storage);
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
obs.erase(entt);
}
}
static void disconnect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().disconnect(&storage), ...);
(parent.template on_construct<Reject>().disconnect(&storage), ...);
parent.template on_update<AnyOf>().disconnect(&storage);
parent.template on_destroy<AnyOf>().disconnect(&storage);
template<std::size_t Index>
static void connect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
reg.template on_update<AnyOf>().template connect<&maybe_valid_if<Index>>(obs);
reg.template on_destroy<AnyOf>().template connect<&discard_if<Index>>(obs);
}
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
reg.template on_update<AnyOf>().disconnect(&obs);
reg.template on_destroy<AnyOf>().disconnect(&obs);
}
};
template<typename... Reject, typename... Require, typename... NoneOf, typename... AllOf>
struct matcher_handler<matcher<type_list<Reject...>, type_list<Require...>, type_list<NoneOf...>, AllOf...>> {
template<std::size_t Index, typename... Ignore>
static void maybe_valid_if(storage_type &storage, Registry &parent, const typename Registry::entity_type entt) {
bool guard{};
static void maybe_valid_if(basic_observer &obs, Registry &reg, const typename Registry::entity_type entt) {
auto condition = [&reg, entt]() {
if constexpr(sizeof...(Ignore) == 0) {
return reg.template all_of<AllOf..., Require...>(entt) && !reg.template any_of<NoneOf..., Reject...>(entt);
} else {
return reg.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !reg.template any_of<NoneOf>(entt)) && ...) && !reg.template any_of<Reject...>(entt);
}
};
if constexpr(sizeof...(Ignore) == 0) {
guard = parent.template all_of<AllOf..., Require...>(entt) && !parent.template any_of<NoneOf..., Reject...>(entt);
} else {
guard = parent.template all_of<AllOf..., Require...>(entt) && ((std::is_same_v<Ignore..., NoneOf> || !parent.template any_of<NoneOf>(entt)) && ...) && !parent.template any_of<Reject...>(entt);
}
if(guard) {
if(!storage.contains(entt)) {
storage.emplace(entt);
if(condition()) {
if(!obs.contains(entt)) {
obs.emplace(entt);
}
storage.get(entt) |= (1 << Index);
obs.get(entt) |= (1 << Index);
}
}
template<std::size_t Index>
static void connect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<Reject>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(storage), ...);
(parent.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(storage), ...);
(parent.template on_destroy<AllOf>().template connect<&discard_if<Index>>(storage), ...);
(parent.template on_construct<NoneOf>().template connect<&discard_if<Index>>(storage), ...);
static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) {
if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) {
obs.erase(entt);
}
}
static void disconnect(storage_type &storage, Registry &parent) {
(parent.template on_destroy<Require>().disconnect(&storage), ...);
(parent.template on_construct<Reject>().disconnect(&storage), ...);
(parent.template on_construct<AllOf>().disconnect(&storage), ...);
(parent.template on_destroy<NoneOf>().disconnect(&storage), ...);
(parent.template on_destroy<AllOf>().disconnect(&storage), ...);
(parent.template on_construct<NoneOf>().disconnect(&storage), ...);
template<std::size_t Index>
static void connect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<Reject>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<AllOf>().template connect<&maybe_valid_if<Index>>(obs), ...);
(reg.template on_destroy<NoneOf>().template connect<&maybe_valid_if<Index, NoneOf>>(obs), ...);
(reg.template on_destroy<AllOf>().template connect<&discard_if<Index>>(obs), ...);
(reg.template on_construct<NoneOf>().template connect<&discard_if<Index>>(obs), ...);
}
static void disconnect(basic_observer &obs, Registry &reg) {
(reg.template on_destroy<Require>().disconnect(&obs), ...);
(reg.template on_construct<Reject>().disconnect(&obs), ...);
(reg.template on_construct<AllOf>().disconnect(&obs), ...);
(reg.template on_destroy<NoneOf>().disconnect(&obs), ...);
(reg.template on_destroy<AllOf>().disconnect(&obs), ...);
(reg.template on_construct<NoneOf>().disconnect(&obs), ...);
}
};
template<typename... Matcher>
static void disconnect(Registry &parent, storage_type &storage) {
(matcher_handler<Matcher>::disconnect(storage, parent), ...);
static void disconnect(Registry &reg, basic_observer &obs) {
(matcher_handler<Matcher>::disconnect(obs, reg), ...);
}
template<typename... Matcher, std::size_t... Index>
static void connect(Registry &parent, storage_type &storage, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<mask_type>::digits, "Too many matchers");
(matcher_handler<Matcher>::template connect<Index>(storage, parent), ...);
void connect(Registry &reg, std::index_sequence<Index...>) {
static_assert(sizeof...(Matcher) < std::numeric_limits<typename base_type::value_type>::digits, "Too many matchers");
(matcher_handler<Matcher>::template connect<Index>(*this, reg), ...);
release.template connect<&basic_observer::disconnect<Matcher...>>(reg);
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! Basic registry type. */
using registry_type = Registry;
/*! @brief Underlying entity identifier. */
using entity_type = typename registry_type::entity_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Random access iterator type. */
using iterator = typename registry_type::common_type::iterator;
@@ -273,9 +282,8 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_observer(const allocator_type &allocator)
: release{},
parent{},
storage{allocator} {}
: base_type{allocator},
release{} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_observer(const basic_observer &) = delete;
@@ -291,15 +299,10 @@ public:
*/
template<typename... Matcher>
basic_observer(registry_type &reg, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{})
: release{&basic_observer::disconnect<Matcher...>},
parent{&reg},
storage{allocator} {
connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
: basic_observer{allocator} {
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
}
/*! @brief Default destructor. */
~basic_observer() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This observer.
@@ -320,18 +323,15 @@ public:
template<typename... Matcher>
void connect(registry_type &reg, basic_collector<Matcher...>) {
disconnect();
storage.clear();
parent = &reg;
release = &basic_observer::disconnect<Matcher...>;
connect<Matcher...>(reg, storage, std::index_sequence_for<Matcher...>{});
connect<Matcher...>(reg, std::index_sequence_for<Matcher...>{});
base_type::clear();
}
/*! @brief Disconnects an observer from the registry it keeps track of. */
void disconnect() {
if(release) {
release(*parent, storage);
release = nullptr;
release(*this);
release.reset();
}
}
@@ -340,7 +340,7 @@ public:
* @return Number of elements.
*/
[[nodiscard]] size_type size() const noexcept {
return storage.size();
return base_type::size();
}
/**
@@ -348,7 +348,7 @@ public:
* @return True if the observer is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return storage.empty();
return base_type::empty();
}
/**
@@ -364,7 +364,7 @@ public:
* @return A pointer to the array of entities.
*/
[[nodiscard]] const entity_type *data() const noexcept {
return storage.data();
return base_type::data();
}
/**
@@ -375,7 +375,7 @@ public:
* @return An iterator to the first entity of the observer.
*/
[[nodiscard]] iterator begin() const noexcept {
return storage.storage_type::base_type::begin();
return base_type::base_type::begin();
}
/**
@@ -384,12 +384,12 @@ public:
* observer.
*/
[[nodiscard]] iterator end() const noexcept {
return storage.storage_type::base_type::end();
return base_type::base_type::end();
}
/*! @brief Clears the underlying container. */
void clear() noexcept {
storage.clear();
base_type::clear();
}
/**
@@ -428,9 +428,7 @@ public:
}
private:
void (*release)(registry_type &, storage_type &);
registry_type *parent;
storage_type storage;
delegate<void(basic_observer &)> release;
};
} // namespace entt

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>
@@ -52,8 +56,8 @@ struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
template<typename... Get, typename... Exclude, typename... Override>
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>;
using ro = type_list_cat_t<type_list<typename Exclude::value_type...>, typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::ro...>;
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::value_type, Get>, type_list<Override...>>::rw...>;
};
template<typename... Get, typename... Exclude, typename... Override>
@@ -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.
@@ -108,7 +116,7 @@ class basic_organizer final {
const char *name{};
const void *payload{};
callback_type *callback{};
dependency_type *dependency{};
dependency_type *dependency;
prepare_type *prepare{};
const type_info *info{};
};
@@ -130,16 +138,14 @@ class basic_organizer final {
}
template<typename... Type>
[[nodiscard]] static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
static std::size_t fill_dependencies(type_list<Type...>, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) {
if constexpr(sizeof...(Type) == 0u) {
return {};
} else {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
const type_info *info[]{&type_id<Type>()...};
const type_info *info[sizeof...(Type)]{&type_id<Type>()...};
const auto length = count < sizeof...(Type) ? count : sizeof...(Type);
for(std::size_t pos{}; pos < length; ++pos) {
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
buffer[pos] = info[pos];
}
@@ -169,14 +175,14 @@ public:
struct vertex {
/**
* @brief Constructs a vertex of the task graph.
* @param vtype True if the vertex is a top-level one, false otherwise.
* @param data The data associated with the vertex.
* @param from List of in-edges of the vertex.
* @param to List of out-edges of the vertex.
* @param edges The indices of the children in the adjacency list.
*/
vertex(vertex_data data, std::vector<std::size_t> from, std::vector<std::size_t> to)
: node{std::move(data)},
in{std::move(from)},
out{std::move(to)} {}
vertex(const bool vtype, vertex_data data, std::vector<std::size_t> edges)
: is_top_level{vtype},
node{std::move(data)},
reachable{std::move(edges)} {}
/**
* @brief Fills a buffer with the type info objects for the writable
@@ -185,7 +191,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
[[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(false, buffer, length);
}
@@ -196,7 +202,7 @@ public:
* @param length The length of the user-supplied buffer.
* @return The number of type info objects written to the buffer.
*/
[[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept {
return node.dependency(true, buffer, length);
}
@@ -204,7 +210,7 @@ public:
* @brief Returns the number of read-only resources of a vertex.
* @return The number of read-only resources of the vertex.
*/
[[nodiscard]] size_type ro_count() const noexcept {
size_type ro_count() const noexcept {
return node.ro_count;
}
@@ -212,7 +218,7 @@ public:
* @brief Returns the number of writable resources of a vertex.
* @return The number of writable resources of the vertex.
*/
[[nodiscard]] size_type rw_count() const noexcept {
size_type rw_count() const noexcept {
return node.rw_count;
}
@@ -220,15 +226,15 @@ public:
* @brief Checks if a vertex is also a top-level one.
* @return True if the vertex is a top-level one, false otherwise.
*/
[[nodiscard]] bool top_level() const noexcept {
return in.empty();
bool top_level() const noexcept {
return is_top_level;
}
/**
* @brief Returns a type info object associated with a vertex.
* @return A properly initialized type info object.
*/
[[nodiscard]] const type_info &info() const noexcept {
const type_info &info() const noexcept {
return *node.info;
}
@@ -236,7 +242,7 @@ public:
* @brief Returns a user defined name associated with a vertex, if any.
* @return The user defined name associated with the vertex, if any.
*/
[[nodiscard]] const char *name() const noexcept {
const char *name() const noexcept {
return node.name;
}
@@ -244,7 +250,7 @@ public:
* @brief Returns the function associated with a vertex.
* @return The function associated with the vertex.
*/
[[nodiscard]] function_type *callback() const noexcept {
function_type *callback() const noexcept {
return node.callback;
}
@@ -252,32 +258,16 @@ public:
* @brief Returns the payload associated with a vertex, if any.
* @return The payload associated with the vertex, if any.
*/
[[nodiscard]] const void *data() const noexcept {
const void *data() const noexcept {
return node.payload;
}
/**
* @brief Returns the list of in-edges of a vertex.
* @return The list of in-edges of a vertex.
*/
[[nodiscard]] const std::vector<std::size_t> &in_edges() const noexcept {
return in;
}
/**
* @brief Returns the list of out-edges of a vertex.
* @return The list of out-edges of a vertex.
*/
[[nodiscard]] const std::vector<std::size_t> &out_edges() const noexcept {
return out;
}
/**
* @brief Returns the list of nodes reachable from a given vertex.
* @return The list of nodes reachable from the vertex.
*/
[[deprecated("use ::out_edges")]] [[nodiscard]] const std::vector<std::size_t> &children() const noexcept {
return out_edges();
const std::vector<std::size_t> &children() const noexcept {
return reachable;
}
/**
@@ -290,9 +280,9 @@ public:
}
private:
bool is_top_level;
vertex_data node;
std::vector<std::size_t> in;
std::vector<std::size_t> out;
std::vector<std::size_t> reachable;
};
/**
@@ -387,24 +377,20 @@ public:
* @brief Generates a task graph for the current content.
* @return The adjacency list of the task graph.
*/
[[nodiscard]] std::vector<vertex> graph() {
std::vector<vertex> graph() {
std::vector<vertex> adjacency_list{};
adjacency_list.reserve(vertices.size());
auto adjacency_matrix = builder.graph();
for(auto curr: adjacency_matrix.vertices()) {
std::vector<std::size_t> in{};
std::vector<std::size_t> out{};
for(auto &&edge: adjacency_matrix.in_edges(curr)) {
in.push_back(edge.first);
}
const auto iterable = adjacency_matrix.in_edges(curr);
std::vector<std::size_t> reachable{};
for(auto &&edge: adjacency_matrix.out_edges(curr)) {
out.push_back(edge.second);
reachable.push_back(edge.second);
}
adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out));
adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable));
}
return adjacency_list;

View File

@@ -1,26 +0,0 @@
#ifndef ENTT_ENTITY_RANGES_HPP
#define ENTT_ENTITY_RANGES_HPP
#if __has_include(<version>)
# include <version>
#
# if defined(__cpp_lib_ranges)
# include <ranges>
# include "fwd.hpp"
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_view<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_borrowed_range<entt::basic_group<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::basic_view<Args...>>{true};
template<class... Args>
inline constexpr bool std::ranges::enable_view<entt::basic_group<Args...>>{true};
# endif
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -11,13 +11,16 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Set>
class runtime_view_iterator final {
using iterator_type = typename Set::iterator;
using iterator_traits = std::iterator_traits<iterator_type>;
[[nodiscard]] bool valid() const {
return (!tombstone_check || *it != tombstone)
@@ -26,10 +29,10 @@ class runtime_view_iterator final {
}
public:
using value_type = typename iterator_traits::value_type;
using pointer = typename iterator_traits::pointer;
using reference = typename iterator_traits::reference;
using difference_type = typename iterator_traits::difference_type;
using difference_type = typename iterator_type::difference_type;
using value_type = typename iterator_type::value_type;
using pointer = typename iterator_type::pointer;
using reference = typename iterator_type::reference;
using iterator_category = std::bidirectional_iterator_tag;
constexpr runtime_view_iterator() noexcept
@@ -38,7 +41,6 @@ public:
it{},
tombstone_check{} {}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
runtime_view_iterator(const std::vector<Set *> &cpools, const std::vector<Set *> &ignore, iterator_type curr) noexcept
: pools{&cpools},
filter{&ignore},
@@ -50,8 +52,7 @@ public:
}
runtime_view_iterator &operator++() {
++it;
for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {}
while(++it != (*pools)[0]->end() && !valid()) {}
return *this;
}
@@ -61,8 +62,7 @@ public:
}
runtime_view_iterator &operator--() {
--it;
for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {}
while(--it != (*pools)[0]->begin() && !valid()) {}
return *this;
}
@@ -95,22 +95,26 @@ private:
};
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Generic runtime view.
*
* Runtime views iterate over those entities that are at least in the given
* storage. During initialization, a runtime view looks at the number of
* entities available for each element and uses the smallest set in order to get
* a performance boost when iterating.
* entities available for each component and uses the smallest set in order to
* get a performance boost when iterating.
*
* @b Important
*
* Iterators aren't invalidated if:
*
* * New elements are added to the storage.
* * The entity currently pointed is modified (for example, elements are added
* * The entity currently pointed is modified (for example, components are added
* or removed from it).
* * The entity currently pointed is destroyed.
*
@@ -163,7 +167,7 @@ public:
filter{other.filter, allocator} {}
/*! @brief Default move constructor. */
basic_runtime_view(basic_runtime_view &&) noexcept = default;
basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v<container_type>) = default;
/**
* @brief Allocator-extended move constructor.
@@ -174,26 +178,23 @@ public:
: pools{std::move(other.pools), allocator},
filter{std::move(other.filter), allocator} {}
/*! @brief Default destructor. */
~basic_runtime_view() = default;
/**
* @brief Default copy assignment operator.
* @return This runtime view.
* @return This container.
*/
basic_runtime_view &operator=(const basic_runtime_view &) = default;
/**
* @brief Default move assignment operator.
* @return This runtime view.
* @return This container.
*/
basic_runtime_view &operator=(basic_runtime_view &&) noexcept = default;
basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v<container_type>) = default;
/**
* @brief Exchanges the contents with those of a given view.
* @param other View to exchange the content with.
*/
void swap(basic_runtime_view &other) noexcept {
void swap(basic_runtime_view &other) {
using std::swap;
swap(pools, other.pools);
swap(filter, other.filter);
@@ -248,11 +249,11 @@ public:
/**
* @brief Returns an iterator to the first entity that has the given
* elements.
* components.
*
* If the view is empty, the returned iterator will be equal to `end()`.
*
* @return An iterator to the first entity that has the given elements.
* @return An iterator to the first entity that has the given components.
*/
[[nodiscard]] iterator begin() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()};
@@ -260,22 +261,14 @@ public:
/**
* @brief Returns an iterator that is past the last entity that has the
* given elements.
* given components.
* @return An iterator to the entity following the last entity that has the
* given elements.
* given components.
*/
[[nodiscard]] iterator end() const {
return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()};
}
/**
* @brief Checks whether a view is initialized or not.
* @return True if the view is initialized, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return !(pools.empty() && filter.empty());
}
/**
* @brief Checks if a view contains an entity.
* @param entt A valid identifier.

View File

@@ -16,28 +16,36 @@
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.
*
* A _snapshot_ can be either a dump of the entire registry or a narrower
* selection of elements of interest.<br/>
* selection of components of interest.<br/>
* This type can be used in both cases if provided with a correctly configured
* output archive.
*
@@ -46,7 +54,7 @@ void orphans(Registry &registry) {
template<typename Registry>
class basic_snapshot {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = entt_traits<typename Registry::entity_type>;
using traits_type = typename Registry::traits_type;
public:
/*! Basic registry type. */
@@ -61,25 +69,10 @@ public:
basic_snapshot(const registry_type &source) noexcept
: reg{&source} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_snapshot(const basic_snapshot &) = delete;
/*! @brief Default move constructor. */
basic_snapshot(basic_snapshot &&) noexcept = default;
/*! @brief Default destructor. */
~basic_snapshot() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This snapshot.
*/
basic_snapshot &operator=(const basic_snapshot &) = delete;
/**
* @brief Default move assignment operator.
* @return This snapshot.
*/
/*! @brief Default move assignment operator. @return This snapshot. */
basic_snapshot &operator=(basic_snapshot &&) noexcept = default;
/**
@@ -93,25 +86,14 @@ public:
template<typename Type, typename Archive>
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
if(const auto *storage = reg->template storage<Type>(id); storage) {
const typename registry_type::common_type &base = *storage;
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 = base.rbegin(), last = base.rend(); first != last; ++first) {
for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) {
archive(*first);
}
} else if constexpr(registry_type::template storage_for_type<Type>::storage_policy == deletion_policy::in_place) {
for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) {
if(const auto entt = *it; entt == tombstone) {
archive(static_cast<entity_type>(null));
} else {
archive(entt);
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
}
}
} else {
for(auto elem: storage->reach()) {
std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem);
@@ -158,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;
};
@@ -175,7 +196,7 @@ private:
template<typename Registry>
class basic_snapshot_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = entt_traits<typename Registry::entity_type>;
using traits_type = typename Registry::traits_type;
public:
/*! Basic registry type. */
@@ -190,28 +211,15 @@ 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>().free_list() == 0u, "Registry must be empty");
for([[maybe_unused]] auto elem: source.storage()) {
ENTT_ASSERT(elem.second.empty(), "Registry must be empty");
}
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_snapshot_loader(const basic_snapshot_loader &) = delete;
/*! @brief Default move constructor. */
basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default;
/*! @brief Default destructor. */
~basic_snapshot_loader() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This loader.
*/
basic_snapshot_loader &operator=(const basic_snapshot_loader &) = delete;
/**
* @brief Default move assignment operator.
* @return This loader.
*/
/*! @brief Default move assignment operator. @return This loader. */
basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default;
/**
@@ -223,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};
@@ -250,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{};
@@ -265,10 +273,37 @@ public:
}
/**
* @brief Destroys those entities that have no elements.
* @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.
*
* In case all the entities were serialized but only part of the elements
* was saved, it could happen that some of the entities have no elements
* 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.
*
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* once restored.<br/>
* This function helps to identify and destroy those entities.
*
@@ -302,7 +337,7 @@ private:
template<typename Registry>
class basic_continuous_loader {
static_assert(!std::is_const_v<Registry>, "Non-const registry type required");
using traits_type = entt_traits<typename Registry::entity_type>;
using traits_type = typename Registry::traits_type;
void restore(typename Registry::entity_type entt) {
if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) {
@@ -373,26 +408,11 @@ public:
: remloc{source.get_allocator()},
reg{&source} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_continuous_loader(const basic_continuous_loader &) = delete;
/*! @brief Default move constructor. */
basic_continuous_loader(basic_continuous_loader &&) noexcept = default;
basic_continuous_loader(basic_continuous_loader &&) = default;
/*! @brief Default destructor. */
~basic_continuous_loader() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This loader.
*/
basic_continuous_loader &operator=(const basic_continuous_loader &) = delete;
/**
* @brief Default move assignment operator.
* @return This loader.
*/
basic_continuous_loader &operator=(basic_continuous_loader &&) noexcept = default;
/*! @brief Default move assignment operator. @return This loader. */
basic_continuous_loader &operator=(basic_continuous_loader &&) = default;
/**
* @brief Restores all elements of a type with associated identifiers.
@@ -409,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};
@@ -447,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{};
@@ -462,10 +482,83 @@ public:
}
/**
* @brief Destroys those entities that have no elements.
* @brief Restores all identifiers, including those to be recycled.
*
* In case all the entities were serialized but only part of the elements
* was saved, it could happen that some of the entities have no elements
* 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.
*
* In case all the entities were serialized but only part of the components
* was saved, it could happen that some of the entities have no components
* once restored.<br/>
* This function helps to identify and destroy those entities.
*

View File

@@ -10,14 +10,18 @@
#include "../config/config.h"
#include "../core/algorithm.hpp"
#include "../core/any.hpp"
#include "../core/bit.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
#include "entity.hpp"
#include "fwd.hpp"
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Container>
@@ -33,7 +37,7 @@ struct sparse_set_iterator final {
offset{} {}
constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept
: packed{&ref},
: packed{std::addressof(ref)},
offset{idx} {}
constexpr sparse_set_iterator &operator++() noexcept {
@@ -73,15 +77,15 @@ struct sparse_set_iterator final {
}
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
return (*packed)[index() - value];
return packed->data()[index() - value];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(operator[](0));
return packed->data() + index();
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
return *operator->();
}
[[nodiscard]] constexpr pointer data() const noexcept {
@@ -133,10 +137,14 @@ template<typename Container>
}
} // namespace internal
/*! @endcond */
/**
* @brief Sparse set implementation.
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Basic sparse set implementation.
*
* Sparse set or packed array or whatever is the name users give it.<br/>
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a
@@ -160,13 +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 traits_type = entt_traits<Entity>;
static constexpr auto max_size = static_cast<std::size_t>(traits_type::to_entity(null));
[[nodiscard]] auto policy_to_head() const noexcept {
return static_cast<size_type>(max_size * static_cast<decltype(max_size)>(mode != deletion_policy::swap_only));
}
[[nodiscard]] auto sparse_ptr(const Entity entt) const {
const auto pos = static_cast<size_type>(traits_type::to_entity(entt));
@@ -193,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() {
@@ -214,38 +216,31 @@ class basic_sparse_set {
}
}
void swap_at(const std::size_t lhs, const std::size_t rhs) {
auto &from = packed[lhs];
auto &to = packed[rhs];
sparse_ref(from) = traits_type::combine(static_cast<typename traits_type::entity_type>(rhs), traits_type::to_integral(from));
sparse_ref(to) = traits_type::combine(static_cast<typename traits_type::entity_type>(lhs), traits_type::to_integral(to));
std::swap(from, to);
}
private:
[[nodiscard]] virtual const void *get_at(const std::size_t) const {
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 < head) == (rhs < head)), "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 = index(*it);
bump(traits_type::next(*it));
swap_at(pos, 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]);
}
/**
@@ -253,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()));
@@ -270,58 +265,43 @@ 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");
const auto pos = static_cast<size_type>(traits_type::to_entity(std::exchange(sparse_ref(*it), null)));
packed[pos] = traits_type::combine(static_cast<typename traits_type::entity_type>(std::exchange(head, pos)), tombstone);
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)] = std::exchange(free_list, traits_type::combine(entt, tombstone));
}
protected:
/**
* @brief Erases entities from a sparse set.
* @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.
*/
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 != max_size) {
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();
}
@@ -332,55 +312,31 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element");
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 != max_size && !force_back) {
pos = head;
ENTT_ASSERT(elem == null, "Slot not available");
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(head), traits_type::to_integral(entt));
head = static_cast<size_type>(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(!(static_cast<size_type>(traits_type::to_entity(elem)) < head), "Slot not available");
bump(entt);
}
pos = 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() - static_cast<typename iterator::difference_type>(pos));
}
/*! @brief Forwards variables to derived classes, if any. */
// NOLINTNEXTLINE(performance-unnecessary-value-param)
virtual void bind_any(any) noexcept {}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Entity traits. */
using traits_type = entt_traits<Entity>;
/*! @brief Underlying entity identifier. */
using entity_type = typename traits_type::value_type;
/*! @brief Underlying version type. */
using version_type = typename traits_type::version_type;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type to contained entities. */
using pointer = typename packed_container_type::const_pointer;
/*! @brief Random access iterator type. */
@@ -401,7 +357,7 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_sparse_set(const allocator_type &allocator)
: basic_sparse_set{deletion_policy::swap_and_pop, allocator} {}
: basic_sparse_set{type_id<void>(), deletion_policy::swap_and_pop, allocator} {}
/**
* @brief Constructs an empty container with the given policy and allocator.
@@ -422,13 +378,8 @@ public:
: sparse{allocator},
packed{allocator},
info{&elem},
mode{pol},
head{policy_to_head()} {
ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions");
}
/*! @brief Default copy constructor, deleted on purpose. */
basic_sparse_set(const basic_sparse_set &) = delete;
free_list{tombstone},
mode{pol} {}
/**
* @brief Move constructor.
@@ -438,21 +389,21 @@ 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.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator)
basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept
: 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())} {
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
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");
}
/*! @brief Default destructor. */
@@ -460,20 +411,20 @@ public:
release_sparse_pages();
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This sparse set.
*/
basic_sparse_set &operator=(const basic_sparse_set &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This sparse set.
*/
basic_sparse_set &operator=(basic_sparse_set &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed");
swap(other);
ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed");
release_sparse_pages();
sparse = std::move(other.sparse);
packed = std::move(other.packed);
info = other.info;
free_list = std::exchange(other.free_list, tombstone);
mode = other.mode;
return *this;
}
@@ -481,13 +432,13 @@ public:
* @brief Exchanges the contents with those of a given sparse set.
* @param other Sparse set to exchange the content with.
*/
void swap(basic_sparse_set &other) noexcept {
void swap(basic_sparse_set &other) {
using std::swap;
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);
}
/**
@@ -506,23 +457,6 @@ public:
return mode;
}
/**
* @brief Returns data on the free list whose meaning depends on the mode.
* @return Free list information that is mode dependent.
*/
[[nodiscard]] size_type free_list() const noexcept {
return head;
}
/**
* @brief Sets data on the free list whose meaning depends on the mode.
* @param value Free list information that is mode dependent.
*/
void free_list(const size_type value) noexcept {
ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value");
head = value;
}
/**
* @brief Increases the capacity of a sparse set.
*
@@ -590,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 == max_size);
return (free_list == null);
}
/**
@@ -609,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};
}
@@ -642,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());
}
@@ -671,7 +605,7 @@ public:
* @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();
}
@@ -681,11 +615,10 @@ public:
* @return True if the sparse set contains the entity, false otherwise.
*/
[[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;
const auto elem = sparse_ptr(entt);
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);
}
/**
@@ -695,7 +628,7 @@ public:
* version otherwise.
*/
[[nodiscard]] version_type current(const entity_type entt) const noexcept {
const auto *elem = sparse_ptr(entt);
const auto elem = sparse_ptr(entt);
constexpr auto fallback = traits_type::to_version(tombstone);
return elem ? traits_type::to_version(*elem) : fallback;
}
@@ -716,12 +649,21 @@ public:
}
/**
* @brief Returns the entity at specified location.
* @brief Returns the entity at specified location, with bounds checking.
* @param pos The position for which to return the entity.
* @return The entity at specified location if any, a null entity otherwise.
*/
[[nodiscard]] entity_type at(const size_type pos) const noexcept {
return pos < packed.size() ? packed[pos] : null;
}
/**
* @brief Returns the entity at specified location, without bounds checking.
* @param pos The position for which to return the entity.
* @return The entity at specified location.
*/
[[nodiscard]] entity_type operator[](const size_type pos) const noexcept {
ENTT_ASSERT(pos < packed.size(), "Index out of bounds");
ENTT_ASSERT(pos < packed.size(), "Position is out of bounds");
return packed[pos];
}
@@ -775,13 +717,11 @@ public:
*/
template<typename It>
iterator push(It first, It last) {
auto curr = end();
for(; first != last; ++first) {
curr = try_emplace(*first, true);
for(auto it = first; it != last; ++it) {
try_emplace(*it, true);
}
return curr;
return first == last ? end() : find(*first);
}
/**
@@ -795,10 +735,10 @@ public:
* @return The version of the given identifier.
*/
version_type bump(const entity_type entt) {
auto &elem = sparse_ref(entt);
ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version");
elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt));
packed[static_cast<size_type>(traits_type::to_entity(elem))] = entt;
auto &entity = sparse_ref(entt);
ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version");
entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt));
packed[static_cast<size_type>(traits_type::to_entity(entity))] = entt;
return traits_type::to_version(entt);
}
@@ -882,27 +822,25 @@ public:
/*! @brief Removes all tombstones from a sparse set. */
void compact() {
if(mode == deletion_policy::in_place) {
size_type from = packed.size();
size_type pos = std::exchange(head, max_size);
size_type from = packed.size();
for(; from && packed[from - 1u] == tombstone; --from) {}
for(; from && packed[from - 1u] == tombstone; --from) {}
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);
while(pos != max_size) {
if(const auto to = std::exchange(pos, static_cast<size_type>(traits_type::to_entity(packed[pos]))); to < from) {
--from;
swap_or_move(from, 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]));
packed[to] = packed[from];
const auto elem = static_cast<typename traits_type::entity_type>(to);
sparse_ref(packed[to]) = traits_type::combine(elem, 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);
}
/**
@@ -959,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 == max_size), "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)...);
@@ -973,8 +911,8 @@ public:
const auto entt = packed[curr];
swap_or_move(next, idx);
const auto elem = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr]));
const auto entity = static_cast<typename traits_type::entity_type>(curr);
sparse_ref(entt) = traits_type::combine(entity, traits_type::to_integral(packed[curr]));
curr = std::exchange(next, idx);
}
}
@@ -994,31 +932,28 @@ public:
*/
template<typename Compare, typename Sort = std_sort, typename... Args>
void sort(Compare compare, Sort algo = Sort{}, Args &&...args) {
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
sort_n(len, 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.
* @return An iterator past the last of the elements actually shared.
* @param other The sparse sets that imposes the order of the entities.
*/
template<typename It>
iterator sort_as(It first, It last) {
ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed");
const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size();
auto it = end() - static_cast<typename iterator::difference_type>(len);
void sort_as(const basic_sparse_set &other) {
compact();
for(const auto other = end(); (it != other) && (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);
@@ -1027,8 +962,6 @@ public:
++it;
}
}
return it;
}
/*! @brief Clears a sparse set. */
@@ -1036,7 +969,7 @@ public:
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();
}
@@ -1044,41 +977,19 @@ public:
* @brief Returned value type, if any.
* @return Returned value type, if any.
*/
[[nodiscard]] const type_info &type() const noexcept {
const type_info &type() const noexcept {
return *info;
}
/**
* @brief Forwards variables to derived classes, if any.
* @tparam Type Type of the element to forward.
* @param value The element to forward.
* @return Nothing.
*/
template<typename Type>
[[deprecated("avoid wrapping elements with basic_any")]] std::enable_if_t<std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
bind(Type &&value) noexcept {
// backward compatibility
bind_any(std::forward<Type>(value));
}
/**
* @brief Forwards variables to derived classes, if any.
* @tparam Type Type of the element to forward.
* @param value The element to forward.
* @return Nothing.
*/
template<typename Type>
std::enable_if_t<!std::is_same_v<std::remove_const_t<std::remove_reference_t<Type>>, basic_any<>>>
bind(Type &&value) noexcept {
bind_any(forward_as_any(std::forward<Type>(value)));
}
/*! @brief Forwards variables to derived classes, if any. */
virtual void bind(any) noexcept {}
private:
sparse_container_type sparse;
packed_container_type packed;
const type_info *info;
entity_type free_list;
deletion_policy mode;
size_type head;
};
} // namespace entt

View File

@@ -9,7 +9,6 @@
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/iterator.hpp"
#include "../core/memory.hpp"
#include "../core/type_info.hpp"
@@ -20,12 +19,16 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
template<typename Container>
template<typename Container, std::size_t Size>
class storage_iterator final {
friend storage_iterator<const Container>;
friend storage_iterator<const Container, Size>;
using container_type = std::remove_const_t<Container>;
using alloc_traits = std::allocator_traits<typename container_type::allocator_type>;
@@ -49,7 +52,7 @@ public:
offset{idx} {}
template<bool Const = std::is_const_v<Container>, typename = std::enable_if_t<Const>>
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>> &other) noexcept
constexpr storage_iterator(const storage_iterator<std::remove_const_t<Container>, Size> &other) noexcept
: storage_iterator{other.payload, other.offset} {}
constexpr storage_iterator &operator++() noexcept {
@@ -90,16 +93,16 @@ public:
[[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept {
const auto pos = index() - value;
constexpr auto page_size = component_traits<value_type>::page_size;
return (*payload)[pos / page_size][fast_mod(static_cast<std::size_t>(pos), page_size)];
return (*payload)[pos / Size][fast_mod(pos, Size)];
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return std::addressof(operator[](0));
const auto pos = index();
return (*payload)[pos / Size] + fast_mod(pos, Size);
}
[[nodiscard]] constexpr reference operator*() const noexcept {
return operator[](0);
return *operator->();
}
[[nodiscard]] constexpr difference_type index() const noexcept {
@@ -111,38 +114,38 @@ private:
difference_type offset;
};
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return rhs.index() - lhs.index();
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator==(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return lhs.index() == rhs.index();
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator!=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs == rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return lhs.index() > rhs.index();
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return rhs < lhs;
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator<=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs > rhs);
}
template<typename Lhs, typename Rhs>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs> &lhs, const storage_iterator<Rhs> &rhs) noexcept {
template<typename Lhs, typename Rhs, std::size_t Size>
[[nodiscard]] constexpr bool operator>=(const storage_iterator<Lhs, Size> &lhs, const storage_iterator<Rhs, Size> &rhs) noexcept {
return !(lhs < rhs);
}
@@ -153,17 +156,16 @@ class extended_storage_iterator final {
public:
using iterator_type = It;
using difference_type = std::ptrdiff_t;
using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval<It>()), std::forward_as_tuple(*std::declval<Other>()...)));
using pointer = input_iterator_pointer<value_type>;
using reference = value_type;
using difference_type = std::ptrdiff_t;
using iterator_category = std::input_iterator_tag;
using iterator_concept = std::forward_iterator_tag;
constexpr 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> && ...)>>
@@ -209,10 +211,14 @@ template<typename... Lhs, typename... Rhs>
}
} // namespace internal
/*! @endcond */
/**
* @brief Storage implementation.
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Basic storage implementation.
*
* Internal data structures arrange elements to maximize performance. There are
* no guarantees that objects are returned in the insertion order when iterate
@@ -222,7 +228,7 @@ template<typename... Lhs, typename... Rhs>
* Empty types aren't explicitly instantiated. Therefore, many of the functions
* normally available for non-empty types will not be available for empty ones.
*
* @tparam Type Element type.
* @tparam Type Type of objects assigned to the entities.
* @tparam Entity A valid entity type.
* @tparam Allocator Type of allocator used to manage memory and elements.
*/
@@ -233,7 +239,8 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>;
using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
using underlying_iterator = typename underlying_type::basic_iterator;
using traits_type = component_traits<Type>;
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)];
@@ -266,8 +273,8 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
const auto it = base_type::try_emplace(entt, force_back);
ENTT_TRY {
auto *elem = to_address(assure_at_least(static_cast<size_type>(it.index())));
entt::uninitialized_construct_using_allocator(elem, get_allocator(), std::forward<Args>(args)...);
auto elem = assure_at_least(static_cast<size_type>(it.index()));
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
}
ENTT_CATCH {
base_type::pop(it, it + 1u);
@@ -283,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 {
@@ -299,12 +306,11 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
}
private:
[[nodiscard]] const void *get_at(const std::size_t pos) const final {
const void *get_at(const std::size_t pos) const final {
return std::addressof(element_at(pos));
}
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");
@@ -374,14 +380,14 @@ protected:
* @return Iterator pointing to the emplaced element.
*/
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
if(value != nullptr) {
if constexpr(std::is_copy_constructible_v<element_type>) {
return emplace_element(entt, force_back, *static_cast<const element_type *>(value));
if(value) {
if constexpr(std::is_copy_constructible_v<value_type>) {
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
} else {
return base_type::end();
}
} else {
if constexpr(std::is_default_constructible_v<element_type>) {
if constexpr(std::is_default_constructible_v<value_type>) {
return emplace_element(entt, force_back);
} else {
return base_type::end();
@@ -390,26 +396,26 @@ protected:
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = underlying_type;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Type of the objects assigned to entities. */
using value_type = element_type;
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Pointer type to contained elements. */
using pointer = typename container_type::pointer;
/*! @brief Constant pointer type to contained elements. */
using const_pointer = typename alloc_traits::template rebind_traits<typename alloc_traits::const_pointer>::const_pointer;
/*! @brief Random access iterator type. */
using iterator = internal::storage_iterator<container_type>;
using iterator = internal::storage_iterator<container_type, traits_type::page_size>;
/*! @brief Constant random access iterator type. */
using const_iterator = internal::storage_iterator<const container_type>;
using const_iterator = internal::storage_iterator<const container_type, traits_type::page_size>;
/*! @brief Reverse iterator type. */
using reverse_iterator = std::reverse_iterator<iterator>;
/*! @brief Constant reverse iterator type. */
@@ -422,8 +428,6 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator, reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator, const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
/*! @brief Default constructor. */
basic_storage()
@@ -434,12 +438,9 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<element_type>(), storage_policy, allocator},
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator},
payload{allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
/**
* @brief Move constructor.
* @param other The instance to move from.
@@ -453,33 +454,28 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator)
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator},
payload{std::move(other.payload), allocator} {
// NOLINTNEXTLINE(bugprone-use-after-move)
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
}
/*! @brief Default destructor. */
// NOLINTNEXTLINE(bugprone-exception-escape)
~basic_storage() override {
shrink_to_size(0u);
}
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed");
swap(other);
ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed");
shrink_to_size(0u);
base_type::operator=(std::move(other));
payload = std::move(other.payload);
return *this;
}
@@ -487,10 +483,10 @@ public:
* @brief Exchanges the contents with those of a given storage.
* @param other Storage to exchange the content with.
*/
void swap(basic_storage &other) noexcept {
void swap(basic_storage &other) {
using std::swap;
swap(payload, other.payload);
base_type::swap(other);
swap(payload, other.payload);
}
/**
@@ -709,7 +705,7 @@ public:
* @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 value An instance of the object to construct.
* @return Iterator pointing to the first element inserted, if any.
* @return Iterator pointing to the last element inserted, if any.
*/
template<typename It>
iterator insert(It first, It last, const value_type &value = {}) {
@@ -746,17 +742,17 @@ public:
* @brief Returns an iterable object to use to _visit_ a storage.
*
* The iterable object returns a tuple that contains the current entity and
* a reference to its element.
* a reference to its component.
*
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}};
return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}};
return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}};
}
/**
@@ -767,12 +763,12 @@ public:
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return reverse_iterable{{base_type::rbegin(), rbegin()}, {base_type::rend(), rend()}};
return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}};
return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}};
}
private:
@@ -785,21 +781,20 @@ class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<T
: public basic_sparse_set<Entity, typename std::allocator_traits<Allocator>::template rebind_alloc<Entity>> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
using traits_type = component_traits<Type>;
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>;
/*! @brief Element type. */
using element_type = Type;
/*! @brief Type of the objects assigned to entities. */
using value_type = void;
using value_type = Type;
/*! @brief Component traits. */
using traits_type = component_traits<value_type>;
/*! @brief Underlying entity identifier. */
using entity_type = Entity;
/*! @brief Unsigned integer type. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
@@ -808,8 +803,6 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy{traits_type::in_place_delete};
/*! @brief Default constructor. */
basic_storage()
@@ -820,10 +813,7 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<element_type>(), storage_policy, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
: base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {}
/**
* @brief Move constructor.
@@ -836,18 +826,9 @@ public:
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator)
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator} {}
/*! @brief Default destructor. */
~basic_storage() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
/**
* @brief Move assignment operator.
* @param other The instance to move from.
@@ -860,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<element_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()};
}
/**
@@ -878,16 +854,21 @@ public:
* @param entt A valid identifier.
*/
void get([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
}
/**
* @brief Returns an empty tuple.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
* @return Returns an empty tuple.
*/
[[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept {
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
return std::tuple{};
}
@@ -914,7 +895,7 @@ public:
*/
template<typename... Func>
void patch([[maybe_unused]] const entity_type entt, Func &&...func) {
ENTT_ASSERT(base_type::contains(entt), "Invalid entity");
ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity");
(std::forward<Func>(func)(), ...);
}
@@ -940,12 +921,12 @@ public:
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return iterable{base_type::begin(), base_type::end()};
return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
return const_iterable{base_type::cbegin(), base_type::cend()};
return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}};
}
/**
@@ -956,12 +937,12 @@ public:
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return reverse_iterable{{base_type::rbegin()}, {base_type::rend()}};
return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}};
}
/*! @copydoc reach */
[[nodiscard]] const_reverse_iterable reach() const noexcept {
return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}};
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}};
}
};
@@ -975,25 +956,42 @@ class basic_storage<Entity, Entity, Allocator>
: public basic_sparse_set<Entity, Allocator> {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, Entity>, "Invalid value type");
using underlying_iterator = typename basic_sparse_set<Entity, Allocator>::basic_iterator;
using traits_type = entt_traits<Entity>;
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 next() noexcept {
entity_type entt = null;
auto entity_at(const std::size_t pos) const noexcept {
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), {});
}
do {
ENTT_ASSERT(placeholder < traits_type::to_entity(null), "No more entities available");
entt = traits_type::combine(static_cast<typename traits_type::entity_type>(placeholder++), {});
} while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null);
return entt;
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 all entities of a storage. */
/**
* @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();
placeholder = {};
}
/**
@@ -1006,18 +1004,18 @@ protected:
}
public:
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Base type. */
using base_type = basic_sparse_set<Entity, Allocator>;
/*! @brief Element type. */
using element_type = Entity;
/*! @brief Type of the objects assigned to entities. */
using value_type = void;
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. */
using size_type = std::size_t;
/*! @brief Allocator type. */
using allocator_type = Allocator;
/*! @brief Extended iterable storage proxy. */
using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator>>;
/*! @brief Constant extended iterable storage proxy. */
@@ -1026,8 +1024,6 @@ public:
using reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::reverse_iterator>>;
/*! @brief Constant extended reverse iterable storage proxy. */
using const_reverse_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_reverse_iterator>>;
/*! @brief Storage deletion policy. */
static constexpr deletion_policy storage_policy = deletion_policy::swap_only;
/*! @brief Default constructor. */
basic_storage()
@@ -1039,10 +1035,8 @@ public:
* @param allocator The allocator to use.
*/
explicit basic_storage(const allocator_type &allocator)
: base_type{type_id<void>(), storage_policy, allocator} {}
/*! @brief Default copy constructor, deleted on purpose. */
basic_storage(const basic_storage &) = delete;
: base_type{type_id<value_type>(), deletion_policy::swap_and_pop, allocator},
length{} {}
/**
* @brief Move constructor.
@@ -1050,25 +1044,16 @@ public:
*/
basic_storage(basic_storage &&other) noexcept
: base_type{std::move(other)},
placeholder{other.placeholder} {}
length{std::exchange(other.length, size_type{})} {}
/**
* @brief Allocator-extended move constructor.
* @param other The instance to move from.
* @param allocator The allocator to use.
*/
basic_storage(basic_storage &&other, const allocator_type &allocator)
basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept
: base_type{std::move(other), allocator},
placeholder{other.placeholder} {}
/*! @brief Default destructor. */
~basic_storage() override = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This storage.
*/
basic_storage &operator=(const basic_storage &) = delete;
length{std::exchange(other.length, size_type{})} {}
/**
* @brief Move assignment operator.
@@ -1076,8 +1061,8 @@ public:
* @return This storage.
*/
basic_storage &operator=(basic_storage &&other) noexcept {
placeholder = other.placeholder;
base_type::operator=(std::move(other));
length = std::exchange(other.length, size_type{});
return *this;
}
@@ -1091,27 +1076,44 @@ 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");
}
/**
* @brief Returns an empty tuple.
*
* @warning
* Attempting to use an entity that doesn't belong to the storage results in
* undefined behavior.
*
* @param entt A valid identifier.
* @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()) ? next() : 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++);
}
/**
@@ -1124,13 +1126,25 @@ public:
* @return A valid identifier.
*/
entity_type emplace(const entity_type hint) {
if(hint != null && hint != tombstone) {
if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) {
return *base_type::try_emplace(hint, true);
if(hint == null || hint == tombstone) {
return emplace();
} 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()), true);
}
base_type::swap_at(pos, length++);
} else if(const auto idx = base_type::index(curr); idx < length) {
return emplace();
} else {
base_type::swap_at(idx, length++);
}
return emplace();
base_type::bump(hint);
return hint;
}
/**
@@ -1141,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)(), ...);
}
@@ -1153,15 +1167,52 @@ 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(next(), true);
*first = *base_type::try_emplace(entity_at(length++), true);
}
}
/**
* @brief Makes all elements in a range contiguous.
* @tparam It Type of forward iterator.
* @param first An iterator to the first element of the range to pack.
* @param last An iterator past the last element of the range to pack.
* @return The number of elements within the newly created range.
*/
template<typename It>
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.
*/
[[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.
*/
void in_use(const size_type len) noexcept {
ENTT_ASSERT(!(len > base_type::size()), "Invalid length");
length = len;
}
/**
* @brief Returns an iterable object to use to _visit_ a storage.
*
@@ -1170,13 +1221,12 @@ public:
* @return An iterable object to use to _visit_ the storage.
*/
[[nodiscard]] iterable each() noexcept {
return std::as_const(*this).each();
return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}};
}
/*! @copydoc each */
[[nodiscard]] const_iterable each() const noexcept {
const auto it = base_type::cend();
return const_iterable{it - base_type::free_list(), it};
return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}};
}
/**
@@ -1187,17 +1237,16 @@ public:
* @return A reverse iterable object to use to _visit_ the storage.
*/
[[nodiscard]] reverse_iterable reach() noexcept {
return std::as_const(*this).reach();
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 {
const auto it = base_type::crbegin();
return const_reverse_iterable{it, it + base_type::free_list()};
return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crbegin() + length}};
}
private:
size_type placeholder{};
size_type length;
};
} // namespace entt

File diff suppressed because it is too large Load Diff

View File

@@ -4,11 +4,9 @@
#include "config/version.h"
#include "container/dense_map.hpp"
#include "container/dense_set.hpp"
#include "container/table.hpp"
#include "core/algorithm.hpp"
#include "core/any.hpp"
#include "core/attribute.h"
#include "core/bit.hpp"
#include "core/compressed_pair.hpp"
#include "core/enum.hpp"
#include "core/family.hpp"
@@ -17,7 +15,6 @@
#include "core/iterator.hpp"
#include "core/memory.hpp"
#include "core/monostate.hpp"
#include "core/ranges.hpp"
#include "core/tuple.hpp"
#include "core/type_info.hpp"
#include "core/type_traits.hpp"
@@ -30,7 +27,6 @@
#include "entity/mixin.hpp"
#include "entity/observer.hpp"
#include "entity/organizer.hpp"
#include "entity/ranges.hpp"
#include "entity/registry.hpp"
#include "entity/runtime_view.hpp"
#include "entity/snapshot.hpp"
@@ -54,6 +50,7 @@
#include "meta/template.hpp"
#include "meta/type_traits.hpp"
#include "meta/utility.hpp"
#include "platform/android-ndk-r17.hpp"
#include "poly/poly.hpp"
#include "process/process.hpp"
#include "process/scheduler.hpp"

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,11 +30,14 @@ 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 = default;
constexpr edge_iterator() noexcept
: it{},
vert{},
pos{},
last{},
offset{} {}
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept
: it{std::move(base)},
vert{vertices},
@@ -62,10 +69,10 @@ public:
friend constexpr bool operator==(const edge_iterator<Type> &, const edge_iterator<Type> &) noexcept;
private:
It it{};
size_type vert{};
size_type pos{};
size_type last{};
It it;
size_type vert;
size_type pos;
size_type last;
size_type offset{};
};
@@ -80,7 +87,11 @@ template<typename Container>
}
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Basic implementation of a directed adjacency matrix.
@@ -107,17 +118,16 @@ public:
using vertex_iterator = iota_iterator<vertex_type>;
/*! @brief Edge iterator type. */
using edge_iterator = internal::edge_iterator<typename container_type::const_iterator>;
/*! @brief Out-edge iterator type. */
/*! @brief Out edge iterator type. */
using out_edge_iterator = edge_iterator;
/*! @brief In-edge iterator type. */
/*! @brief In edge iterator type. */
using in_edge_iterator = edge_iterator;
/*! @brief Graph category tag. */
using graph_category = Category;
/*! @brief Default constructor. */
adjacency_matrix() noexcept(noexcept(allocator_type{}))
: adjacency_matrix{0u} {
}
: adjacency_matrix{0u} {}
/**
* @brief Constructs an empty container with a given allocator.
@@ -136,8 +146,12 @@ public:
: matrix{vertices * vertices, allocator},
vert{vertices} {}
/*! @brief Default copy constructor. */
adjacency_matrix(const adjacency_matrix &) = default;
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
adjacency_matrix(const adjacency_matrix &other)
: adjacency_matrix{other, other.get_allocator()} {}
/**
* @brief Allocator-extended copy constructor.
@@ -148,8 +162,12 @@ public:
: matrix{other.matrix, allocator},
vert{other.vert} {}
/*! @brief Default move constructor. */
adjacency_matrix(adjacency_matrix &&) noexcept = default;
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
adjacency_matrix(adjacency_matrix &&other) noexcept
: adjacency_matrix{std::move(other), other.get_allocator()} {}
/**
* @brief Allocator-extended move constructor.
@@ -158,22 +176,29 @@ public:
*/
adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator)
: matrix{std::move(other.matrix), allocator},
vert{other.vert} {}
/*! @brief Default destructor. */
~adjacency_matrix() = default;
vert{std::exchange(other.vert, 0u)} {}
/**
* @brief Default copy assignment operator.
* @param other The instance to copy from.
* @return This container.
*/
adjacency_matrix &operator=(const adjacency_matrix &) = default;
adjacency_matrix &operator=(const adjacency_matrix &other) {
matrix = other.matrix;
vert = other.vert;
return *this;
}
/**
* @brief Default move assignment operator.
* @param other The instance to move from.
* @return This container.
*/
adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default;
adjacency_matrix &operator=(adjacency_matrix &&other) noexcept {
matrix = std::move(other.matrix);
vert = std::exchange(other.vert, 0u);
return *this;
}
/**
* @brief Returns the associated allocator.
@@ -193,25 +218,12 @@ public:
* @brief Exchanges the contents with those of a given adjacency matrix.
* @param other Adjacency matrix to exchange the content with.
*/
void swap(adjacency_matrix &other) noexcept {
void swap(adjacency_matrix &other) {
using std::swap;
swap(matrix, other.matrix);
swap(vert, other.vert);
}
/**
* @brief Returns true if an adjacency matrix is empty, false otherwise.
*
* @warning
* Potentially expensive, try to avoid it on hot paths.
*
* @return True if the adjacency matrix is empty, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
const auto iterable = edges();
return (iterable.begin() == iterable.end());
}
/**
* @brief Returns the number of vertices.
* @return The number of vertices.
@@ -239,9 +251,9 @@ public:
}
/**
* @brief Returns an iterable object to visit all out-edges of a vertex.
* @param vertex The vertex of which to return all out-edges.
* @return An iterable object to visit all out-edges of a vertex.
* @brief Returns an iterable object to visit all out edges of a vertex.
* @param vertex The vertex of which to return all out edges.
* @return An iterable object to visit all out edges of a vertex.
*/
[[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();
@@ -251,9 +263,9 @@ public:
}
/**
* @brief Returns an iterable object to visit all in-edges of a vertex.
* @param vertex The vertex of which to return all in-edges.
* @return An iterable object to visit all in-edges of a vertex.
* @brief Returns an iterable object to visit all in edges of a vertex.
* @param vertex The vertex of which to return all in edges.
* @return An iterable object to visit all in edges of a vertex.
*/
[[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept {
const auto it = matrix.cbegin();

View File

@@ -29,9 +29,9 @@ template<typename Allocator>
class basic_flow {
using alloc_traits = std::allocator_traits<Allocator>;
static_assert(std::is_same_v<typename alloc_traits::value_type, id_type>, "Invalid value type");
using task_container_type = dense_set<id_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<id_type>>;
using task_container_type = dense_set<id_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<id_type>>;
using ro_rw_container_type = std::vector<std::pair<std::size_t, bool>, typename alloc_traits::template rebind_alloc<std::pair<std::size_t, bool>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using deps_container_type = dense_map<id_type, ro_rw_container_type, identity, std::equal_to<id_type>, typename alloc_traits::template rebind_alloc<std::pair<const id_type, ro_rw_container_type>>>;
using adjacency_matrix_type = adjacency_matrix<directed_tag, typename alloc_traits::template rebind_alloc<std::size_t>>;
void emplace(const id_type res, const bool is_rw) {
@@ -134,8 +134,9 @@ public:
*/
explicit basic_flow(const allocator_type &allocator)
: index{0u, allocator},
vertices{allocator},
deps{allocator} {}
vertices{},
deps{},
sync_on{} {}
/*! @brief Default copy constructor. */
basic_flow(const basic_flow &) = default;
@@ -165,9 +166,6 @@ public:
deps{std::move(other.deps), allocator},
sync_on{other.sync_on} {}
/*! @brief Default destructor. */
~basic_flow() = default;
/**
* @brief Default copy assignment operator.
* @return This flow builder.
@@ -209,7 +207,7 @@ public:
* @brief Exchanges the contents with those of a given flow builder.
* @param other Flow builder to exchange the content with.
*/
void swap(basic_flow &other) noexcept {
void swap(basic_flow &other) {
using std::swap;
std::swap(index, other.index);
std::swap(vertices, other.vertices);
@@ -217,14 +215,6 @@ public:
std::swap(sync_on, other.sync_on);
}
/**
* @brief Returns true if a flow builder contains no tasks, false otherwise.
* @return True if the flow builder contains no tasks, false otherwise.
*/
[[nodiscard]] bool empty() const noexcept {
return vertices.empty();
}
/**
* @brief Returns the number of tasks.
* @return The number of tasks.
@@ -343,7 +333,7 @@ private:
compressed_pair<size_type, allocator_type> index;
task_container_type vertices;
deps_container_type deps;
size_type sync_on{};
size_type sync_on;
};
} // namespace entt

View File

@@ -38,19 +38,9 @@ public:
/*! @brief Default constructor, deleted on purpose. */
locator() = delete;
/*! @brief Default copy constructor, deleted on purpose. */
locator(const locator &) = delete;
/*! @brief Default destructor, deleted on purpose. */
~locator() = delete;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This locator.
*/
locator &operator=(const locator &) = delete;
/**
* @brief Checks whether a service locator contains a value.
* @return True if the service locator contains a value, false otherwise.
@@ -149,7 +139,6 @@ public:
private:
// std::shared_ptr because of its type erased allocator which is useful here
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
inline static std::shared_ptr<Service> service{};
};

View File

@@ -1,5 +1,3 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_CONTAINER_HPP
#define ENTT_META_CONTAINER_HPP
@@ -21,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 Unsigned integer type. */
using size_type = typename meta_sequence_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_sequence_container::iterator;
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool fixed_size = internal::fixed_size_sequence_container_v<Type>;
/**
* @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 != nullptr) ? 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 != nullptr) ? 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([[maybe_unused]] 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{};
} 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 != nullptr) ? *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([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) {
if constexpr(fixed_size) {
return iterator{};
} 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 Unsigned integer type. */
using size_type = typename meta_associative_container::size_type;
/*! @brief Meta iterator type. */
using iterator = typename meta_associative_container::iterator;
static constexpr auto key_only = is_key_only_meta_associative_container<Type>::value;
/*! @brief True in case of key-only containers, false otherwise. */
static constexpr bool key_only = internal::key_only_associative_container_v<Type>;
/**
* @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 != nullptr) ? 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 != nullptr) ? 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 != nullptr) ? 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.
@@ -315,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.
@@ -323,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.
@@ -331,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.
@@ -339,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
@@ -348,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.
@@ -356,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
@@ -365,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.
@@ -373,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.
@@ -381,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

@@ -2,14 +2,12 @@
#define ENTT_META_FACTORY_HPP
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "../config/config.h"
#include "../core/bit.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
@@ -24,157 +22,63 @@
namespace entt {
/*! @cond TURN_OFF_DOXYGEN */
/**
* @cond TURN_OFF_DOXYGEN
* Internal details not to be documented.
*/
namespace internal {
class basic_meta_factory {
using invoke_type = std::remove_pointer_t<decltype(meta_func_node::invoke)>;
[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) {
auto &&context = internal::meta_context::from(ctx);
ENTT_ASSERT(context.value.contains(info.hash()), "Type not available");
return context.value[info.hash()];
}
auto *find_member_or_assert() {
auto *member = find_member<&meta_data_node::id>(details->data, bucket);
ENTT_ASSERT(member != nullptr, "Cannot find member");
return member;
}
inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) {
return parent.details->data.insert_or_assign(id, std::move(node)).first->second;
}
auto *find_overload_or_assert() {
auto *overload = find_overload(find_member<&meta_func_node::id>(details->func, bucket), invoke);
ENTT_ASSERT(overload != nullptr, "Cannot find overload");
return overload;
}
void reset_bucket(const id_type id, invoke_type *const ref = nullptr) {
invoke = ref;
bucket = id;
}
protected:
void type(const id_type id) noexcept {
reset_bucket(parent);
auto &&elem = meta_context::from(*ctx).value[parent];
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
elem.id = id;
}
template<typename Type>
void insert_or_assign(Type node) {
reset_bucket(parent);
if constexpr(std::is_same_v<Type, meta_base_node>) {
auto *member = find_member<&meta_base_node::type>(details->base, node.type);
member ? (*member = node) : details->base.emplace_back(node);
} else if constexpr(std::is_same_v<Type, meta_conv_node>) {
auto *member = find_member<&meta_conv_node::type>(details->conv, node.type);
member ? (*member = node) : details->conv.emplace_back(node);
} else {
static_assert(std::is_same_v<Type, meta_ctor_node>, "Unexpected type");
auto *member = find_member<&meta_ctor_node::id>(details->ctor, node.id);
member ? (*member = node) : details->ctor.emplace_back(node);
}
}
void dtor(meta_dtor_node node) {
reset_bucket(parent);
meta_context::from(*ctx).value[parent].dtor = node;
}
void data(meta_data_node node) {
reset_bucket(node.id);
if(auto *member = find_member<&meta_data_node::id>(details->data, node.id); member == nullptr) {
details->data.emplace_back(std::move(node));
} else if(member->set != node.set || member->get != node.get) {
*member = std::move(node);
}
}
void func(meta_func_node node) {
reset_bucket(node.id, node.invoke);
if(auto *member = find_member<&meta_func_node::id>(details->func, node.id); member == nullptr) {
details->func.emplace_back(std::move(node));
} else if(auto *overload = find_overload(member, node.invoke); overload == nullptr) {
while(member->next != nullptr) { member = member->next.get(); }
member->next = std::make_shared<meta_func_node>(std::move(node));
}
}
void prop(meta_prop_node node) {
std::vector<meta_prop_node> *container = nullptr;
if(bucket == parent) {
container = &details->prop;
} else if(invoke == nullptr) {
container = &find_member_or_assert()->prop;
} else {
container = &find_overload_or_assert()->prop;
inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) {
if(auto it = parent.details->func.find(id); it != parent.details->func.end()) {
for(auto *curr = &it->second; curr; curr = curr->next.get()) {
if(curr->invoke == node.invoke) {
node.next = std::move(curr->next);
*curr = std::move(node);
return *curr;
}
}
auto *member = find_member<&meta_prop_node::id>(*container, node.id);
(member != nullptr) ? (*member = std::move(node)) : container->emplace_back(std::move(node));
// locally overloaded function
node.next = std::make_shared<meta_func_node>(std::move(parent.details->func[id]));
}
void traits(const meta_traits value) {
if(bucket == parent) {
meta_context::from(*ctx).value[bucket].traits |= value;
} else if(invoke == nullptr) {
find_member_or_assert()->traits |= value;
} else {
find_overload_or_assert()->traits |= value;
}
}
void custom(meta_custom_node node) {
if(bucket == parent) {
meta_context::from(*ctx).value[bucket].custom = std::move(node);
} else if(invoke == nullptr) {
find_member_or_assert()->custom = std::move(node);
} else {
find_overload_or_assert()->custom = std::move(node);
}
}
public:
basic_meta_factory(const id_type id, meta_ctx &area)
: ctx{&area},
parent{id},
bucket{id} {
auto &&elem = meta_context::from(*ctx).value[parent];
if(!elem.details) {
elem.details = std::make_shared<meta_type_descriptor>();
}
details = elem.details.get();
}
private:
meta_ctx *ctx{};
id_type parent{};
id_type bucket{};
invoke_type *invoke{};
meta_type_descriptor *details{};
};
return parent.details->func.insert_or_assign(id, std::move(node)).first->second;
}
} // namespace internal
/*! @endcond */
/**
* @brief Meta factory to be used for reflection purposes.
* Internal details not to be documented.
* @endcond
*/
/**
* @brief Basic meta factory to be used for reflection purposes.
* @tparam Type Reflected type for which the factory was created.
*/
template<typename Type>
class meta_factory: private internal::basic_meta_factory {
using base_type = internal::basic_meta_factory;
class meta_factory {
template<typename Setter, auto Getter, typename Policy, std::size_t... Index>
void data(const id_type id, std::index_sequence<Index...>) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
using args_type = type_list<typename meta_function_helper_t<Type, decltype(value_list_element_v<Index, Setter>)>::args_type...>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
base_type::data(
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
id,
/* this is never static */
(std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
Setter::size,
@@ -182,27 +86,42 @@ class meta_factory: private internal::basic_meta_factory {
&meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
+[](meta_handle instance, meta_any value) { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
public:
/*! @brief Default constructor. */
meta_factory() noexcept
: internal::basic_meta_factory{type_id<Type>(), locator<meta_ctx>::value_or()} {}
: meta_factory{locator<meta_ctx>::value_or()} {}
/**
* @brief Context aware constructor.
* @param area The context into which to construct meta types.
*/
meta_factory(meta_ctx &area) noexcept
: internal::basic_meta_factory{type_id<Type>().hash(), area} {}
: ctx{&area},
bucket{},
info{&type_id<Type>()} {
auto &&elem = internal::owner(*ctx, *info);
if(!elem.details) {
elem.details = std::make_shared<internal::meta_type_descriptor>();
}
bucket = &elem.details->prop;
}
/**
* @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.
*/
meta_factory type(const id_type id) noexcept {
base_type::type(id);
auto type(const id_type id) noexcept {
auto &&elem = internal::owner(*ctx, *info);
ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier");
bucket = &elem.details->prop;
elem.id = id;
return *this;
}
@@ -215,10 +134,11 @@ public:
* @return A meta factory for the parent type.
*/
template<typename Base>
meta_factory base() noexcept {
auto base() noexcept {
static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type");
auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); };
base_type::insert_or_assign(internal::meta_base_node{type_id<Base>().hash(), &internal::resolve<Base>, op});
internal::owner(*ctx, *info).details->base.insert_or_assign(type_id<Base>().hash(), internal::meta_base_node{&internal::resolve<Base>, op});
bucket = nullptr;
return *this;
}
@@ -238,7 +158,8 @@ public:
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); };
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
return *this;
}
@@ -252,10 +173,11 @@ public:
* @return A meta factory for the parent type.
*/
template<typename To>
meta_factory conv() noexcept {
auto conv() noexcept {
using conv_type = std::remove_cv_t<std::remove_reference_t<To>>;
auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); };
base_type::insert_or_assign(internal::meta_conv_node{type_id<conv_type>().hash(), op});
internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op});
bucket = nullptr;
return *this;
}
@@ -270,14 +192,15 @@ 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>
meta_factory ctor() noexcept {
auto ctor() noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
static_assert(std::is_same_v<std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>, Type>, "The function doesn't return an object of the required type");
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Candidate, Policy>});
bucket = nullptr;
return *this;
}
@@ -289,16 +212,17 @@ 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>
meta_factory ctor() noexcept {
auto ctor() noexcept {
// default constructor is already implicitly generated, no need for redundancy
if constexpr(sizeof...(Args) != 0u) {
using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>;
base_type::insert_or_assign(internal::meta_ctor_node{type_id<typename descriptor::args_type>().hash(), descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id<typename descriptor::args_type>().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg<typename descriptor::args_type>, &meta_construct<Type, Args...>});
}
bucket = nullptr;
return *this;
}
@@ -321,10 +245,11 @@ public:
* @return A meta factory for the parent type.
*/
template<auto Func>
meta_factory dtor() noexcept {
auto dtor() noexcept {
static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided");
auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); };
base_type::dtor(internal::meta_dtor_node{op});
internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op};
bucket = nullptr;
return *this;
}
@@ -339,17 +264,18 @@ 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>
meta_factory data(const id_type id) noexcept {
auto data(const id_type id) noexcept {
if constexpr(std::is_member_object_pointer_v<decltype(Data)>) {
using data_type = std::invoke_result_t<decltype(Data), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
base_type::data(
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
id,
/* this is never static */
std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
1u,
@@ -357,6 +283,8 @@ public:
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
} else {
using data_type = std::remove_pointer_t<decltype(Data)>;
@@ -366,15 +294,18 @@ public:
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
}
base_type::data(
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
id,
((std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<data_type>>> || std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
1u,
&internal::resolve<std::remove_cv_t<std::remove_reference_t<data_type>>>,
&meta_arg<type_list<std::remove_cv_t<std::remove_reference_t<data_type>>>>,
&meta_setter<Type, Data>,
&meta_getter<Type, Data, Policy>});
bucket = &elem.prop;
}
return *this;
@@ -398,17 +329,18 @@ 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>
meta_factory data(const id_type id) noexcept {
auto data(const id_type id) noexcept {
using data_type = std::invoke_result_t<decltype(Getter), Type &>;
static_assert(Policy::template value<data_type>, "Invalid return type for the given policy");
if constexpr(std::is_same_v<decltype(Setter), std::nullptr_t>) {
base_type::data(
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
id,
/* this is never static */
internal::meta_traits::is_const,
0u,
@@ -416,12 +348,15 @@ public:
&meta_arg<type_list<>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
} else {
using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
base_type::data(
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_data_node{
id,
/* this is never static nor const */
internal::meta_traits::is_none,
1u,
@@ -429,6 +364,8 @@ public:
&meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
&meta_setter<Type, Setter>,
&meta_getter<Type, Getter, Policy>});
bucket = &elem.prop;
}
return *this;
@@ -449,10 +386,10 @@ 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>
meta_factory data(const id_type id) noexcept {
auto data(const id_type id) noexcept {
data<Setter, Getter, Policy>(id, std::make_index_sequence<Setter::size>{});
return *this;
}
@@ -468,74 +405,56 @@ 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>
meta_factory func(const id_type id) noexcept {
auto func(const id_type id) noexcept {
using descriptor = meta_function_helper_t<Type, decltype(Candidate)>;
static_assert(Policy::template value<typename descriptor::return_type>, "Invalid return type for the given policy");
base_type::func(
auto &&elem = internal::meta_extend(
internal::owner(*ctx, *info),
id,
internal::meta_func_node{
id,
(descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
descriptor::args_type::size,
&internal::resolve<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>,
&meta_arg<typename descriptor::args_type>,
&meta_invoke<Type, Candidate, Policy>});
bucket = &elem.prop;
return *this;
}
/**
* @brief Assigns a property to the last created meta object.
* @brief Assigns a property to the last meta object created.
*
* Both the key and the value (if any) must be at least copy constructible.
*
* @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>
[[deprecated("use ::custom() instead")]] meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) {
ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties");
if constexpr(sizeof...(Value) == 0u) {
base_type::prop(internal::meta_prop_node{id, &internal::resolve<void>});
(*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>};
} else {
base_type::prop(internal::meta_prop_node{id, &internal::resolve<std::decay_t<Value>>..., std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...});
(*bucket)[id] = internal::meta_prop_node{
&internal::resolve<std::decay_t<Value>>...,
std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...};
}
return *this;
}
/**
* @brief Sets traits on the last created meta object.
*
* The assigned value must be an enum and intended as a bitmask.
*
* @tparam Value Type of the traits value.
* @param value Traits value.
* @return A meta factory for the parent type.
*/
template<typename Value>
meta_factory traits(const Value value) {
static_assert(std::is_enum_v<Value>, "Invalid enum type");
base_type::traits(internal::user_to_meta_traits(value));
return *this;
}
/**
* @brief Sets user defined data that will never be used by the library.
* @tparam Value Type of user defined data to store.
* @tparam Args Types of arguments to use to construct the user data.
* @param args Parameters to use to initialize the user data.
* @return A meta factory for the parent type.
*/
template<typename Value, typename... Args>
meta_factory custom(Args &&...args) {
base_type::custom(internal::meta_custom_node{type_id<Value>().hash(), std::make_shared<Value>(std::forward<Args>(args)...)});
return *this;
}
private:
meta_ctx *ctx;
dense_map<id_type, internal::meta_prop_node, identity> *bucket;
const type_info *info;
};
/**

View File

@@ -13,8 +13,6 @@ struct meta_handle;
struct meta_prop;
struct meta_custom;
struct meta_data;
struct meta_func;

File diff suppressed because it is too large Load Diff

View File

@@ -5,10 +5,9 @@
#include <memory>
#include <type_traits>
#include <utility>
#include <vector>
#include "../config/config.h"
#include "../container/dense_map.hpp"
#include "../core/attribute.h"
#include "../core/bit.hpp"
#include "../core/enum.hpp"
#include "../core/fwd.hpp"
#include "../core/type_info.hpp"
@@ -23,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 {
@@ -36,58 +39,31 @@ enum class meta_traits : std::uint32_t {
is_array = 0x0020,
is_enum = 0x0040,
is_class = 0x0080,
is_pointer = 0x0100,
is_meta_pointer_like = 0x0200,
is_meta_sequence_container = 0x0400,
is_meta_associative_container = 0x0800,
_user_defined_traits = 0xFFFF,
_entt_enum_as_bitmask = 0xFFFF
is_meta_pointer_like = 0x0100,
is_meta_sequence_container = 0x0200,
is_meta_associative_container = 0x0400,
_entt_enum_as_bitmask
};
template<typename Type>
[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept {
static_assert(std::is_enum_v<Type>, "Invalid enum type");
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
return Type{static_cast<std::underlying_type_t<Type>>(static_cast<std::underlying_type_t<meta_traits>>(traits) >> shift)};
}
template<typename Type>
[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept {
static_assert(std::is_enum_v<Type>, "Invalid enum type");
constexpr auto shift = popcount(static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits));
const auto traits = static_cast<std::underlying_type_t<internal::meta_traits>>(static_cast<std::underlying_type_t<Type>>(value));
ENTT_ASSERT(traits < ((~static_cast<std::underlying_type_t<meta_traits>>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits");
return meta_traits{traits << shift};
}
struct meta_type_node;
struct meta_custom_node {
id_type type{};
std::shared_ptr<void> value{};
};
struct meta_prop_node {
id_type id{};
meta_type_node (*type)(const meta_context &) noexcept {};
std::shared_ptr<void> value{};
};
struct meta_base_node {
id_type type{};
meta_type_node (*resolve)(const meta_context &) noexcept {};
meta_type_node (*type)(const meta_context &) noexcept {};
const void *(*cast)(const void *) noexcept {};
};
struct meta_conv_node {
id_type type{};
meta_any (*conv)(const meta_ctx &, const void *){};
};
struct meta_ctor_node {
using size_type = std::size_t;
id_type id{};
size_type arity{0u};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
meta_any (*invoke)(const meta_ctx &, meta_any *const){};
@@ -100,46 +76,42 @@ struct meta_dtor_node {
struct meta_data_node {
using size_type = std::size_t;
id_type id{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*type)(const meta_context &) noexcept {};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
bool (*set)(meta_handle, meta_any){};
meta_any (*get)(const meta_ctx &, meta_handle){};
meta_custom_node custom{};
std::vector<meta_prop_node> prop{};
dense_map<id_type, meta_prop_node, identity> prop{};
};
struct meta_func_node {
using size_type = std::size_t;
id_type id{};
meta_traits traits{meta_traits::is_none};
size_type arity{0u};
meta_type_node (*ret)(const meta_context &) noexcept {};
meta_type (*arg)(const meta_ctx &, const size_type) noexcept {};
meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){};
std::shared_ptr<meta_func_node> next{};
meta_custom_node custom{};
std::vector<meta_prop_node> prop{};
dense_map<id_type, meta_prop_node, identity> prop{};
};
struct meta_template_node {
using size_type = std::size_t;
size_type arity{0u};
meta_type_node (*resolve)(const meta_context &) noexcept {};
meta_type_node (*type)(const meta_context &) noexcept {};
meta_type_node (*arg)(const meta_context &, const size_type) noexcept {};
};
struct meta_type_descriptor {
std::vector<meta_ctor_node> ctor{};
std::vector<meta_base_node> base{};
std::vector<meta_conv_node> conv{};
std::vector<meta_data_node> data{};
std::vector<meta_func_node> func{};
std::vector<meta_prop_node> prop{};
dense_map<id_type, meta_ctor_node, identity> ctor{};
dense_map<id_type, meta_base_node, identity> base{};
dense_map<id_type, meta_conv_node, identity> conv{};
dense_map<id_type, meta_data_node, identity> data{};
dense_map<id_type, meta_func_node, identity> func{};
dense_map<id_type, meta_prop_node, identity> prop{};
};
struct meta_type_node {
@@ -156,45 +128,9 @@ struct meta_type_node {
meta_any (*from_void)(const meta_ctx &, void *, const void *){};
meta_template_node templ{};
meta_dtor_node dtor{};
meta_custom_node custom{};
std::shared_ptr<meta_type_descriptor> details{};
};
template<auto Member, typename Type, typename Value>
[[nodiscard]] auto *find_member(Type &from, const Value value) {
for(auto &&elem: from) {
if((elem.*Member) == value) {
return &elem;
}
}
return static_cast<typename Type::value_type *>(nullptr);
}
[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t<decltype(meta_func_node::invoke)> *const ref) {
while((curr != nullptr) && (curr->invoke != ref)) { curr = curr->next.get(); }
return curr;
}
template<auto Member>
[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) {
using value_type = typename std::remove_reference_t<decltype((node.details.get()->*Member))>::value_type;
if(node.details) {
if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) {
return member;
}
for(auto &&curr: node.details->base) {
if(auto *elem = look_for<Member>(context, curr.resolve(context), id); elem) {
return elem;
}
}
}
return static_cast<value_type *>(nullptr);
}
template<typename Type>
meta_type_node resolve(const meta_context &) noexcept;
@@ -208,13 +144,13 @@ template<typename... Args>
}
[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept {
if((from.info != nullptr) && (to.info != nullptr) && *from.info == *to.info) {
if(from.info && to.info && *from.info == *to.info) {
return instance;
}
if(from.details) {
for(auto &&curr: from.details->base) {
if(const void *elem = try_cast(context, curr.resolve(context), to, curr.cast(instance)); elem) {
if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) {
return elem;
}
}
@@ -223,33 +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) {
for(auto &&elem: from.details->conv) {
if(elem.type == to.hash()) {
return func(instance, elem);
}
}
for(auto &&curr: from.details->base) {
if(auto other = try_convert(context, curr.resolve(context), to, arithmetic_or_enum, curr.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;
@@ -272,7 +181,6 @@ template<typename Type>
| (std::is_array_v<Type> ? meta_traits::is_array : meta_traits::is_none)
| (std::is_enum_v<Type> ? meta_traits::is_enum : meta_traits::is_none)
| (std::is_class_v<Type> ? meta_traits::is_class : meta_traits::is_none)
| (std::is_pointer_v<Type> ? meta_traits::is_pointer : meta_traits::is_none)
| (is_meta_pointer_like_v<Type> ? meta_traits::is_meta_pointer_like : meta_traits::is_none)
| (is_complete_v<meta_sequence_container_traits<Type>> ? meta_traits::is_meta_sequence_container : meta_traits::is_none)
| (is_complete_v<meta_associative_container_traits<Type>> ? meta_traits::is_meta_associative_container : meta_traits::is_none),
@@ -287,22 +195,22 @@ template<typename Type>
}
if constexpr(std::is_arithmetic_v<Type>) {
node.conversion_helper = +[](void *lhs, const void *rhs) {
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(*static_cast<const double *>(rhs))) : static_cast<double>(*static_cast<const Type *>(rhs));
node.conversion_helper = +[](void *bin, const void *value) {
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(*static_cast<const double *>(value))) : static_cast<double>(*static_cast<const Type *>(value));
};
} else if constexpr(std::is_enum_v<Type>) {
node.conversion_helper = +[](void *lhs, const void *rhs) {
return lhs ? static_cast<double>(*static_cast<Type *>(lhs) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(rhs)))) : static_cast<double>(*static_cast<const Type *>(rhs));
node.conversion_helper = +[](void *bin, const void *value) {
return bin ? static_cast<double>(*static_cast<Type *>(bin) = static_cast<Type>(static_cast<std::underlying_type_t<Type>>(*static_cast<const double *>(value)))) : static_cast<double>(*static_cast<const Type *>(value));
};
}
if constexpr(!std::is_void_v<Type> && !std::is_function_v<Type>) {
node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) {
if(elem) {
return meta_any{ctx, std::in_place_type<std::decay_t<Type> &>, *static_cast<std::decay_t<Type> *>(elem)};
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)};
}
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(celem)};
return meta_any{ctx, std::in_place_type<const std::decay_t<Type> &>, *static_cast<const std::decay_t<Type> *>(as_const)};
};
}
@@ -317,7 +225,11 @@ template<typename Type>
}
} // namespace internal
/*! @endcond */
/**
* Internal details not to be documented.
* @endcond
*/
} // namespace entt

View File

@@ -1,5 +1,3 @@
// IWYU pragma: always_keep
#ifndef ENTT_META_POINTER_HPP
#define ENTT_META_POINTER_HPP
@@ -23,7 +21,6 @@ struct is_meta_pointer_like<Type *>
* @tparam N Number of elements of the array.
*/
template<typename Type, std::size_t N>
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
struct is_meta_pointer_like<Type (*)[N]>
: std::false_type {};

Some files were not shown because too many files have changed in this diff Show More