Compare commits
	
		
			172 Commits
		
	
	
		
			40cd04f9dd
			...
			0aeafec
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 0aeafec019 | |||
| 9a0df4f577 | |||
| 61714836bb | |||
| cff0c100ec | |||
| 010c49d100 | |||
| ff5dbaffc0 | |||
| b56d581e4b | |||
| aa661aaaa7 | |||
| cc3f430bab | |||
| 139db5b03b | |||
| 7d0e5c80bd | |||
| f716ad9dd1 | |||
| 671772a20e | |||
| b0173f6d68 | |||
| 3da5872df8 | |||
| 3deb6e8469 | |||
| 0c674e0137 | |||
| 7948d820c3 | |||
| 5aac3422aa | |||
| e8eaa7a232 | |||
| 04b3382029 | |||
| 7fe6df5889 | |||
| 2647c85323 | |||
| 93140231c6 | |||
| e76e56e025 | |||
| b1062e701e | |||
| e72aebc043 | |||
| 865dfa994f | |||
| 25be42e308 | |||
| c6a0df409d | |||
| a15a9af2b3 | |||
| ca6909b64a | |||
| 5af8cfa879 | |||
| f701b7d2f8 | |||
| 852f2a6343 | |||
| 7359e30c3e | |||
| 5effe64474 | |||
| 61accfe184 | |||
| 2f44996edc | |||
| b1fe064484 | |||
| 18ca88a0d4 | |||
| 565aa4b7eb | |||
| b117da5ccf | |||
| 8eb4892b49 | |||
| 82fe4c3dd7 | |||
| 78b0e9a77f | |||
| 7fa6aa7ac2 | |||
| 20b4cdc5f1 | |||
| 7c576dd4d0 | |||
| f6cda522ca | |||
| 3d2f5b644b | |||
| 9ace11a0e2 | |||
| 6104d3b6d1 | |||
| f637c7c942 | |||
| afb886ea7c | |||
| 74129cabef | |||
| be8ceb861c | |||
| da0f59a3f5 | |||
| 000254320e | |||
| 2fac7206d2 | |||
| e8234f2a4a | |||
| a0ba0b39d8 | |||
| 845967cf12 | |||
| 92b58cbfa9 | |||
| 5a0651eaf0 | |||
| 14fcaf1d7e | |||
| 5a0252d8d0 | |||
| 9c0ffd38ce | |||
| b2ae9530a4 | |||
| ae1fb0fde3 | |||
| 35ebbbef93 | |||
| 83e200df43 | |||
| 260d3b7818 | |||
| 4ebffd8c63 | |||
| 8923e09b36 | |||
| ec4195f18a | |||
| 062ad7ae80 | |||
| aad07611c7 | |||
| 0dcb66f143 | |||
| 331c25b0e6 | |||
| e50844be06 | |||
| 4dd7c98c1a | |||
| 63bad2e99a | |||
| 9ddeea3d06 | |||
| b95f0498b6 | |||
| 7495a50723 | |||
| 1cdde5170b | |||
| 4248d1d9ab | |||
| 4f02c2b55b | |||
| 05d1648209 | |||
| fd9d14d00c | |||
| 4e4f62dd20 | |||
| cdc4284cb5 | |||
| bedbacddde | |||
| 32a8dba185 | |||
| d6e5051b15 | |||
| 780a67a40d | |||
| 0c7fff3029 | |||
| e7db39d20a | |||
| 869edb8d84 | |||
| dce42b866a | |||
| da19b0ac31 | |||
| bc090bdaa8 | |||
| 2a5937652e | |||
| b9d4f594ce | |||
| c79068c561 | |||
| e7095a1849 | |||
| 3f78e17888 | |||
| 897253e1d6 | |||
| cd28b26761 | |||
| a3126d581b | |||
| 1da4a12104 | |||
| 8725bafbdb | |||
| c24dc45e93 | |||
| 62b00a4bd6 | |||
| f1f67fe1ba | |||
| 621327bf55 | |||
| 9dabdd32fa | |||
| b479db4989 | |||
| 8633c5eafd | |||
| 440489f228 | |||
| bb824a9fb7 | |||
| c98dd8a584 | |||
| e12d7b1458 | |||
| 58edc97787 | |||
| ca6853e2c0 | |||
| fc90106d83 | |||
| 89bc11eca7 | |||
| 5c4c397f6c | |||
| da774e10e8 | |||
| 2f8eca71a4 | |||
| 90ce4bda4e | |||
| 4afe39dacc | |||
| dd316d2589 | |||
| 644725478f | |||
| 12e8b5d6c4 | |||
| f5c7261805 | |||
| 73ee0f905b | |||
| 0b46b891c2 | |||
| d131b5a02f | |||
| 9d9a486537 | |||
| 610ed10011 | |||
| 4edab11616 | |||
| 4d48f9d237 | |||
| 6df0417667 | |||
| bd9bbf67d4 | |||
| 8cab1307ab | |||
| 5bfc429450 | |||
| 7a7b55bebf | |||
| b4eda033c6 | |||
| c9672bf352 | |||
| 5547ff6d2b | |||
| ea8cc85c61 | |||
| 5ecec26731 | |||
| 01fddd19cd | |||
| e362af8271 | |||
| 2dba4f8fbb | |||
| bdfe44399a | |||
| 67653bbe50 | |||
| a144459e8d | |||
| d725ed8cd0 | |||
| 3cd551098b | |||
| 87d7eb69af | |||
| 95b77cb696 | |||
| 2e28ad7bb9 | |||
| 75f78f8c7f | |||
| ef59386e5c | |||
| c0b57c30bd | |||
| 42b3866753 | |||
| aff239377d | |||
| 3aaa1b0350 | |||
| 823a4ae189 | 
							
								
								
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| github: Green-Sky | ||||
							
								
								
									
										195
									
								
								.github/workflows/cd.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										195
									
								
								.github/workflows/cd.yml
									
									
									
									
										vendored
									
									
								
							| @@ -8,12 +8,62 @@ on: | ||||
|  | ||||
| env: | ||||
|   BUILD_TYPE: Release | ||||
|   BRANCH_NAME: ${{ github.head_ref || github.ref_name }} | ||||
|  | ||||
| jobs: | ||||
|   linux-ubuntu: | ||||
|     timeout-minutes: 10 | ||||
|  | ||||
|     runs-on: ubuntu-20.04 | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v3 | ||||
|       with: | ||||
|         submodules: recursive | ||||
|  | ||||
|     - name: Install Dependencies | ||||
|       run: sudo apt update && sudo apt -y install libsodium-dev | ||||
|  | ||||
|     - name: Configure CMake | ||||
|       run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} | ||||
|  | ||||
|     - name: Build | ||||
|       run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 | ||||
|  | ||||
|     - name: temp test | ||||
|       run: ${{github.workspace}}/build/bin/mono_time_test | ||||
|  | ||||
|     - name: Determine tag name | ||||
|       id: tag | ||||
|       shell: bash | ||||
|       # taken from llama.cpp | ||||
|       run: | | ||||
|         SHORT_HASH="$(git rev-parse --short=7 HEAD)" | ||||
|         if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then | ||||
|           echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         else | ||||
|           SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-') | ||||
|           echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|  | ||||
|     - name: Compress artifacts | ||||
|       shell: bash | ||||
|       run: | | ||||
|         tar -czvf ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-ubuntu20.04-x86_64.tar.gz -C ${{github.workspace}}/build/bin/ . | ||||
|  | ||||
|     - uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
|         # TODO: simpler name? | ||||
|         name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-ubuntu20.04-x86_64 | ||||
|         # TODO: do propper packing | ||||
|         path: | | ||||
|           ${{github.workspace}}/${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-ubuntu20.04-x86_64.tar.gz | ||||
|  | ||||
|  | ||||
|   windows: | ||||
|     timeout-minutes: 15 | ||||
|  | ||||
|     runs-on: windows-latest | ||||
|     runs-on: windows-2019 | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v3 | ||||
| @@ -32,12 +82,147 @@ jobs: | ||||
|       run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static | ||||
|  | ||||
|     - name: Build | ||||
|       run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 | ||||
|       run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} | ||||
|  | ||||
|     - uses: actions/upload-artifact@v3 | ||||
|     - name: temp test | ||||
|       run: ${{github.workspace}}/build/bin/mono_time_test.exe | ||||
|  | ||||
|     - name: Determine tag name | ||||
|       id: tag | ||||
|       shell: bash | ||||
|       # taken from llama.cpp | ||||
|       run: | | ||||
|         SHORT_HASH="$(git rev-parse --short=7 HEAD)" | ||||
|         if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then | ||||
|           echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         else | ||||
|           SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-') | ||||
|           echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|  | ||||
|     - name: Compress artifacts | ||||
|       shell: powershell | ||||
|       run: | | ||||
|         Compress-Archive -Path ${{github.workspace}}/build/bin/* -Destination ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-x86_64.zip | ||||
|  | ||||
|     - uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
|         name: windows_msvc_x86-64 | ||||
|         # TODO: simpler name? | ||||
|         name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-x86_64 | ||||
|         # TODO: do propper packing | ||||
|         path: | | ||||
|           ${{github.workspace}}/build/bin/ | ||||
|           ${{github.workspace}}/${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-x86_64.zip | ||||
|  | ||||
|  | ||||
|   windows-asan: | ||||
|     timeout-minutes: 15 | ||||
|  | ||||
|     runs-on: windows-2019 | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v3 | ||||
|       with: | ||||
|         submodules: recursive | ||||
|  | ||||
|     - name: Install Dependencies | ||||
|       run: vcpkg install libsodium:x64-windows-static pthreads:x64-windows-static | ||||
|  | ||||
|     # setup vs env | ||||
|     - uses: ilammy/msvc-dev-cmd@v1 | ||||
|       with: | ||||
|         arch: amd64 | ||||
|  | ||||
|     - name: Configure CMake | ||||
|       run: cmake -G Ninja -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_TARGET_TRIPLET=x64-windows-static -DTOMATO_ASAN=ON -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded | ||||
|  | ||||
|     - name: Build | ||||
|       run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 | ||||
|  | ||||
|     - name: temp test | ||||
|       run: ${{github.workspace}}/build/bin/mono_time_test.exe | ||||
|  | ||||
|     - name: Determine tag name | ||||
|       id: tag | ||||
|       shell: bash | ||||
|       # taken from llama.cpp | ||||
|       run: | | ||||
|         SHORT_HASH="$(git rev-parse --short=7 HEAD)" | ||||
|         if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then | ||||
|           echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         else | ||||
|           SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-') | ||||
|           echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|  | ||||
|     - name: Compress artifacts | ||||
|       shell: powershell | ||||
|       run: | | ||||
|         Compress-Archive -Path ${{github.workspace}}/build/bin/* -Destination ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-asan-x86_64.zip | ||||
|  | ||||
|     - uses: actions/upload-artifact@v4 | ||||
|       with: | ||||
|         # TODO: simpler name? | ||||
|         name: ${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-asan-x86_64 | ||||
|         # TODO: do propper packing | ||||
|         path: | | ||||
|           ${{github.workspace}}/${{ github.event.repository.name }}-${{ steps.tag.outputs.name }}-${{ runner.os }}-msvc-asan-x86_64.zip | ||||
|  | ||||
|   release: | ||||
|     if: ${{ ( github.event_name == 'push' && github.ref == 'refs/heads/master' ) }} | ||||
|  | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     needs: | ||||
|       - linux-ubuntu | ||||
|       - windows | ||||
|       - windows-asan | ||||
|  | ||||
|     permissions: | ||||
|       contents: write | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v3 | ||||
|       with: | ||||
|         submodules: recursive | ||||
|  | ||||
|     - name: Determine tag name | ||||
|       id: tag | ||||
|       shell: bash | ||||
|       # taken from llama.cpp | ||||
|       run: | | ||||
|         SHORT_HASH="$(git rev-parse --short=7 HEAD)" | ||||
|         if [[ "${{ env.BRANCH_NAME }}" == "master" ]]; then | ||||
|           echo "name=${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         else | ||||
|           SAFE_NAME=$(echo "${{ env.BRANCH_NAME }}" | tr '/' '-') | ||||
|           echo "name=${SAFE_NAME}-${SHORT_HASH}" >> $GITHUB_OUTPUT | ||||
|         fi | ||||
|  | ||||
|     - name: Download artifacts | ||||
|       id: download-artifact | ||||
|       uses: actions/download-artifact@v4 | ||||
|       with: | ||||
|         path: ./artifacts/ | ||||
|  | ||||
|     - name: Create release | ||||
|       env: | ||||
|         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|         tag: ${{ steps.tag.outputs.name }} | ||||
|       shell: bash | ||||
|       run: | | ||||
|         gh release create "$tag" \ | ||||
|             --repo="$GITHUB_REPOSITORY" \ | ||||
|             --title="nightly ${tag#v}" \ | ||||
|             --notes="nightly build" \ | ||||
|             --prerelease | ||||
|  | ||||
|     - name: Upload artifacts | ||||
|       env: | ||||
|         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||||
|         tag: ${{ steps.tag.outputs.name }} | ||||
|       shell: bash | ||||
|       run: | | ||||
|         ls -laR ./artifacts | ||||
|         gh release upload "$tag" ./artifacts/*/* \ | ||||
|             --repo="$GITHUB_REPOSITORY" | ||||
|  | ||||
|   | ||||
							
								
								
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/ci.yml
									
									
									
									
										vendored
									
									
								
							| @@ -29,6 +29,9 @@ jobs: | ||||
|     - name: Build | ||||
|       run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 | ||||
|  | ||||
|     - name: temp test | ||||
|       run: ${{github.workspace}}/build/bin/mono_time_test | ||||
|  | ||||
|   macos: | ||||
|     timeout-minutes: 10 | ||||
|  | ||||
| @@ -72,3 +75,6 @@ jobs: | ||||
|     - name: Build | ||||
|       run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -j 4 | ||||
|  | ||||
|     - name: temp test | ||||
|       run: ${{github.workspace}}/build/bin/mono_time_test.exe | ||||
|  | ||||
|   | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -11,6 +11,7 @@ cmake-build-release/ | ||||
| *.coredump | ||||
| compile_commands.json | ||||
| /build* | ||||
| /result* | ||||
| .clangd | ||||
| .cache | ||||
|  | ||||
| @@ -20,3 +21,6 @@ compile_commands.json | ||||
|  | ||||
| CMakeLists.txt.user* | ||||
| CMakeCache.txt | ||||
|  | ||||
| *.tox | ||||
| imgui.ini | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| cmake_minimum_required(VERSION 3.9 FATAL_ERROR) | ||||
| cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR) | ||||
|  | ||||
| # cmake setup begin | ||||
| project(tomato) | ||||
| @@ -6,7 +6,7 @@ project(tomato) | ||||
| set(CMAKE_POSITION_INDEPENDENT_CODE ON) | ||||
|  | ||||
| # defaulting to debug mode, if not specified | ||||
| if(NOT CMAKE_BUILD_TYPE) | ||||
| if (NOT CMAKE_BUILD_TYPE) | ||||
| 	set(CMAKE_BUILD_TYPE "Debug") | ||||
| endif() | ||||
|  | ||||
| @@ -18,6 +18,26 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib") | ||||
| set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") | ||||
| set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") | ||||
|  | ||||
| option(TOMATO_ASAN "Build tomato with asan (gcc/clang/msvc)" OFF) | ||||
|  | ||||
| if (TOMATO_ASAN) | ||||
| 	if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") | ||||
| 		if (NOT WIN32) # exclude mingw | ||||
| 			link_libraries(-fsanitize=address) | ||||
| 			#link_libraries(-fsanitize=address,undefined) | ||||
| 			#link_libraries(-fsanitize=undefined) | ||||
| 			message("II enabled ASAN") | ||||
| 		else() | ||||
| 			message("!! can not enable ASAN on this platform (gcc/clang + win)") | ||||
| 		endif() | ||||
| 	elseif (MSVC) | ||||
| 		add_compile_options("/fsanitize=address") | ||||
| 		message("II enabled ASAN") | ||||
| 	else() | ||||
| 		message("!! can not enable ASAN on this platform") | ||||
| 	endif() | ||||
| endif() | ||||
|  | ||||
| # external libs | ||||
| add_subdirectory(./external) # before increasing warn levels, sad :( | ||||
|  | ||||
| @@ -33,13 +53,8 @@ if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL | ||||
| 		#-Wsign-conversion # Warn on sign conversions | ||||
| 		-Wshadow # Warn if a variable declaration shadows one from a parent context | ||||
| 	) | ||||
|  | ||||
| 	if (NOT WIN32) | ||||
| 		#link_libraries(-fsanitize=address,undefined) | ||||
| 		#link_libraries(-fsanitize=undefined) | ||||
| 	endif() | ||||
| elseif (${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") | ||||
| 	if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") | ||||
| 	if (MSVC) | ||||
| 		string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") | ||||
| 	else() | ||||
| 		set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") | ||||
|   | ||||
							
								
								
									
										3
									
								
								external/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								external/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -15,3 +15,6 @@ add_subdirectory(./solanaceae_tox) | ||||
| add_subdirectory(./sdl) | ||||
| add_subdirectory(./imgui) | ||||
|  | ||||
| add_subdirectory(./stb) | ||||
| add_subdirectory(./libwebp) | ||||
|  | ||||
|   | ||||
| @@ -12,14 +12,14 @@ jobs: | ||||
|     timeout-minutes: 30 | ||||
|  | ||||
|     env: | ||||
|       IWYU: 0.18 | ||||
|       LLVM: 14 | ||||
|       IWYU: 0.19 | ||||
|       LLVM: 15 | ||||
|  | ||||
|     runs-on: ubuntu-latest | ||||
|     continue-on-error: true | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Install llvm/clang | ||||
|       # see: https://apt.llvm.org/ | ||||
|       run: | | ||||
| @@ -39,17 +39,23 @@ jobs: | ||||
|         git clone https://github.com/include-what-you-use/include-what-you-use.git --branch $IWYU --depth 1 | ||||
|         mkdir include-what-you-use/build | ||||
|         cd include-what-you-use/build | ||||
|         cmake -DCMAKE_C_COMPILER=clang-$LLVM -DCMAKE_CXX_COMPILER=clang++-$LLVM -DCMAKE_INSTALL_PREFIX=./ .. | ||||
|         cmake -DCMAKE_C_COMPILER=clang-$LLVM \ | ||||
|               -DCMAKE_CXX_COMPILER=clang++-$LLVM \ | ||||
|               -DCMAKE_INSTALL_PREFIX=./ \ | ||||
|               .. | ||||
|         make -j4 | ||||
|         bin/include-what-you-use --version | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       run: | | ||||
|         export PATH=$PATH:${GITHUB_WORKSPACE}/build/include-what-you-use/build/bin | ||||
|         cmake -DENTT_BUILD_TESTING=ON \ | ||||
|         cmake -DCMAKE_C_COMPILER=clang-$LLVM \ | ||||
|               -DCMAKE_CXX_COMPILER=clang++-$LLVM \ | ||||
|               -DENTT_BUILD_TESTING=ON \ | ||||
|               -DENTT_BUILD_BENCHMARK=ON \ | ||||
|               -DENTT_BUILD_EXAMPLE=ON \ | ||||
|               -DENTT_BUILD_LIB=ON \ | ||||
|               -DENTT_BUILD_SNAPSHOT=ON \ | ||||
|               -DCMAKE_CXX_INCLUDE_WHAT_YOU_USE="include-what-you-use;-Xiwyu;--mapping_file=${GITHUB_WORKSPACE}/entt.imp;-Xiwyu;--no_fwd_decls;-Xiwyu;--verbose=1" .. | ||||
|               -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 | ||||
|   | ||||
							
								
								
									
										129
									
								
								external/entt/entt/.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										129
									
								
								external/entt/entt/.github/workflows/build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -9,38 +9,61 @@ jobs: | ||||
|  | ||||
|     strategy: | ||||
|       matrix: | ||||
|         os: [ubuntu-latest, ubuntu-20.04] | ||||
|         compiler: | ||||
|           - pkg: g++-7 | ||||
|             exe: g++-7 | ||||
|           - pkg: g++-8 | ||||
|             exe: g++-8 | ||||
|           - pkg: g++-9 | ||||
|             exe: g++-9 | ||||
|           - pkg: g++-10 | ||||
|             exe: g++-10 | ||||
|           - pkg: clang-8 | ||||
|             exe: clang++-8 | ||||
|           - pkg: clang-9 | ||||
|             exe: clang++-9 | ||||
|           - pkg: clang-10 | ||||
|             exe: clang++-10 | ||||
|           - pkg: clang-11 | ||||
|             exe: clang++-11 | ||||
|           - pkg: clang-12 | ||||
|             exe: clang++-12 | ||||
|           - { 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: 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 } | ||||
|         exclude: | ||||
|           - os: ubuntu-latest | ||||
|             compiler: { pkg: g++, exe: 'g++', version: 7 } | ||||
|           - os: ubuntu-latest | ||||
|             compiler: { pkg: g++, exe: 'g++', version: 8 } | ||||
|           - os: ubuntu-latest | ||||
|             compiler: { pkg: g++, exe: 'g++', version: 9 } | ||||
|           - 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-20.04 | ||||
|             compiler: { pkg: g++, exe: 'g++', version: 11 } | ||||
|           - 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-20.04 | ||||
|             compiler: { pkg: clang, exe: 'clang++', version: 14 } | ||||
|  | ||||
|     runs-on: ubuntu-latest | ||||
|     runs-on: ${{ matrix.os }} | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Install compiler | ||||
|       run: | | ||||
|         sudo apt update | ||||
|         sudo apt install -y ${{ matrix.compiler.pkg }} | ||||
|         sudo apt install -y ${{ matrix.compiler.pkg }}-${{ matrix.compiler.version }} | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|         CXX: ${{ matrix.compiler.exe }} | ||||
|         CXX: ${{ matrix.compiler.exe }}-${{ matrix.compiler.version }} | ||||
|       run: | | ||||
|         cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON .. | ||||
|         make -j4 | ||||
| @@ -50,32 +73,6 @@ jobs: | ||||
|         CTEST_OUTPUT_ON_FAILURE: 1 | ||||
|       run: ctest --timeout 30 -C Debug -j4 | ||||
|  | ||||
|   linux-extra: | ||||
|     timeout-minutes: 15 | ||||
|  | ||||
|     strategy: | ||||
|       matrix: | ||||
|         compiler: [g++, clang++] | ||||
|         id_type: ["std::uint32_t", "std::uint64_t"] | ||||
|         cxx_std: [cxx_std_17, cxx_std_20] | ||||
|  | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|         CXX: ${{ matrix.compiler }} | ||||
|       run: | | ||||
|         cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} .. | ||||
|         make -j4 | ||||
|     - name: Run tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|         CTEST_OUTPUT_ON_FAILURE: 1 | ||||
|       run: ctest --timeout 30 -C Debug -j4 | ||||
|  | ||||
|   windows: | ||||
|     timeout-minutes: 15 | ||||
|  | ||||
| @@ -93,7 +90,7 @@ jobs: | ||||
|     runs-on: windows-latest | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       run: | | ||||
| @@ -105,35 +102,12 @@ jobs: | ||||
|         CTEST_OUTPUT_ON_FAILURE: 1 | ||||
|       run: ctest --timeout 30 -C Debug -j4 | ||||
|  | ||||
|   windows-extra: | ||||
|     timeout-minutes: 15 | ||||
|  | ||||
|     strategy: | ||||
|       matrix: | ||||
|         id_type: ["std::uint32_t", "std::uint64_t"] | ||||
|         cxx_std: [cxx_std_17, cxx_std_20] | ||||
|  | ||||
|     runs-on: windows-latest | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       run: | | ||||
|         cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} .. | ||||
|         cmake --build . -j 4 | ||||
|     - name: Run tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|         CTEST_OUTPUT_ON_FAILURE: 1 | ||||
|       run: ctest --timeout 30 -C Debug -j4 | ||||
|  | ||||
|   macos: | ||||
|     timeout-minutes: 15 | ||||
|     runs-on: macOS-latest | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       run: | | ||||
| @@ -145,23 +119,24 @@ jobs: | ||||
|         CTEST_OUTPUT_ON_FAILURE: 1 | ||||
|       run: ctest --timeout 30 -C Debug -j4 | ||||
|  | ||||
|   macos-extra: | ||||
|   extra: | ||||
|     timeout-minutes: 15 | ||||
|  | ||||
|     strategy: | ||||
|       matrix: | ||||
|         os: [windows-latest, macOS-latest, ubuntu-latest] | ||||
|         id_type: ["std::uint32_t", "std::uint64_t"] | ||||
|         cxx_std: [cxx_std_17, cxx_std_20] | ||||
|  | ||||
|     runs-on: macOS-latest | ||||
|     runs-on: ${{ matrix.os }} | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       run: | | ||||
|         cmake -DENTT_BUILD_TESTING=ON -DENTT_CXX_STD=${{ matrix.cxx_std }} -DENTT_ID_TYPE=${{ matrix.id_type }} .. | ||||
|         make -j4 | ||||
|         cmake --build . -j 4 | ||||
|     - name: Run tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|   | ||||
| @@ -9,11 +9,11 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|         CXXFLAGS: "--coverage -fno-inline" | ||||
|         CXXFLAGS: "--coverage -fno-elide-constructors -fno-inline -fno-default-inline" | ||||
|         CXX: g++ | ||||
|       run: | | ||||
|         cmake -DENTT_BUILD_TESTING=ON -DENTT_BUILD_LIB=ON -DENTT_BUILD_EXAMPLE=ON .. | ||||
| @@ -30,7 +30,7 @@ jobs: | ||||
|         lcov -c -d . -o coverage.info | ||||
|         lcov -l coverage.info | ||||
|     - name: Upload coverage to Codecov | ||||
|       uses: codecov/codecov-action@v2 | ||||
|       uses: codecov/codecov-action@v3 | ||||
|       with: | ||||
|         token: ${{ secrets.CODECOV_TOKEN }} | ||||
|         files: build/coverage.info | ||||
|   | ||||
| @@ -15,7 +15,7 @@ jobs: | ||||
|       FORMULA: entt.rb | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Clone repository | ||||
|       working-directory: build | ||||
|       env: | ||||
|   | ||||
| @@ -16,7 +16,7 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|     - uses: actions/checkout@v3 | ||||
|     - name: Compile tests | ||||
|       working-directory: build | ||||
|       env: | ||||
|   | ||||
							
								
								
									
										6
									
								
								external/entt/entt/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								external/entt/entt/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,7 @@ | ||||
| # EnTT | ||||
| # | ||||
|  | ||||
| cmake_minimum_required(VERSION 3.12.4) | ||||
| cmake_minimum_required(VERSION 3.15.7) | ||||
|  | ||||
| # | ||||
| # Read project version | ||||
| @@ -31,7 +31,7 @@ endif() | ||||
|  | ||||
| message(VERBOSE "*") | ||||
| message(VERBOSE "* ${PROJECT_NAME} v${PROJECT_VERSION} (${CMAKE_BUILD_TYPE})") | ||||
| message(VERBOSE "* Copyright (c) 2017-2022 Michele Caini <michele.caini@gmail.com>") | ||||
| message(VERBOSE "* Copyright (c) 2017-2023 Michele Caini <michele.caini@gmail.com>") | ||||
| message(VERBOSE "*") | ||||
|  | ||||
| # | ||||
| @@ -143,6 +143,7 @@ if(ENTT_INCLUDE_HEADERS) | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/fwd.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/group.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/handle.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/mixin.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/helper.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/observer.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/organizer.hpp> | ||||
| @@ -151,7 +152,6 @@ if(ENTT_INCLUDE_HEADERS) | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/snapshot.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/sparse_set.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/storage_mixin.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/entity/view.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/adjacency_matrix.hpp> | ||||
|             $<BUILD_INTERFACE:${EnTT_SOURCE_DIR}/src/entt/graph/dot.hpp> | ||||
|   | ||||
							
								
								
									
										2
									
								
								external/entt/entt/LICENSE
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								external/entt/entt/LICENSE
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| The MIT License (MIT) | ||||
|  | ||||
| Copyright (c) 2017-2022 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 | ||||
|   | ||||
							
								
								
									
										19
									
								
								external/entt/entt/README.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										19
									
								
								external/entt/entt/README.md
									
									
									
									
										vendored
									
									
								
							| @@ -6,7 +6,8 @@ | ||||
| [](https://github.com/skypjack/entt/actions) | ||||
| [](https://codecov.io/gh/skypjack/entt) | ||||
| [](https://godbolt.org/z/zxW73f) | ||||
| [](http://entt.docsforge.com/) | ||||
| [](https://vcpkg.link/ports/entt) | ||||
| [](https://skypjack.github.io/entt/) | ||||
| [](https://gitter.im/skypjack/entt) | ||||
| [](https://discord.gg/5BjPWBd) | ||||
| [](https://www.paypal.me/skypjack) | ||||
| @@ -343,8 +344,8 @@ The documentation is based on [doxygen](http://www.doxygen.nl/). To build it: | ||||
|     $ cmake .. -DENTT_BUILD_DOCS=ON | ||||
|     $ make | ||||
|  | ||||
| The API reference will be created in HTML format within the directory | ||||
| `build/docs/html`. To navigate it with your favorite browser: | ||||
| The API reference is created in HTML format in the `build/docs/html` directory. | ||||
| To navigate it with your favorite browser: | ||||
|  | ||||
|     $ cd build | ||||
|     $ your_favorite_browser docs/html/index.html | ||||
| @@ -353,10 +354,7 @@ The API reference will be created in HTML format within the directory | ||||
| @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. If you are looking for | ||||
| something more pleasing to the eye, consider reading the nice-looking version | ||||
| available on [docsforge](https://entt.docsforge.com/): same documentation, much | ||||
| more pleasant to read.<br/> | ||||
| 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. | ||||
| <!-- | ||||
| @@ -366,9 +364,8 @@ to the project where users can find all related documentation pages. | ||||
| # Tests | ||||
|  | ||||
| To compile and run the tests, `EnTT` requires *googletest*.<br/> | ||||
| `cmake` will download and compile the library before compiling anything else. | ||||
| In order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to | ||||
| `ON`. | ||||
| `cmake` downloads and compiles the library before compiling anything else. In | ||||
| order to build the tests, set the `CMake` option `ENTT_BUILD_TESTING` to `ON`. | ||||
|  | ||||
| To build the most basic set of tests: | ||||
|  | ||||
| @@ -420,7 +417,7 @@ know who has participated so far. | ||||
|  | ||||
| # License | ||||
|  | ||||
| Code and documentation Copyright (c) 2017-2022 Michele Caini.<br/> | ||||
| Code and documentation Copyright (c) 2017-2023 Michele Caini.<br/> | ||||
| Colorful logo Copyright (c) 2018-2021 Richard Caseres. | ||||
|  | ||||
| Code released under | ||||
|   | ||||
							
								
								
									
										21
									
								
								external/entt/entt/TODO
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										21
									
								
								external/entt/entt/TODO
									
									
									
									
										vendored
									
									
								
							| @@ -1,27 +1,26 @@ | ||||
| * debugging tools (#60): the issue online already contains interesting tips on this, look at it | ||||
| * work stealing job system (see #100) + mt scheduler based on const awareness for types | ||||
|  | ||||
| EXAMPLES | ||||
| * filter on runtime values/variables (not only types) | ||||
| * support to polymorphic types (see #859) | ||||
|  | ||||
| DOC: | ||||
| * storage<void> | ||||
| * 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 | ||||
|  | ||||
| TODO (high prio): | ||||
| * remove the static storage from the const assure in the registry | ||||
| * 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 | ||||
| * add storage getter for filters to views and groups | ||||
| * exploit the tombstone mechanism to allow enabling/disabling entities (see bump, compact and clear for further details) | ||||
| * basic_storage::bind for cross-registry setups (see and remove todo from entity_copy.cpp) | ||||
| * process scheduler: reviews, use free lists internally | ||||
| * dedicated entity storage, in-place O(1) release/destroy for non-orphaned entities, out-of-sync model | ||||
| * entity-only and exclude-only views (both solved with entity storage and storage<void>) | ||||
| * custom allocators all over (registry, ...) | ||||
| * add test for maximum number of entities reached | ||||
| * 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 | ||||
|   | ||||
							
								
								
									
										16
									
								
								external/entt/entt/docs/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								external/entt/entt/docs/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -2,8 +2,22 @@ | ||||
| # Doxygen configuration (documentation) | ||||
| # | ||||
|  | ||||
| set(DOXY_DEPS_DIRECTORY ${EnTT_SOURCE_DIR}/deps) | ||||
| FetchContent_Declare( | ||||
|     doxygen-awesome-css | ||||
|     GIT_REPOSITORY https://github.com/jothepro/doxygen-awesome-css | ||||
|     GIT_TAG main | ||||
|     GIT_SHALLOW 1 | ||||
| ) | ||||
|  | ||||
| FetchContent_GetProperties(doxygen-awesome-css) | ||||
|  | ||||
| if(NOT doxygen-awesome-css_POPULATED) | ||||
|     FetchContent_Populate(doxygen-awesome-css) | ||||
|     set(doxygen-awesome-css_INCLUDE_DIR ${doxygen-awesome-css_SOURCE_DIR}) | ||||
| endif() | ||||
|  | ||||
| set(DOXY_SOURCE_DIRECTORY ${EnTT_SOURCE_DIR}/src) | ||||
| set(DOXY_CSS_DIRECTORY ${doxygen-awesome-css_INCLUDE_DIR}) | ||||
| set(DOXY_DOCS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) | ||||
| set(DOXY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) | ||||
|  | ||||
|   | ||||
							
								
								
									
										142
									
								
								external/entt/entt/docs/doxy.in
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										142
									
								
								external/entt/entt/docs/doxy.in
									
									
									
									
										vendored
									
									
								
							| @@ -1,4 +1,4 @@ | ||||
| # Doxyfile 1.9.4 | ||||
| # Doxyfile 1.9.6 | ||||
|  | ||||
| # This file describes the settings to be used by the documentation system | ||||
| # doxygen (www.doxygen.org) for a project. | ||||
| @@ -19,7 +19,8 @@ | ||||
| # configuration file: | ||||
| # doxygen -x [configFile] | ||||
| # Use doxygen to compare the used configuration file with the template | ||||
| # configuration file without replacing the environment variables: | ||||
| # configuration file without replacing the environment variables or CMake type | ||||
| # replacement variables: | ||||
| # doxygen -x_noenv [configFile] | ||||
|  | ||||
| #--------------------------------------------------------------------------- | ||||
| @@ -85,7 +86,7 @@ CREATE_SUBDIRS         = NO | ||||
| # level increment doubles the number of directories, resulting in 4096 | ||||
| # directories at level 8 which is the default and also the maximum value. The | ||||
| # sub-directories are organized in 2 levels, the first level always has a fixed | ||||
| # numer of 16 directories. | ||||
| # number of 16 directories. | ||||
| # Minimum value: 0, maximum value: 8, default value: 8. | ||||
| # This tag requires that the tag CREATE_SUBDIRS is set to YES. | ||||
|  | ||||
| @@ -557,7 +558,8 @@ HIDE_UNDOC_MEMBERS     = NO | ||||
| # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all | ||||
| # undocumented classes that are normally visible in the class hierarchy. If set | ||||
| # to NO, these classes will be included in the various overviews. This option | ||||
| # has no effect if EXTRACT_ALL is enabled. | ||||
| # will also hide undocumented C++ concepts if enabled. This option has no effect | ||||
| # if EXTRACT_ALL is enabled. | ||||
| # The default value is: NO. | ||||
|  | ||||
| HIDE_UNDOC_CLASSES     = NO | ||||
| @@ -595,7 +597,8 @@ INTERNAL_DOCS          = NO | ||||
| # Windows (including Cygwin) and MacOS, users should typically set this option | ||||
| # to NO, whereas on Linux or other Unix flavors it should typically be set to | ||||
| # YES. | ||||
| # The default value is: system dependent. | ||||
| # Possible values are: SYSTEM, NO and YES. | ||||
| # The default value is: SYSTEM. | ||||
|  | ||||
| CASE_SENSE_NAMES       = YES | ||||
|  | ||||
| @@ -847,6 +850,14 @@ WARN_IF_INCOMPLETE_DOC = YES | ||||
|  | ||||
| WARN_NO_PARAMDOC       = YES | ||||
|  | ||||
| # If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about | ||||
| # undocumented enumeration values. If set to NO, doxygen will accept | ||||
| # undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag | ||||
| # will automatically be disabled. | ||||
| # The default value is: NO. | ||||
|  | ||||
| WARN_IF_UNDOC_ENUM_VAL = NO | ||||
|  | ||||
| # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when | ||||
| # 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 | ||||
| @@ -905,10 +916,21 @@ INPUT                  = @DOXY_SOURCE_DIRECTORY@ \ | ||||
| # libiconv (or the iconv built into libc) for the transcoding. See the libiconv | ||||
| # documentation (see: | ||||
| # https://www.gnu.org/software/libiconv/) for the list of possible encodings. | ||||
| # See also: INPUT_FILE_ENCODING | ||||
| # The default value is: UTF-8. | ||||
|  | ||||
| INPUT_ENCODING         = UTF-8 | ||||
|  | ||||
| # This tag can be used to specify the character encoding of the source files | ||||
| # that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify | ||||
| # character encoding on a per file pattern basis. Doxygen will compare the file | ||||
| # name with each pattern and apply the encoding instead of the default | ||||
| # INPUT_ENCODING) if there is a match. The character encodings are a list of the | ||||
| # form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding | ||||
| # "INPUT_ENCODING" for further information on supported encodings. | ||||
|  | ||||
| INPUT_FILE_ENCODING    = | ||||
|  | ||||
| # If the value of the INPUT tag contains directories, you can use the | ||||
| # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and | ||||
| # *.h) to filter out the source-files in the directories. | ||||
| @@ -945,7 +967,7 @@ RECURSIVE              = YES | ||||
| # Note that relative paths are relative to the directory from which doxygen is | ||||
| # run. | ||||
|  | ||||
| EXCLUDE                = @DOXY_DEPS_DIRECTORY@ | ||||
| EXCLUDE                = | ||||
|  | ||||
| # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or | ||||
| # directories that are symbolic links (a Unix file system feature) are excluded | ||||
| @@ -1015,6 +1037,11 @@ IMAGE_PATH             = | ||||
| # code is scanned, but not when the output code is generated. If lines are added | ||||
| # or removed, the anchors will not be placed correctly. | ||||
| # | ||||
| # Note that doxygen will use the data processed and written to standard output | ||||
| # for further processing, therefore nothing else, like debug statements or used | ||||
| # commands (so in case of a Windows batch file always use @echo OFF), should be | ||||
| # written to standard output. | ||||
| # | ||||
| # Note that for custom extensions or not directly supported extensions you also | ||||
| # need to set EXTENSION_MAPPING for the extension otherwise the files are not | ||||
| # properly processed by doxygen. | ||||
| @@ -1056,6 +1083,15 @@ FILTER_SOURCE_PATTERNS = | ||||
|  | ||||
| USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/README.md | ||||
|  | ||||
| # The Fortran standard specifies that for fixed formatted Fortran code all | ||||
| # characters from position 72 are to be considered as comment. A common | ||||
| # extension is to allow longer lines before the automatic comment starts. The | ||||
| # setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can | ||||
| # be processed before the automatic comment starts. | ||||
| # Minimum value: 7, maximum value: 10000, default value: 72. | ||||
|  | ||||
| FORTRAN_COMMENT_AFTER  = 72 | ||||
|  | ||||
| #--------------------------------------------------------------------------- | ||||
| # Configuration options related to source browsing | ||||
| #--------------------------------------------------------------------------- | ||||
| @@ -1193,10 +1229,11 @@ CLANG_DATABASE_PATH    = | ||||
|  | ||||
| ALPHABETICAL_INDEX     = YES | ||||
|  | ||||
| # In case all classes in a project start with a common prefix, all classes will | ||||
| # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag | ||||
| # can be used to specify a prefix (or a list of prefixes) that should be ignored | ||||
| # while generating the index headers. | ||||
| # The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) | ||||
| # that should be ignored while generating the index headers. The IGNORE_PREFIX | ||||
| # tag works for classes, function and member names. The entity will be placed in | ||||
| # the alphabetical list under the first letter of the entity name that remains | ||||
| # after removing the prefix. | ||||
| # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. | ||||
|  | ||||
| IGNORE_PREFIX          = | ||||
| @@ -1265,7 +1302,7 @@ HTML_FOOTER            = | ||||
| # obsolete. | ||||
| # This tag requires that the tag GENERATE_HTML is set to YES. | ||||
|  | ||||
| HTML_STYLESHEET        = | ||||
| HTML_STYLESHEET        = @DOXY_CSS_DIRECTORY@/doxygen-awesome.css | ||||
|  | ||||
| # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined | ||||
| # cascading style sheets that are included after the standard style sheets | ||||
| @@ -1275,7 +1312,12 @@ HTML_STYLESHEET        = | ||||
| # Doxygen will copy the style sheet files to the output directory. | ||||
| # Note: The order of the extra style sheet files is of importance (e.g. the last | ||||
| # style sheet in the list overrules the setting of the previous ones in the | ||||
| # list). For an example see the documentation. | ||||
| # list). | ||||
| # Note: Since the styling of scrollbars can currently not be overruled in | ||||
| # Webkit/Chromium, the styling will be left out of the default doxygen.css if | ||||
| # one or more extra stylesheets have been specified. So if scrollbar | ||||
| # customization is desired it has to be added explicitly. For an example see the | ||||
| # documentation. | ||||
| # This tag requires that the tag GENERATE_HTML is set to YES. | ||||
|  | ||||
| HTML_EXTRA_STYLESHEET  = | ||||
| @@ -1290,6 +1332,19 @@ HTML_EXTRA_STYLESHEET  = | ||||
|  | ||||
| HTML_EXTRA_FILES       = | ||||
|  | ||||
| # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output | ||||
| # should be rendered with a dark or light theme. | ||||
| # Possible values are: LIGHT always generate light mode output, DARK always | ||||
| # generate dark mode output, AUTO_LIGHT automatically set the mode according to | ||||
| # the user preference, use light mode if no preference is set (the default), | ||||
| # AUTO_DARK automatically set the mode according to the user preference, use | ||||
| # dark mode if no preference is set and TOGGLE allow to user to switch between | ||||
| # light and dark mode via a button. | ||||
| # The default value is: AUTO_LIGHT. | ||||
| # This tag requires that the tag GENERATE_HTML is set to YES. | ||||
|  | ||||
| HTML_COLORSTYLE        = LIGHT | ||||
|  | ||||
| # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen | ||||
| # will adjust the colors in the style sheet and background images according to | ||||
| # this color. Hue is specified as an angle on a color-wheel, see | ||||
| @@ -1653,17 +1708,6 @@ HTML_FORMULA_FORMAT    = png | ||||
|  | ||||
| FORMULA_FONTSIZE       = 10 | ||||
|  | ||||
| # Use the FORMULA_TRANSPARENT tag to determine whether or not the images | ||||
| # generated for formulas are transparent PNGs. Transparent PNGs are not | ||||
| # supported properly for IE 6.0, but are supported on all modern browsers. | ||||
| # | ||||
| # Note that when changing this option you need to delete any form_*.png files in | ||||
| # the HTML output directory before the changes have effect. | ||||
| # The default value is: YES. | ||||
| # This tag requires that the tag GENERATE_HTML is set to YES. | ||||
|  | ||||
| FORMULA_TRANSPARENT    = YES | ||||
|  | ||||
| # The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands | ||||
| # to create new LaTeX commands to be used in formulas as building blocks. See | ||||
| # the section "Including formulas" for details. | ||||
| @@ -2379,26 +2423,38 @@ HAVE_DOT               = YES | ||||
|  | ||||
| DOT_NUM_THREADS        = 0 | ||||
|  | ||||
| # When you want a differently looking font in the dot files that doxygen | ||||
| # generates you can specify the font name using DOT_FONTNAME. You need to make | ||||
| # sure dot is able to find the font, which can be done by putting it in a | ||||
| # standard location or by setting the DOTFONTPATH environment variable or by | ||||
| # setting DOT_FONTPATH to the directory containing the font. | ||||
| # The default value is: Helvetica. | ||||
| # DOT_COMMON_ATTR is common attributes for nodes, edges and labels of | ||||
| # subgraphs. When you want a differently looking font in the dot files that | ||||
| # doxygen generates you can specify fontname, fontcolor and fontsize attributes. | ||||
| # For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node, | ||||
| # Edge and Graph Attributes specification</a> You need to make sure dot is able | ||||
| # to find the font, which can be done by putting it in a standard location or by | ||||
| # setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the | ||||
| # directory containing the font. Default graphviz fontsize is 14. | ||||
| # The default value is: fontname=Helvetica,fontsize=10. | ||||
| # This tag requires that the tag HAVE_DOT is set to YES. | ||||
|  | ||||
| DOT_FONTNAME           = Helvetica | ||||
| DOT_COMMON_ATTR        = "fontname=Helvetica,fontsize=10" | ||||
|  | ||||
| # The DOT_FONTSIZE tag can be used to set the size (in points) of the font of | ||||
| # dot graphs. | ||||
| # Minimum value: 4, maximum value: 24, default value: 10. | ||||
| # DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can | ||||
| # add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a | ||||
| # href=https://graphviz.org/doc/info/arrows.html>Complete documentation about | ||||
| # arrows shapes.</a> | ||||
| # The default value is: labelfontname=Helvetica,labelfontsize=10. | ||||
| # This tag requires that the tag HAVE_DOT is set to YES. | ||||
|  | ||||
| DOT_FONTSIZE           = 10 | ||||
| DOT_EDGE_ATTR          = "labelfontname=Helvetica,labelfontsize=10" | ||||
|  | ||||
| # By default doxygen will tell dot to use the default font as specified with | ||||
| # DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set | ||||
| # the path where dot can find it using this tag. | ||||
| # DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes | ||||
| # around nodes set 'shape=plain' or 'shape=plaintext' <a | ||||
| # href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a> | ||||
| # The default value is: shape=box,height=0.2,width=0.4. | ||||
| # This tag requires that the tag HAVE_DOT is set to YES. | ||||
|  | ||||
| DOT_NODE_ATTR          = "shape=box,height=0.2,width=0.4" | ||||
|  | ||||
| # You can set the path where dot can find font specified with fontname in | ||||
| # DOT_COMMON_ATTR and others dot attributes. | ||||
| # This tag requires that the tag HAVE_DOT is set to YES. | ||||
|  | ||||
| DOT_FONTPATH           = | ||||
| @@ -2641,18 +2697,6 @@ DOT_GRAPH_MAX_NODES    = 50 | ||||
|  | ||||
| MAX_DOT_GRAPH_DEPTH    = 0 | ||||
|  | ||||
| # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent | ||||
| # background. This is disabled by default, because dot on Windows does not seem | ||||
| # to support this out of the box. | ||||
| # | ||||
| # Warning: Depending on the platform used, enabling this option may lead to | ||||
| # badly anti-aliased labels on the edges of a graph (i.e. they become hard to | ||||
| # read). | ||||
| # The default value is: NO. | ||||
| # This tag requires that the tag HAVE_DOT is set to YES. | ||||
|  | ||||
| DOT_TRANSPARENT        = NO | ||||
|  | ||||
| # Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output | ||||
| # files in one run (i.e. multiple -o and -T options on the command line). This | ||||
| # makes dot run faster, but since only newer versions of dot (>1.8.10) support | ||||
|   | ||||
							
								
								
									
										1
									
								
								external/entt/entt/docs/md/config.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								external/entt/entt/docs/md/config.md
									
									
									
									
										vendored
									
									
								
							| @@ -17,7 +17,6 @@ | ||||
|     * [ENTT_DISABLE_ASSERT](#entt_disable_assert) | ||||
|   * [ENTT_NO_ETO](#entt_no_eto) | ||||
|   * [ENTT_STANDARD_CPP](#entt_standard_cpp) | ||||
|  | ||||
| <!-- | ||||
| @endcond TURN_OFF_DOXYGEN | ||||
| --> | ||||
|   | ||||
							
								
								
									
										7
									
								
								external/entt/entt/docs/md/container.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								external/entt/entt/docs/md/container.md
									
									
									
									
										vendored
									
									
								
							| @@ -9,7 +9,6 @@ | ||||
| * [Containers](#containers) | ||||
|   * [Dense map](#dense-map) | ||||
|   * [Dense set](#dense-set) | ||||
|  | ||||
| <!-- | ||||
| @endcond TURN_OFF_DOXYGEN | ||||
| --> | ||||
| @@ -21,7 +20,7 @@ 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 functionality by | ||||
| However, the library also tries to fill a gap in features and functionalities by | ||||
| making available some containers initially developed for internal use. | ||||
|  | ||||
| This section of the library is likely to grow larger over time. However, for the | ||||
| @@ -40,7 +39,7 @@ The implementation is based on _sparse sets_ and each bucket is identified by an | ||||
| implicit list within the packed array itself. | ||||
|  | ||||
| The interface is very close to its counterpart in the standard library, that is, | ||||
| `std::unordered_map`.<br/> | ||||
| the `std::unordered_map` class.<br/> | ||||
| However, both local and non-local iterators returned by a dense map belong to | ||||
| the input iterator category although they respectively model the concepts of a | ||||
| _forward iterator_ type and a _random access iterator_ type.<br/> | ||||
| @@ -63,5 +62,5 @@ The implementation is based on _sparse sets_ and each bucket is identified by an | ||||
| implicit list within the packed array itself. | ||||
|  | ||||
| The interface is in all respects similar to its counterpart in the standard | ||||
| library, that is, `std::unordered_set`.<br/> | ||||
| library, that is, the `std::unordered_set` class.<br/> | ||||
| Therefore, there is no need to go into the API description. | ||||
|   | ||||
							
								
								
									
										279
									
								
								external/entt/entt/docs/md/core.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										279
									
								
								external/entt/entt/docs/md/core.md
									
									
									
									
										vendored
									
									
								
							| @@ -46,25 +46,24 @@ | ||||
| # Introduction | ||||
|  | ||||
| `EnTT` comes with a bunch of core functionalities mostly used by the other parts | ||||
| of the library itself.<br/> | ||||
| Hardly users will include these features in their code, but it's worth | ||||
| describing what `EnTT` offers so as not to reinvent the wheel in case of need. | ||||
| of the library.<br/> | ||||
| Many of these tools are also useful in everyday work. Therefore, it's worth | ||||
| describing them so as not to reinvent the wheel in case of need. | ||||
|  | ||||
| # Any as in any type | ||||
|  | ||||
| `EnTT` comes with its own `any` type. It may seem redundant considering that | ||||
| C++17 introduced `std::any`, but it is not (hopefully).<br/> | ||||
| `EnTT` offers its own `any` type. It may seem redundant considering that C++17 | ||||
| introduced `std::any`, but it is not (hopefully).<br/> | ||||
| First of all, the _type_ returned by an `std::any` is a const reference to an | ||||
| `std::type_info`, an implementation defined class that's not something everyone | ||||
| wants to see in a software. Furthermore, there is no way to connect it with the | ||||
| type system of the library and therefore with its integrated RTTI support.<br/> | ||||
| Note that this class is largely used internally by the library itself. | ||||
| wants to see in a software. Furthermore, there is no way to bind it to the type | ||||
| system of the library and therefore with its integrated RTTI support. | ||||
|  | ||||
| The API is very similar to that of its most famous counterpart, mainly because | ||||
| this class serves the same purpose of being an opaque container for any type of | ||||
| value.<br/> | ||||
| Instances of `any` also minimize the number of allocations by relying on a well | ||||
| known technique called _small buffer optimization_ and a fake vtable. | ||||
| The `any` API is very similar to that of its most famous counterpart, mainly | ||||
| because this class serves the same purpose of being an opaque container for any | ||||
| type of value.<br/> | ||||
| Instances also minimize the number of allocations by relying on a well known | ||||
| technique called _small buffer optimization_ and a fake vtable. | ||||
|  | ||||
| Creating an object of the `any` type, whether empty or not, is trivial: | ||||
|  | ||||
| @@ -93,8 +92,8 @@ Furthermore, an instance of `any` isn't tied to an actual type. Therefore, the | ||||
| wrapper is reconfigured when it's assigned a new object of a type other than | ||||
| the one it contains. | ||||
|  | ||||
| There exists also a way to directly assign a value to the variable contained by | ||||
| an `entt::any`, without necessarily replacing it. This is especially useful when | ||||
| There is also a way to directly assign a value to the variable contained by an | ||||
| `entt::any`, without necessarily replacing it. This is especially useful when | ||||
| the object is used in _aliasing mode_, as described below: | ||||
|  | ||||
| ```cpp | ||||
| @@ -108,15 +107,15 @@ any.assign(value); | ||||
| any.assign(std::move(value)); | ||||
| ``` | ||||
|  | ||||
| The `any` class will also perform a check on the type information and whether or | ||||
| not the original type was copy or move assignable, as appropriate.<br/> | ||||
| In all cases, the `assign` function returns a boolean value to indicate the | ||||
| success or failure of the operation. | ||||
| The `any` class performs a check on the type information and whether or not the | ||||
| original type was copy or move assignable, as appropriate.<br/> | ||||
| In all cases, the `assign` function returns a boolean value that is true in case | ||||
| of success and false otherwise. | ||||
|  | ||||
| When in doubt about the type of object contained, the `type` member function of | ||||
| `any` returns a const reference to the `type_info` associated with its element, | ||||
| or `type_id<void>()` if the container is empty. The type is also used internally | ||||
| when comparing two `any` objects: | ||||
| When in doubt about the type of object contained, the `type` member function | ||||
| returns a const reference to the `type_info` associated with its element, or | ||||
| `type_id<void>()` if the container is empty.<br/> | ||||
| The type is also used internally when comparing two `any` objects: | ||||
|  | ||||
| ```cpp | ||||
| if(any == empty) { /* ... */ } | ||||
| @@ -125,7 +124,7 @@ if(any == empty) { /* ... */ } | ||||
| In this case, before proceeding with a comparison, it's verified that the _type_ | ||||
| of the two objects is actually the same.<br/> | ||||
| Refer to the `EnTT` type system documentation for more details about how | ||||
| `type_info` works and on possible risks of a comparison. | ||||
| `type_info` works and the possible risks of a comparison. | ||||
|  | ||||
| A particularly interesting feature of this class is that it can also be used as | ||||
| an opaque container for const and non-const references: | ||||
| @@ -153,22 +152,19 @@ entt::any ref = other.as_ref(); | ||||
| ``` | ||||
|  | ||||
| In this case, it doesn't matter if the original container actually holds an | ||||
| object or acts already as a reference for unmanaged elements, the new instance | ||||
| thus created won't create copies and will only serve as a reference for the | ||||
| original item.<br/> | ||||
| This means that, starting from the example above, both `ref` and `other` will | ||||
| point to the same object, whether it's initially contained in `other` or already | ||||
| an unmanaged element. | ||||
| object or is as a reference for unmanaged elements already. The new instance | ||||
| thus created doesn't create copies and only serves as a reference for the | ||||
| original item. | ||||
|  | ||||
| As a side note, it's worth mentioning that, while everything works transparently | ||||
| when it comes to non-const references, there are some exceptions when it comes | ||||
| to const references.<br/> | ||||
| It's worth mentioning that, while everything works transparently when it comes | ||||
| to non-const references, there are some exceptions when it comes to const | ||||
| references.<br/> | ||||
| In particular, the `data` member function invoked on a non-const instance of | ||||
| `any` that wraps a const reference will return a null pointer in all cases. | ||||
| `any` that wraps a const reference returns a null pointer in all cases. | ||||
|  | ||||
| To cast an instance of `any` to a type, the library offers a set of `any_cast` | ||||
| functions in all respects similar to their most famous counterparts.<br/> | ||||
| The only difference is that, in the case of `EnTT`, these won't raise exceptions | ||||
| The only difference is that, in the case of `EnTT`, they won't raise exceptions | ||||
| but will only trigger an assert in debug mode, otherwise resulting in undefined | ||||
| behavior in case of misuse in release mode. | ||||
|  | ||||
| @@ -188,31 +184,23 @@ using my_any = entt::basic_any<sizeof(double[4])>; | ||||
| This feature, in addition to allowing the choice of a size that best suits the | ||||
| needs of an application, also offers the possibility of forcing dynamic creation | ||||
| of objects during construction.<br/> | ||||
| In other terms, if the size is 0, `any` avoids the use of any optimization and | ||||
| always dynamically allocates objects (except for aliasing cases). | ||||
|  | ||||
| Note that the size of the internal storage as well as the alignment requirements | ||||
| are directly part of the type and therefore contribute to define different types | ||||
| that won't be able to interoperate with each other. | ||||
| In other terms, if the size is 0, `any` suppresses the small buffer optimization | ||||
| and always dynamically allocates objects (except for aliasing cases). | ||||
|  | ||||
| ## Alignment requirement | ||||
|  | ||||
| The alignment requirement is optional and by default the most stringent (the | ||||
| largest) for any object whose size is at most equal to the one provided.<br/> | ||||
| The `basic_any` class template inspects the alignment requirements in each case, | ||||
| even when not provided and may decide not to use the small buffer optimization | ||||
| in order to meet them. | ||||
|  | ||||
| The alignment requirement is provided as an optional second parameter following | ||||
| the desired size for the internal storage: | ||||
| It's provided as an optional second parameter following the desired size for the | ||||
| internal storage: | ||||
|  | ||||
| ```cpp | ||||
| using my_any = entt::basic_any<sizeof(double[4]), alignof(double[4])>; | ||||
| ``` | ||||
|  | ||||
| Note that the alignment requirements as well as the size of the internal storage | ||||
| are directly part of the type and therefore contribute to define different types | ||||
| that won't be able to interoperate with each other. | ||||
| 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. | ||||
|  | ||||
| # Compressed pair | ||||
|  | ||||
| @@ -225,8 +213,8 @@ is more important than having some cool and probably useless feature. | ||||
|  | ||||
| Although the API is very close to that of `std::pair` (apart from the fact that | ||||
| the template parameters are inferred from the constructor and therefore there is | ||||
| no` entt::make_compressed_pair`), the major difference is that `first` and | ||||
| `second` are functions for implementation needs: | ||||
| no `entt::make_compressed_pair`), the major difference is that `first` and | ||||
| `second` are functions for implementation requirements: | ||||
|  | ||||
| ```cpp | ||||
| entt::compressed_pair pair{0, 3.}; | ||||
| @@ -239,16 +227,15 @@ intuition. At the end of the day, it's just a pair and nothing more. | ||||
| # Enum as bitmask | ||||
|  | ||||
| Sometimes it's useful to be able to use enums as bitmasks. However, enum classes | ||||
| aren't really suitable for the purpose out of the box. Main problem is that they | ||||
| don't convert implicitly to their underlying type.<br/> | ||||
| All that remains is to make a choice between using old-fashioned enums (with all | ||||
| their problems that I don't want to discuss here) or writing _ugly_ code. | ||||
| aren't really suitable for the purpose. Main problem is that they don't convert | ||||
| implicitly to their underlying type.<br/> | ||||
| The choice is then between using old-fashioned enums (with all their problems | ||||
| that I don't want to discuss here) or writing _ugly_ code. | ||||
|  | ||||
| Fortunately, there is also a third way: adding enough operators in the global | ||||
| scope to treat enum classes as bitmask transparently.<br/> | ||||
| The ultimate goal is to be able to write code like the following (or maybe | ||||
| something more meaningful, but this should give a grasp and remain simple at the | ||||
| same time): | ||||
| scope to treat enum classes as bitmasks transparently.<br/> | ||||
| The ultimate goal is to write code like the following (or maybe something more | ||||
| meaningful, but this should give a grasp and remain simple at the same time): | ||||
|  | ||||
| ```cpp | ||||
| enum class my_flag { | ||||
| @@ -261,11 +248,11 @@ const my_flag flags = my_flag::enabled; | ||||
| const bool is_enabled = !!(flags & my_flag::enabled); | ||||
| ``` | ||||
|  | ||||
| The problem with adding all operators to the global scope is that these will | ||||
| come into play even when not required, with the risk of introducing errors that | ||||
| are difficult to deal with.<br/> | ||||
| The problem with adding all operators to the global scope is that these come | ||||
| into play even when not required, with the risk of introducing errors that are | ||||
| difficult to deal with.<br/> | ||||
| However, C++ offers enough tools to get around this problem. In particular, the | ||||
| library requires users to register all enum classes for which bitmask support | ||||
| library requires users to register the enum classes for which bitmask support | ||||
| should be enabled: | ||||
|  | ||||
| ```cpp | ||||
| @@ -276,7 +263,7 @@ struct entt::enum_as_bitmask<my_flag> | ||||
| ``` | ||||
|  | ||||
| This is handy when dealing with enum classes defined by third party libraries | ||||
| and over which the users have no control. However, it's also verbose and can be | ||||
| and over which the user has no control. However, it's also verbose and can be | ||||
| avoided by adding a specific value to the enum class itself: | ||||
|  | ||||
| ```cpp | ||||
| @@ -289,23 +276,21 @@ enum class my_flag { | ||||
| ``` | ||||
|  | ||||
| In this case, there is no need to specialize the `enum_as_bitmask` traits, since | ||||
| `EnTT` will automatically detect the flag and enable the bitmask support.<br/> | ||||
| Once the enum class has been registered (in one way or the other) all the most | ||||
| common operators will be available, such as `&`, `|` but also `&=` and `|=`. | ||||
| `EnTT` automatically detects the flag and enables the bitmask support.<br/> | ||||
| Once the enum class is registered (in one way or the other), the most common | ||||
| operators such as `&`, `|` but also `&=` and `|=` are available for use. | ||||
|  | ||||
| Refer to the official documentation for the full list of operators. | ||||
|  | ||||
| # Hashed strings | ||||
|  | ||||
| A hashed string is a zero overhead unique identifier. Users can use | ||||
| human-readable identifiers in the codebase while using their numeric | ||||
| counterparts at runtime, thus without affecting performance.<br/> | ||||
| Hashed strings are human-readable identifiers in the codebase that turn into | ||||
| numeric values at runtime, thus without affecting performance.<br/> | ||||
| The class has an implicit `constexpr` constructor that chews a bunch of | ||||
| characters. Once created, all what one can do with it is getting back the | ||||
| original string through the `data` member function or converting the instance | ||||
| into a number.<br/> | ||||
| The good part is that a hashed string can be used wherever a constant expression | ||||
| is required and no _string-to-number_ conversion will take place at runtime if | ||||
| used carefully. | ||||
| characters. Once created, one can get the original string by means of the `data` | ||||
| member function or convert the instance into a number.<br/> | ||||
| A hashed string is well suited wherever a constant expression is required. No | ||||
| _string-to-number_ conversion will take place at runtime if used carefully. | ||||
|  | ||||
| Example of use: | ||||
|  | ||||
| @@ -318,19 +303,18 @@ auto resource = load(entt::hashed_string{"gui/background"}); | ||||
| ``` | ||||
|  | ||||
| There is also a _user defined literal_ dedicated to hashed strings to make them | ||||
| more user-friendly: | ||||
| more _user-friendly_: | ||||
|  | ||||
| ```cpp | ||||
| using namespace entt::literals; | ||||
| constexpr auto str = "text"_hs; | ||||
| ``` | ||||
|  | ||||
| To use it, remember that all user defined literals in `EnTT` are enclosed in the | ||||
| `entt::literals` namespace. Therefore, the entire namespace or selectively the | ||||
| literal of interest must be explicitly included before each use, a bit like | ||||
| `std::literals`.<br/> | ||||
| Finally, in case users need to create hashed strings at runtime, this class also | ||||
| offers the necessary functionalities: | ||||
| User defined literals in `EnTT` are enclosed in the `entt::literals` namespace. | ||||
| Therefore, the entire namespace or selectively the literal of interest must be | ||||
| explicitly included before each use, a bit like `std::literals`.<br/> | ||||
| The class also offers the necessary functionalities to create hashed strings at | ||||
| runtime: | ||||
|  | ||||
| ```cpp | ||||
| std::string orig{"text"}; | ||||
| @@ -343,16 +327,14 @@ const auto hash = entt::hashed_string::value(orig.c_str()); | ||||
| ``` | ||||
|  | ||||
| This possibility shouldn't be exploited in tight loops, since the computation | ||||
| takes place at runtime and no longer at compile-time and could therefore impact | ||||
| takes place at runtime and no longer at compile-time. It could therefore affect | ||||
| performance to some degrees. | ||||
|  | ||||
| ## Wide characters | ||||
|  | ||||
| The hashed string has a design that is close to that of an `std::basic_string`. | ||||
| It means that `hashed_string` is nothing more than an alias for | ||||
| `basic_hashed_string<char>`. For those who want to use the C++ type for wide | ||||
| character representation, there exists also the alias `hashed_wstring` for | ||||
| `basic_hashed_string<wchar_t>`.<br/> | ||||
| The `hashed_string` class is an alias  for `basic_hashed_string<char>`. To use | ||||
| the C++ type for wide character representations, there exists also the alias | ||||
| `hashed_wstring` for `basic_hashed_string<wchar_t>`.<br/> | ||||
| In this case, the user defined literal to use to create hashed strings on the | ||||
| fly is `_hws`: | ||||
|  | ||||
| @@ -360,16 +342,15 @@ fly is `_hws`: | ||||
| constexpr auto str = L"text"_hws; | ||||
| ``` | ||||
|  | ||||
| Note that the hash type of the `hashed_wstring` is the same of its counterpart. | ||||
| The hash type of `hashed_wstring` is the same as its counterpart. | ||||
|  | ||||
| ## Conflicts | ||||
|  | ||||
| The hashed string class uses internally FNV-1a to compute the numeric | ||||
| counterpart of a string. Because of the _pigeonhole principle_, conflicts are | ||||
| possible. This is a fact.<br/> | ||||
| The hashed string class uses FNV-1a internally to hash strings. Because of the | ||||
| _pigeonhole principle_, conflicts are possible. This is a fact.<br/> | ||||
| There is no silver bullet to solve the problem of conflicts when dealing with | ||||
| hashing functions. In this case, the best solution seemed to be to give up. | ||||
| That's all.<br/> | ||||
| hashing functions. In this case, the best solution is likely to give up. That's | ||||
| all.<br/> | ||||
| After all, human-readable unique identifiers aren't something strictly defined | ||||
| and over which users have not the control. Choosing a slightly different | ||||
| identifier is probably the best solution to make the conflict disappear in this | ||||
| @@ -377,8 +358,8 @@ case. | ||||
|  | ||||
| # Iterators | ||||
|  | ||||
| Writing and working with iterators isn't always easy and more often than not | ||||
| leads to duplicated code.<br/> | ||||
| Writing and working with iterators isn't always easy. More often than not it | ||||
| also leads to duplicated code.<br/> | ||||
| `EnTT` tries to overcome this problem by offering some utilities designed to | ||||
| make this hard work easier. | ||||
|  | ||||
| @@ -431,7 +412,7 @@ user. | ||||
| ## Iterable adaptor | ||||
|  | ||||
| Typically, a container class provides `begin` and `end` member functions (with | ||||
| their const counterparts) to be iterated by the user.<br/> | ||||
| their const counterparts) for iteration.<br/> | ||||
| However, it can happen that a class offers multiple iteration methods or allows | ||||
| users to iterate different sets of _elements_. | ||||
|  | ||||
| @@ -452,8 +433,8 @@ by returning an iterable object for the purpose. | ||||
| There are a handful of tools within `EnTT` to interact with memory in one way or | ||||
| another.<br/> | ||||
| Some are geared towards simplifying the implementation of (internal or external) | ||||
| allocator aware containers. Others, on the other hand, are designed to help the | ||||
| developer with everyday problems. | ||||
| allocator aware containers. Others are designed to help the developer with | ||||
| everyday problems. | ||||
|  | ||||
| The former are very specific and for niche problems. These are tools designed to | ||||
| unwrap fancy or plain pointers (`to_address`) or to help forget the meaning of | ||||
| @@ -481,7 +462,7 @@ modulus and for this reason preferred in many areas. | ||||
|  | ||||
| A nasty thing in C++ (at least up to C++20) is the fact that shared pointers | ||||
| support allocators while unique pointers don't.<br/> | ||||
| There is a proposal at the moment that also shows among the other things how | ||||
| There is a proposal at the moment that also shows (among the other things) how | ||||
| this can be implemented without any compiler support. | ||||
|  | ||||
| The `allocate_unique` function follows this proposal, making a virtue out of | ||||
| @@ -498,13 +479,16 @@ the same feature. | ||||
| # Monostate | ||||
|  | ||||
| The monostate pattern is often presented as an alternative to a singleton based | ||||
| configuration system. This is exactly its purpose in `EnTT`. Moreover, this | ||||
| implementation is thread safe by design (hopefully).<br/> | ||||
| Keys are represented by hashed strings, values are basic types like `int`s or | ||||
| `bool`s. Values of different types can be associated to each key, even more than | ||||
| one at a time. Because of this, users must pay attention to use the same type | ||||
| both during an assignment and when they try to read back their data. Otherwise, | ||||
| they will probably incur in unexpected results. | ||||
| configuration system.<br/> | ||||
| This is exactly its purpose in `EnTT`. Moreover, this implementation is thread | ||||
| safe by design (hopefully). | ||||
|  | ||||
| Keys are integral values (easily obtained by hashed strings), values are basic | ||||
| types like `int`s or `bool`s. Values of different types can be associated with | ||||
| each key, even more than one at a time.<br/> | ||||
| Because of this, one should pay attention to use the same type both during an | ||||
| assignment and when trying to read back the data. Otherwise, there is the risk | ||||
| to incur in unexpected results. | ||||
|  | ||||
| Example of use: | ||||
|  | ||||
| @@ -542,17 +526,10 @@ Basically, the whole system relies on a handful of classes. In particular: | ||||
|   auto index = entt::type_index<a_type>::value(); | ||||
|   ``` | ||||
|  | ||||
|   The returned value isn't guaranteed to be stable across different runs. | ||||
|   The returned value isn't guaranteed to be stable across different runs.<br/> | ||||
|   However, it can be very useful as index in associative and unordered | ||||
|   associative containers or for positional accesses in a vector or an array. | ||||
|    | ||||
|   So as not to conflict with the other tools available, the `family` class isn't | ||||
|   used to generate these indexes. Therefore, the numeric identifiers returned by | ||||
|   the two tools may differ.<br/> | ||||
|   On the other hand, this leaves users with full powers over the `family` class | ||||
|   and therefore the generation of custom runtime sequences of indices for their | ||||
|   own purposes, if necessary. | ||||
|    | ||||
|   An external generator can also be used if needed. In fact, `type_index` can be | ||||
|   specialized by type and is also _sfinae-friendly_ in order to allow more | ||||
|   refined specializations such as: | ||||
| @@ -566,7 +543,7 @@ Basically, the whole system relies on a handful of classes. In particular: | ||||
|   }; | ||||
|   ``` | ||||
|    | ||||
|   Note that indexes **must** still be generated sequentially in this case.<br/> | ||||
|   Indexes **must** be sequentially generated in this case.<br/> | ||||
|   The tool is widely used within `EnTT`. Generating indices not sequentially | ||||
|   would break an assumption and would likely lead to undesired behaviors. | ||||
|  | ||||
| @@ -583,14 +560,14 @@ Basically, the whole system relies on a handful of classes. In particular: | ||||
|   This function **can** use non-standard features of the language for its own | ||||
|   purposes. This makes it possible to provide compile-time identifiers that | ||||
|   remain stable across different runs.<br/> | ||||
|   In all cases, users can prevent the library from using these features by means | ||||
|   of the `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee | ||||
|   that identifiers remain stable across executions. Moreover, they are generated | ||||
|   Users can prevent the library from using these features by means of the | ||||
|   `ENTT_STANDARD_CPP` definition. In this case, there is no guarantee that | ||||
|   identifiers remain stable across executions. Moreover, they are generated | ||||
|   at runtime and are no longer a compile-time thing. | ||||
|  | ||||
|   As for `type_index`, also `type_hash` is a _sfinae-friendly_ class that can be | ||||
|   specialized in order to customize its behavior globally or on a per-type or | ||||
|   per-traits basis. | ||||
|   As it happens with `type_index`, also `type_hash` is a _sfinae-friendly_ class | ||||
|   that can be specialized in order to customize its behavior globally or on a | ||||
|   per-type or per-traits basis. | ||||
|  | ||||
| * The name associated with a given type: | ||||
|  | ||||
| @@ -598,10 +575,9 @@ Basically, the whole system relies on a handful of classes. In particular: | ||||
|   auto name = entt::type_name<a_type>::value(); | ||||
|   ``` | ||||
|  | ||||
|   The name associated with a type is extracted from some information generally | ||||
|   made available by the compiler in use. Therefore, it may differ depending on | ||||
|   the compiler and may be empty in the event that this information isn't | ||||
|   available.<br/> | ||||
|   This value is extracted from some information generally made available by the | ||||
|   compiler in use. Therefore, it may differ depending on the compiler and may be | ||||
|   empty in the event that this information isn't available.<br/> | ||||
|   For example, given the following class: | ||||
|  | ||||
|   ```cpp | ||||
| @@ -612,21 +588,20 @@ Basically, the whole system relies on a handful of classes. In particular: | ||||
|   when MSVC is in use.<br/> | ||||
|   Most of the time the name is also retrieved at compile-time and is therefore | ||||
|   always returned through an `std::string_view`. Users can easily access it and | ||||
|   modify it as needed, for example by removing the word `struct` to standardize | ||||
|   the result. `EnTT` won't do this for obvious reasons, since it requires | ||||
|   copying and creating a new string potentially at runtime. | ||||
|   modify it as needed, for example by removing the word `struct` to normalize | ||||
|   the result. `EnTT` doesn't do this for obvious reasons, since it would be | ||||
|   creating a new string at runtime otherwise. | ||||
|  | ||||
|   This function **can** use non-standard features of the language for its own | ||||
|   purposes. Users can prevent the library from using non-standard features by | ||||
|   means of the `ENTT_STANDARD_CPP` definition. In this case, the name will be | ||||
|   empty by default. | ||||
|   purposes. Users can prevent the library from using these features by means of | ||||
|   the `ENTT_STANDARD_CPP` definition. In this case, the name is just empty. | ||||
|  | ||||
|   As for `type_index`, also `type_name` is a _sfinae-friendly_ class that can be | ||||
|   specialized in order to customize its behavior globally or on a per-type or | ||||
|   per-traits basis. | ||||
|   As it happens with `type_index`, also `type_name` is a _sfinae-friendly_ class | ||||
|   that can be specialized in order to customize its behavior globally or on a | ||||
|   per-type or per-traits basis. | ||||
|  | ||||
| These are then combined into utilities that aim to offer an API that is somewhat | ||||
| similar to that offered by the language. | ||||
| similar to that made available by the standard library. | ||||
|  | ||||
| ### Type info | ||||
|  | ||||
| @@ -708,8 +683,8 @@ In fact, although this is quite rare, it's not entirely excluded. | ||||
|  | ||||
| Another case where two types are assigned the same identifier is when classes | ||||
| from different contexts (for example two or more libraries loaded at runtime) | ||||
| have the same fully qualified name. In this case, also `type_name` will return | ||||
| the same value for the two types.<br/> | ||||
| have the same fully qualified name. In this case, `type_name` returns the same | ||||
| value for the two types.<br/> | ||||
| Fortunately, there are several easy ways to deal with this: | ||||
|  | ||||
| * The most trivial one is to define the `ENTT_STANDARD_CPP` macro. Runtime | ||||
| @@ -739,9 +714,9 @@ offered by this module. | ||||
|  | ||||
| ### Size of | ||||
|  | ||||
| The standard operator `sizeof` complains when users provide it for example with | ||||
| function or incomplete types. On the other hand, it's guaranteed that its result | ||||
| is always nonzero, even if applied to an empty class type.<br/> | ||||
| The standard operator `sizeof` complains if users provide it with functions or | ||||
| incomplete types. On the other hand, it's guaranteed that its result is always | ||||
| non-zero, even if applied to an empty class type.<br/> | ||||
| This small class combines the two and offers an alternative to `sizeof` that | ||||
| works under all circumstances, returning zero if the type isn't supported: | ||||
|  | ||||
| @@ -777,8 +752,8 @@ A utility to easily transfer the constness of a type to another type: | ||||
| using type = entt::constness_as_t<dst_type, const src_type>; | ||||
| ``` | ||||
|  | ||||
| The trait is subject to the rules of the language. Therefore, for example, | ||||
| transferring constness between references won't give the desired effect. | ||||
| The trait is subject to the rules of the language. For example, _transferring_ | ||||
| constness between references won't give the desired effect. | ||||
|  | ||||
| ### Member class type | ||||
|  | ||||
| @@ -798,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::nt_argument_t<1u, &clazz::member>; | ||||
| using type = entt::nth_argument_t<1u, &clazz::member>; | ||||
| ``` | ||||
|  | ||||
| Disambiguation of overloaded functions is the responsibility of the user, should | ||||
| @@ -825,8 +800,8 @@ registry.emplace<enemy_tag>(entity); | ||||
|  | ||||
| ### Tag | ||||
|  | ||||
| Since `id_type` is very important and widely used in `EnTT`, there is a more | ||||
| user-friendly shortcut for the creation of integral constants based on it.<br/> | ||||
| Type `id_type` is very important and widely used in `EnTT`. Therefore, there is | ||||
| a more user-friendly shortcut for the creation of constants based on it.<br/> | ||||
| This shortcut is the alias template `entt::tag`. | ||||
|  | ||||
| If used in combination with hashed strings, it helps to use human-readable names | ||||
| @@ -918,8 +893,8 @@ Perhaps a bit ugly to see in a codebase but it gets the job done at least. | ||||
|  | ||||
| ## Runtime generator | ||||
|  | ||||
| To generate sequential numeric identifiers at runtime, `EnTT` offers the | ||||
| `family` class template: | ||||
| The `family` class template helps to generate sequential numeric identifiers for | ||||
| types at runtime: | ||||
|  | ||||
| ```cpp | ||||
| // defines a custom generator | ||||
| @@ -936,8 +911,8 @@ numeric identifier for the given type.<br/> | ||||
| The generator is customizable, so as to get different _sequences_ for different | ||||
| purposes if needed. | ||||
|  | ||||
| Please, note that identifiers aren't guaranteed to be stable across different | ||||
| runs. Indeed it mostly depends on the flow of execution. | ||||
| Identifiers aren't guaranteed to be stable across different runs. Indeed it | ||||
| mostly depends on the flow of execution. | ||||
|  | ||||
| # Utilities | ||||
|  | ||||
|   | ||||
							
								
								
									
										1512
									
								
								external/entt/entt/docs/md/entity.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1512
									
								
								external/entt/entt/docs/md/entity.md
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								external/entt/entt/docs/md/faq.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								external/entt/entt/docs/md/faq.md
									
									
									
									
										vendored
									
									
								
							| @@ -193,7 +193,7 @@ to mitigate the problem makes it manageable. | ||||
|  | ||||
| ## Which functions trigger which signals | ||||
|  | ||||
| The `registry` class offers three signals that are emitted following specific | ||||
| Storage classes offer three _signals_ that are emitted following specific | ||||
| operations. Maybe not everyone knows what these operations are, though.<br/> | ||||
| If this isn't clear, below you can find a _vademecum_ for this purpose: | ||||
|  | ||||
|   | ||||
							
								
								
									
										175
									
								
								external/entt/entt/docs/md/graph.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										175
									
								
								external/entt/entt/docs/md/graph.md
									
									
									
									
										vendored
									
									
								
							| @@ -14,7 +14,6 @@ | ||||
|   * [Fake resources and order of execution](#fake-resources-and-order-of-execution) | ||||
|   * [Sync points](#sync-points) | ||||
|   * [Execution graph](#execution-graph) | ||||
|  | ||||
| <!-- | ||||
| @endcond TURN_OFF_DOXYGEN | ||||
| --> | ||||
| @@ -23,15 +22,15 @@ | ||||
|  | ||||
| `EnTT` doesn't aim to offer everything one needs to work with graphs. Therefore, | ||||
| anyone looking for this in the _graph_ submodule will be disappointed.<br/> | ||||
| Quite the opposite is true. This submodule is minimal and contains only the data | ||||
| structures and algorithms strictly necessary for the development of some tools | ||||
| such as the _flow builder_. | ||||
| Quite the opposite is true though. This submodule is minimal and contains only | ||||
| the data structures and algorithms strictly necessary for the development of | ||||
| some tools such as the _flow builder_. | ||||
|  | ||||
| # Data structures | ||||
|  | ||||
| As anticipated in the introduction, the aim isn't to offer all possible data | ||||
| structures suitable for representing and working with graphs. Many will likely | ||||
| be added or refined over time, however I want to discourage anyone expecting | ||||
| be added or refined over time. However I want to discourage anyone expecting | ||||
| tight scheduling on the subject.<br/> | ||||
| The data structures presented in this section are mainly useful for the | ||||
| development and support of some tools which are also part of the same submodule. | ||||
| @@ -49,7 +48,7 @@ The `directed_tag` type _creates_ the graph as directed. There is also an | ||||
| `undirected_tag` counterpart which creates it as undirected.<br/> | ||||
| The interface deviates slightly from the typical double indexing of C and offers | ||||
| an API that is perhaps more familiar to a C++ programmer. Therefore, the access | ||||
| and modification of an element will take place via the `contains`, `insert` and | ||||
| and modification of an element takes place via the `contains`, `insert` and | ||||
| `erase` functions rather than a double call to an `operator[]`: | ||||
|  | ||||
| ```cpp | ||||
| @@ -60,14 +59,14 @@ if(adjacency_matrix.contains(0u, 1u)) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Both `insert` and` erase` are idempotent functions which have no effect if the | ||||
| Both `insert` and` erase` are _idempotent_ functions which have no effect if the | ||||
| element already exists or has already been deleted.<br/> | ||||
| The first one returns an `std::pair` containing the iterator to the element and | ||||
| a boolean value indicating whether the element has been inserted or was already | ||||
| present. The second one instead returns the number of deleted elements (0 or 1). | ||||
| a boolean value indicating whether the element was newly inserted or not. The | ||||
| second one returns the number of deleted elements (0 or 1). | ||||
|  | ||||
| An adjacency matrix must be initialized with the number of elements (vertices) | ||||
| when constructing it but can also be resized later using the `resize` function: | ||||
| An adjacency matrix is initialized with the number of elements (vertices) when | ||||
| constructing it but can also be resized later using the `resize` function: | ||||
|  | ||||
| ```cpp | ||||
| entt::adjacency_matrix<entt::directed_tag> adjacency_matrix{3u}; | ||||
| @@ -82,8 +81,8 @@ for(auto &&vertex: adjacency_matrix.vertices()) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Note that the same result can be obtained with the following snippet, since the | ||||
| vertices are unsigned integral values: | ||||
| The same result is obtained with the following snippet, since the vertices are | ||||
| plain unsigned integral values: | ||||
|  | ||||
| ```cpp | ||||
| for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) { | ||||
| @@ -93,8 +92,8 @@ for(auto last = adjacency_matrix.size(), pos = {}; pos < last; ++pos) { | ||||
|  | ||||
| As for visiting the edges, a few functions are available.<br/> | ||||
| When the purpose is to visit all the edges of a given adjacency matrix, the | ||||
| `edges` function returns an iterable object that can be used to get them as | ||||
| pairs of vertices: | ||||
| `edges` function returns an iterable object that is used to get them as pairs of | ||||
| vertices: | ||||
|  | ||||
| ```cpp | ||||
| for(auto [lhs, rhs]: adjacency_matrix.edges()) { | ||||
| @@ -102,8 +101,8 @@ for(auto [lhs, rhs]: adjacency_matrix.edges()) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| On the other hand, if the goal is to visit all the in- or out-edges of a given | ||||
| vertex, the `in_edges` and `out_edges` functions are meant for that: | ||||
| If the goal is to visit all the in- or out-edges of a given vertex instead, the | ||||
| `in_edges` and `out_edges` functions are meant for that: | ||||
|  | ||||
| ```cpp | ||||
| for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) { | ||||
| @@ -111,11 +110,11 @@ for(auto [lhs, rhs]: adjacency_matrix.out_edges(3u)) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| As might be expected, these functions expect the vertex to visit (that is, to | ||||
| return the in- or out-edges for) as an argument.<br/> | ||||
| Both the functions expect the vertex to visit (that is, to return the in- or | ||||
| out-edges for) as an argument.<br/> | ||||
| Finally, the adjacency matrix is an allocator-aware container and offers most of | ||||
| the functionality one would expect from this type of containers, such as `clear` | ||||
| or 'get_allocator` and so on. | ||||
| the functionalities one would expect from this type of containers, such as | ||||
| `clear` or 'get_allocator` and so on. | ||||
|  | ||||
| ## Graphviz dot language | ||||
|  | ||||
| @@ -129,19 +128,19 @@ std::ostringstream output{}; | ||||
| entt::dot(output, adjacency_matrix); | ||||
| ``` | ||||
|  | ||||
| However, there is also the option of providing a callback to which the vertices | ||||
| are passed and which can be used to add (`dot`) properties to the output from | ||||
| time to time: | ||||
| It's also possible to provide a callback to which the vertices are passed and | ||||
| which can be used to add (`dot`) properties to the output as needed: | ||||
|  | ||||
| ```cpp | ||||
| std::ostringstream output{}; | ||||
|  | ||||
| entt::dot(output, adjacency_matrix, [](auto &output, auto vertex) { | ||||
|     out << "label=\"v\"" << vertex << ",shape=\"box\""; | ||||
| }); | ||||
| ``` | ||||
|  | ||||
| This second mode is particularly convenient when the user wants to associate | ||||
| data managed externally to the graph being converted. | ||||
| externally managed data to the graph being converted. | ||||
|  | ||||
| # Flow builder | ||||
|  | ||||
| @@ -155,42 +154,42 @@ specified.<br/> | ||||
| Most of the functions in the API also return the flow builder itself, according | ||||
| to what is the common sense API when it comes to builder classes. | ||||
|  | ||||
| Once all tasks have been registered and resources assigned to them, an execution | ||||
| graph in the form of an adjacency matrix is returned to the user.<br/> | ||||
| Once all tasks are registered and resources assigned to them, an execution graph | ||||
| in the form of an adjacency matrix is returned to the user.<br/> | ||||
| This graph contains all the tasks assigned to the flow builder in the form of | ||||
| _vertices_. The _vertex_ itself can be used as an index to get the identifier | ||||
| passed during registration. | ||||
| _vertices_. The _vertex_ itself is used as an index to get the identifier passed | ||||
| during registration. | ||||
|  | ||||
| ## Tasks and resources | ||||
|  | ||||
| Although these terms are used extensively in the documentation, the flow builder | ||||
| has no real concept of tasks and resources.<br/> | ||||
| This class works mainly with _identifiers_, that is, values of type `id_type`. | ||||
| That is, both tasks and resources are identified by integral values.<br/> | ||||
| In other terms, both tasks and resources are identified by integral values.<br/> | ||||
| This allows not to couple the class itself to the rest of the library or to any | ||||
| particular data structure. On the other hand, it requires the user to keep track | ||||
| of the association between identifiers and operations or actual data. | ||||
|  | ||||
| Once a flow builder has been created (which requires no constructor arguments), | ||||
| the first thing to do is to bind a task. This will indicate to the builder who | ||||
| intends to consume the resources that will be specified immediately after: | ||||
| Once a flow builder is created (which requires no constructor arguments), the | ||||
| first thing to do is to bind a task. This tells to the builder _who_ intends to | ||||
| consume the resources that are specified immediately after: | ||||
|  | ||||
| ```cpp | ||||
| entt::flow builder{}; | ||||
| builder.bind("task_1"_hs); | ||||
| ``` | ||||
|  | ||||
| Note that the example uses the `EnTT` hashed string to generate an identifier | ||||
| for the task.<br/> | ||||
| Indeed, the use of `id_type` as an identifier type is not by accident. In fact, | ||||
| The example uses the `EnTT` hashed string to generate an identifier for the | ||||
| task.<br/> | ||||
| Indeed, the use of `id_type` as an identifier type isn't by accident. In fact, | ||||
| it matches well with the internal hashed string class. Moreover, it's also the | ||||
| same type returned by the hash function of the internal RTTI system, in case the | ||||
| user wants to rely on that.<br/> | ||||
| However, being an integral value, it leaves the user full freedom to rely on his | ||||
| own tools if he deems it necessary. | ||||
| own tools if necessary. | ||||
|  | ||||
| Once a task has been associated with the flow builder, it can be assigned | ||||
| read-only or read-write resources, as appropriate: | ||||
| Once a task is associated with the flow builder, it's also assigned read-only or | ||||
| read-write resources as appropriate: | ||||
|  | ||||
| ```cpp | ||||
| builder | ||||
| @@ -203,13 +202,87 @@ builder | ||||
|  | ||||
| As mentioned, many functions return the builder itself and it's therefore easy | ||||
| to concatenate the different calls.<br/> | ||||
| Also in the case of resources, these are identified by numeric values of type | ||||
| Also in the case of resources, they are identified by numeric values of type | ||||
| `id_type`. As above, the choice is not entirely random. This goes well with the | ||||
| tools offered by the library while leaving room for maximum flexibility. | ||||
|  | ||||
| Finally, both the `ro` and` rw` functions also offer an overload that accepts a | ||||
| pair of iterators, so that one can pass a range of resources in one go. | ||||
|  | ||||
| ### Rebinding | ||||
|  | ||||
| The `flow` class is resource based rather than task based. This means that graph | ||||
| generation is driven by resources and not by the order of _appearance_ of tasks | ||||
| during flow definition.<br/> | ||||
| Although this concept is particularly important, it's almost irrelevant for the | ||||
| vast majority of cases. However, it becomes relevant when _rebinding_ resources | ||||
| or tasks. | ||||
|  | ||||
| In fact, nothing prevents rebinding elements to a flow.<br/> | ||||
| However, the behavior changes slightly from case to case and has some nuances | ||||
| that it's worth knowing about. | ||||
|  | ||||
| Directly rebinding a resource without the task being replaced trivially results | ||||
| in the task's access mode for that resource being updated: | ||||
|  | ||||
| ```cpp | ||||
| builder.bind("task"_hs).rw("resource"_hs).ro("resource"_hs) | ||||
| ``` | ||||
|  | ||||
| In this case, the resource is accessed in read-only mode, regardless of the | ||||
| first call to `rw`.<br/> | ||||
| Behind the scenes, the call doesn't actually _replace_ the previous one but is | ||||
| queued after it instead, overwriting it when generating the graph. Thus, a large | ||||
| number of resource rebindings may even impact processing times (very difficult | ||||
| to observe but theoretically possible). | ||||
|  | ||||
| Rebinding resources and also combining it with changes to tasks has far more | ||||
| implications instead.<br/> | ||||
| As mentioned, graph generation takes place starting from resources and not from | ||||
| tasks. Therefore, the result may not be as expected: | ||||
|  | ||||
| ```cpp | ||||
| builder | ||||
|     .bind("task_1"_hs) | ||||
|         .ro("resource"_hs) | ||||
|     .bind("task_2"_hs) | ||||
|         .ro("resource"_hs) | ||||
|     .bind("task_1"_hs) | ||||
|         .rw("resource"_hs); | ||||
| ``` | ||||
|  | ||||
| What happens here is that the resource first _sees_ a read-only access request | ||||
| from the first task, then a read-write request from the second task and finally | ||||
| a new read-only request from the first task.<br/> | ||||
| Although this definition would probably be counted as an error, the resulting | ||||
| graph may be unexpected. This in fact consists of a single edge outgoing from | ||||
| the second task and directed to the first task.<br/> | ||||
| To intuitively understand what happens, it's enough to think of the fact that a | ||||
| task never has an edge pointing to itself. | ||||
|  | ||||
| While not obvious, this approach has its pros and cons like any other solution. | ||||
| For example, creating loops is actually simple in the context of resource-based | ||||
| graph generations: | ||||
|  | ||||
| ```cpp | ||||
| builder | ||||
|     .bind("task_1"_hs) | ||||
|         .rw("resource"_hs) | ||||
|     .bind("task_2"_hs) | ||||
|         .rw("resource"_hs) | ||||
|     .bind("task_1"_hs) | ||||
|         .rw("resource"_hs); | ||||
| ``` | ||||
|  | ||||
| As expected, this definition leads to the creation of two edges that define a | ||||
| loop between the two tasks. | ||||
|  | ||||
| As a general rule, rebinding resources and tasks is highly discouraged because | ||||
| it could lead to subtle bugs if users don't know what they're doing.<br/> | ||||
| However, once the mechanisms of resource-based graph generation are understood, | ||||
| it can offer to the expert user a flexibility and a range of possibilities | ||||
| otherwise inaccessible. | ||||
|  | ||||
| ## Fake resources and order of execution | ||||
|  | ||||
| The flow builder doesn't offer the ability to specify when a task should execute | ||||
| @@ -217,10 +290,10 @@ before or after another task.<br/> | ||||
| In fact, the order of _registration_ on the resources also determines the order | ||||
| in which the tasks are processed during the generation of the execution graph. | ||||
|  | ||||
| However, there is a way to force the execution order of two processes.<br/> | ||||
| However, there is a way to _force_ the execution order of two processes.<br/> | ||||
| Briefly, since accessing a resource in opposite modes requires sequential rather | ||||
| than parallel scheduling, it's possible to make use of fake resources to force | ||||
| the order execution: | ||||
| than parallel scheduling, it's possible to make use of fake resources to rule on | ||||
| the execution order: | ||||
|  | ||||
| ```cpp | ||||
| builder | ||||
| @@ -235,10 +308,10 @@ builder | ||||
|         .ro("fake"_hs) | ||||
| ``` | ||||
|  | ||||
| This snippet forces the execution of `task_2` and `task_3` **after** `task_1`. | ||||
| This is due to the fact that the latter sets a read-write requirement on a fake | ||||
| This snippet forces the execution of `task_1` **before** `task_2` and `task_3`. | ||||
| This is due to the fact that the former sets a read-write requirement on a fake | ||||
| resource that the other tasks also want to access in read-only mode.<br/> | ||||
| Similarly, it's possible to force a task to run after a certain group: | ||||
| Similarly, it's possible to force a task to run **after** a certain group: | ||||
|  | ||||
| ```cpp | ||||
| builder | ||||
| @@ -261,7 +334,7 @@ others tasks. | ||||
|  | ||||
| Sometimes it's useful to assign the role of _sync point_ to a node.<br/> | ||||
| Whether it accesses new resources or is simply a watershed, the procedure for | ||||
| assigning this role to a vertex is always the same: first it's tied to the flow | ||||
| assigning this role to a vertex is always the same. First it's tied to the flow | ||||
| builder, then the `sync` function is invoked: | ||||
|  | ||||
| ```cpp | ||||
| @@ -283,7 +356,7 @@ all specified constraints to return the best scheduling for the vertices: | ||||
| entt::adjacency_matrix<entt::directed_tag> graph = builder.graph(); | ||||
| ``` | ||||
|  | ||||
| The search for the main vertices, that is those without in-edges, is usually the | ||||
| Searching for the main vertices (that is, those without in-edges) is usually the | ||||
| first thing required: | ||||
|  | ||||
| ```cpp | ||||
| @@ -294,6 +367,6 @@ for(auto &&vertex: graph) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Starting from them, using the other functions appropriately (such as `out_edges` | ||||
| to retrieve the children of a given task or `edges` to access their identifiers) | ||||
| it will be possible to instantiate an execution graph. | ||||
| Then it's possible to instantiate an execution graph by means of other functions | ||||
| such as `out_edges` to retrieve the children of a given task or `edges` to get | ||||
| the identifiers. | ||||
|   | ||||
							
								
								
									
										18
									
								
								external/entt/entt/docs/md/lib.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										18
									
								
								external/entt/entt/docs/md/lib.md
									
									
									
									
										vendored
									
									
								
							| @@ -19,14 +19,12 @@ | ||||
| general and on GNU/Linux when default visibility was set to hidden. The | ||||
| limitation was mainly due to a custom utility used to assign unique, sequential | ||||
| identifiers with different types.<br/> | ||||
| Fortunately, nowadays using `EnTT` across boundaries is much easier. | ||||
| Fortunately, nowadays `EnTT` works smoothly across boundaries. | ||||
|  | ||||
| ## Smooth until proven otherwise | ||||
|  | ||||
| Many classes in `EnTT` make extensive use of type erasure for their purposes. | ||||
| This isn't a problem on itself (in fact, it's the basis of an API so convenient | ||||
| to use). However, a way is needed to recognize the objects whose type has been | ||||
| erased on the other side of a boundary.<br/> | ||||
| This raises the need to identify objects whose type has been erased.<br/> | ||||
| The `type_hash` class template is how identifiers are generated and thus made | ||||
| available to the rest of the library. In general, this class doesn't arouse much | ||||
| interest. The only exception is when a conflict between identifiers occurs | ||||
| @@ -36,13 +34,13 @@ The section dedicated to `type_info` contains all the details to get around the | ||||
| issue in a concise and elegant way. Please refer to the specific documentation. | ||||
|  | ||||
| When working with linked libraries, compile definitions `ENTT_API_EXPORT` and | ||||
| `ENTT_API_IMPORT` can be used where there is a need to import or export symbols, | ||||
| so as to make everything work nicely across boundaries.<br/> | ||||
| `ENTT_API_IMPORT` are to import or export symbols, so as to make everything work | ||||
| nicely across boundaries.<br/> | ||||
| On the other hand, everything should run smoothly when working with plugins or | ||||
| shared libraries that don't export any symbols. | ||||
|  | ||||
| For anyone who needs more details, the test suite contains multiple examples | ||||
| covering the most common cases (see the `lib` directory for all details).<br/> | ||||
| For those who need more details, the test suite contains many examples covering | ||||
| the most common cases (see the `lib` directory for all details).<br/> | ||||
| It goes without saying that it's impossible to cover **all** possible cases. | ||||
| However, what is offered should hopefully serve as a basis for all of them. | ||||
|  | ||||
| @@ -70,8 +68,8 @@ entt::locator<entt::meta_ctx>::reset(handle); | ||||
|  | ||||
| From now on, both spaces refer to the same context and on it are attached all | ||||
| new meta types, no matter where they are created.<br/> | ||||
| Note that resetting the main context doesn't also propagate changes across | ||||
| boundaries. In other words, resetting a context results in the decoupling of the | ||||
| Note that _replacing_ the main context doesn't also propagate changes across | ||||
| boundaries. In other words, replacing a context results in the decoupling of the | ||||
| two sides and therefore a divergence in the contents. | ||||
|  | ||||
| ## Memory Management | ||||
|   | ||||
							
								
								
									
										60
									
								
								external/entt/entt/docs/md/links.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										60
									
								
								external/entt/entt/docs/md/links.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,5 +1,22 @@ | ||||
| # EnTT in Action | ||||
|  | ||||
| <!-- | ||||
| @cond TURN_OFF_DOXYGEN | ||||
| --> | ||||
| # Table of Contents | ||||
|  | ||||
| * [Introduction](#introduction) | ||||
| * [EnTT in Action](#entt-in-action) | ||||
|   * [Games](#games) | ||||
|   * [Engines and the like](#engines-and-the-like) | ||||
|   * [Articles, videos and blog posts](#articles-videos-and-blog-posts) | ||||
|   * [Any Other Business](#any-other-business) | ||||
| <!-- | ||||
| @endcond TURN_OFF_DOXYGEN | ||||
| --> | ||||
|  | ||||
| # Introduction | ||||
|  | ||||
| `EnTT` is widely used in private and commercial applications. I cannot even | ||||
| mention most of them because of some signatures I put on some documents time | ||||
| ago. Fortunately, there are also people who took the time to implement open | ||||
| @@ -7,13 +24,18 @@ source projects based on `EnTT` and didn't hold back when it came to documenting | ||||
| them. | ||||
|  | ||||
| Below an incomplete list of games, applications and articles that can be used as | ||||
| a reference. Where I put the word _apparently_ means that the use of `EnTT` is | ||||
| documented but the authors didn't make explicit announcements or contacted me | ||||
| directly. | ||||
| a reference.<br/> | ||||
| Where I put the word _apparently_ means that the use of `EnTT` is documented but | ||||
| the authors didn't make explicit announcements or contacted me directly. | ||||
|  | ||||
| I hope this list can grow much more in the future: | ||||
| If you know of other resources out there that are about `EnTT`, feel free to | ||||
| open an issue or a PR and I'll be glad to add them to this page.<br/> | ||||
| I hope the following lists can grow much more in the future. | ||||
|  | ||||
| # EnTT in Action | ||||
|  | ||||
| ## Games | ||||
|  | ||||
| * Games: | ||||
|   * [Minecraft](https://minecraft.net/en-us/attribution/) by | ||||
|     [Mojang](https://mojang.com/): of course, **that** Minecraft, see the | ||||
|     open source attributions page for more details. | ||||
| @@ -103,7 +125,8 @@ I hope this list can grow much more in the future: | ||||
|   * [Confetti Party](https://github.com/hexerei/entt-confetti): C++ sample | ||||
|     application as a starting point using `EnTT` and `SDL2`. | ||||
|  | ||||
| * Engines and the like: | ||||
| ## Engines and the like: | ||||
|  | ||||
|   * [Aether Engine](https://hadean.com/spatial-simulation/) | ||||
|     [v1.1+](https://docs.hadean.com/v1.1/Licenses/) by | ||||
|     [Hadean](https://hadean.com/): a library designed for spatially partitioning | ||||
| @@ -168,8 +191,19 @@ I hope this list can grow much more in the future: | ||||
|     engine based on `SDL2` and `EnTT`. | ||||
|   * [Ducktape](https://github.com/DucktapeEngine/Ducktape): an open source C++ | ||||
|     2D & 3D game engine that focuses on being fast and powerful. | ||||
|   * [The Worst Engine](https://github.com/Parasik72/TWE): a game engine based on | ||||
|     OpenGL. | ||||
|   * [Ecsact](https://ecsact.dev/): a language aimed at describing ECS, with a | ||||
|     [runtime implementation](https://github.com/ecsact-dev/ecsact_rt_entt) based | ||||
|     on `EnTT`. | ||||
|   * [AGE (Arc Game Engine)](https://github.com/MohitSethi99/ArcGameEngine): an | ||||
|     open-source engine for building 2D & 3D real-time rendering and interactive | ||||
|     contents. | ||||
|   * [Kengine](https://github.com/phisko/kengine): the _Koala engine_ is a game | ||||
|     engine entirely implemented as an entity-component-ystem. | ||||
|  | ||||
| ## Articles, videos and blog posts: | ||||
|  | ||||
| * Articles, videos and blog posts: | ||||
|   * [Some posts](https://skypjack.github.io/tags/#entt) on my personal | ||||
|     [blog](https://skypjack.github.io/) are about `EnTT`, for those who want to | ||||
|     know **more** on this project. | ||||
| @@ -193,6 +227,12 @@ I hope this list 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. | ||||
|   * [Warmonger Dynasty devlog series](https://david-delassus.medium.com/list/warmonger-dynasty-devlogs-f64b71f556de) | ||||
|     by [linkdd](https://github.com/linkdd): an interesting walkthrough of | ||||
|     developing a game (also) with EnTT. | ||||
|   * [Use EnTT When You Need An ECS](https://www.codingwiththomas.com/blog/use-entt-when-you-need-an-ecs) | ||||
|     by [Thomas](https://www.codingwiththomas.com/): I couldn't have said it | ||||
|     better. | ||||
|   * [Space Battle: Huge edition](http://victor.madtriangles.com/code%20experiment/2018/06/11/post-ecs-battle-huge.html): | ||||
|     huge space battle built entirely from scratch. | ||||
|   * [Space Battle](https://github.com/vblanco20-1/ECS_SpaceBattle): huge space | ||||
| @@ -219,7 +259,8 @@ I hope this list can grow much more in the future: | ||||
|     MMO(RPG)s and its [follow-up](https://youtu.be/yGlZeopx2hU) episode about | ||||
|     player bots and full external ECS: a series definitely worth looking at. | ||||
|  | ||||
| * Any Other Business: | ||||
| ## Any Other Business: | ||||
|  | ||||
|   * [ArcGIS Runtime SDKs](https://developers.arcgis.com/arcgis-runtime/) by | ||||
|     [Esri](https://www.esri.com/): they use `EnTT` for the internal ECS and the | ||||
|     cross platform C++ rendering engine. The SDKs are utilized by a lot of | ||||
| @@ -261,6 +302,3 @@ I hope this list can grow much more in the future: | ||||
|   * GitHub contains also | ||||
|     [many other examples](https://github.com/search?o=desc&q=%22skypjack%2Fentt%22&s=indexed&type=Code) | ||||
|     of use of `EnTT` from which to take inspiration if interested. | ||||
|  | ||||
| If you know of other resources out there that are about `EnTT`, feel free to | ||||
| open an issue or a PR and I'll be glad to add them to this page. | ||||
|   | ||||
							
								
								
									
										420
									
								
								external/entt/entt/docs/md/meta.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										420
									
								
								external/entt/entt/docs/md/meta.md
									
									
									
									
										vendored
									
									
								
							| @@ -67,17 +67,15 @@ recommended. | ||||
|  | ||||
| # Reflection in a nutshell | ||||
|  | ||||
| Reflection always starts from real types (users cannot reflect imaginary types | ||||
| and it would not make much sense, we wouldn't be talking about reflection | ||||
| anymore).<br/> | ||||
| To create a meta node, the library provides the `meta` function that accepts a | ||||
| type to reflect as a template parameter: | ||||
| Reflection always starts from actual C++ types. Users cannot reflect _imaginary_ | ||||
| types.<br/> | ||||
| The `meta` function is where it all starts: | ||||
|  | ||||
| ```cpp | ||||
| auto factory = entt::meta<my_type>(); | ||||
| ``` | ||||
|  | ||||
| The returned value is a factory object to use to continue building the meta | ||||
| The returned value is a _factory object_ to use to continue building the meta | ||||
| type. | ||||
|  | ||||
| By default, a meta type is associated with the identifier returned by the | ||||
| @@ -88,45 +86,42 @@ However, it's also possible to assign custom identifiers to meta types: | ||||
| auto factory = entt::meta<my_type>().type("reflected_type"_hs); | ||||
| ``` | ||||
|  | ||||
| Identifiers are important because users can retrieve meta types at runtime by | ||||
| searching for them by _name_ other than by type.<br/> | ||||
| On the other hand, there are cases in which users can be interested in adding | ||||
| features to a reflected type so that the reflection system can use it correctly | ||||
| under the hood, but they don't want to also make the type _searchable_. In this | ||||
| case, it's sufficient not to invoke `type`. | ||||
| Identifiers are used to _retrieve_ meta types at runtime by _name_ other than by | ||||
| type.<br/> | ||||
| However, users can be interested in adding features to a reflected type so that | ||||
| the reflection system can use it correctly under the hood, while they don't want | ||||
| to also make the type _searchable_. In this case, it's sufficient not to invoke | ||||
| `type`. | ||||
|  | ||||
| A factory is such that all its member functions return the factory itself or a | ||||
| decorated version of it. This object can be used to add the following: | ||||
| A factory is such that all its member functions return the factory itself. It's | ||||
| generally used to create the following: | ||||
|  | ||||
| * _Constructors_. Actual constructors can be assigned to a reflected type by | ||||
|   specifying their list of arguments. Free functions (namely, factories) can be | ||||
|   used as well, as long as the return type is the expected one. From a client's | ||||
|   point of view, nothing changes if a constructor is a free function or an | ||||
|   actual constructor.<br/> | ||||
|   Use the `ctor` member function for this purpose: | ||||
| * _Constructors_. A constructors is assigned to a reflected type by specifying | ||||
|   its _list of arguments_. Free functions are also accepted if the return type | ||||
|   is the expected one. From a client perspective, nothing changes between a free | ||||
|   function or an actual constructor: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<my_type>().ctor<int, char>().ctor<&factory>(); | ||||
|   ``` | ||||
|  | ||||
| * _Destructors_. Free functions and member functions can be used as destructors | ||||
|   of reflected types. The purpose is to give users the ability to free up | ||||
|   resources that require special treatment before an object is actually | ||||
|   destroyed.<br/> | ||||
|   Use the `dtor` member function for this purpose: | ||||
|   Meta default constructors are implicitly generated, if possible. | ||||
|  | ||||
| * _Destructors_. Both free functions and member functions are valid destructors: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<my_type>().dtor<&destroy>(); | ||||
|   ``` | ||||
|  | ||||
|   The purpose is to offer the possibility to free up resources that require | ||||
|   _special treatment_ before an object is actually destroyed.<br/> | ||||
|   A function should neither delete nor explicitly invoke the destructor of a | ||||
|   given instance. | ||||
|  | ||||
| * _Data members_. Both real data members of the underlying type and static and | ||||
|   global variables, as well as constants of any kind, can be attached to a meta | ||||
|   type. From the point of view of the client, all the variables associated with | ||||
|   the reflected type will appear as if they were part of the type itself.<br/> | ||||
|   Use the `data` member function for this purpose: | ||||
| * _Data members_. Meta data members are actual data members of the underlying | ||||
|   type but also static and global variables or constants of any kind. From the | ||||
|   point of view of the client, all the variables associated with the reflected | ||||
|   type appear as if they were part of the type itself: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<my_type>() | ||||
| @@ -135,13 +130,11 @@ decorated version of it. This object can be used to add the following: | ||||
|       .data<&global_variable>("global"_hs); | ||||
|   ``` | ||||
|  | ||||
|   The function requires as an argument the identifier to give to the meta data | ||||
|   once created. Users can then access meta data at runtime by searching for them | ||||
|   by _name_.<br/> | ||||
|   Data members can also be defined by means of a setter and getter pair. Setters | ||||
|   and getters can be either free functions, class members or a mix of them, as | ||||
|   long as they respect the required signatures. This approach is also convenient | ||||
|   to create a read-only variable from a non-const data member: | ||||
|   The `data` function requires the identifier to use for the meta data member. | ||||
|   Users can then access it by _name_ at runtime.<br/> | ||||
|   Data members are also defined by means of a setter and getter pair. These are | ||||
|   either free functions, class members or a mix of them. This approach is also | ||||
|   convenient to create read-only properties from a non-const data member: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<my_type>().data<nullptr, &my_type::data_member>("member"_hs); | ||||
| @@ -153,13 +146,10 @@ decorated version of it. This object can be used to add the following: | ||||
|   entt::meta<my_type>().data<entt::value_list<&from_int, &from_string>, &my_type::data_member>("member"_hs); | ||||
|   ``` | ||||
|  | ||||
|   Refer to the inline documentation for all the details. | ||||
|  | ||||
| * _Member functions_. Both real member functions of the underlying type and free | ||||
|   functions can be attached to a meta type. From the point of view of the | ||||
|   client, all the functions associated with the reflected type will appear as if | ||||
|   they were part of the type itself.<br/> | ||||
|   Use the `func` member function for this purpose: | ||||
| * _Member functions_. Meta member functions are actual member functions of the | ||||
|   underlying type but also plain free functions. From the point of view of the | ||||
|   client, all the functions associated with the reflected type appear as if they | ||||
|   were part of the type itself: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<my_type>() | ||||
| @@ -168,40 +158,31 @@ decorated version of it. This object can be used to add the following: | ||||
|       .func<&free_function>("free"_hs); | ||||
|   ``` | ||||
|  | ||||
|   The function requires as an argument the identifier to give to the meta | ||||
|   function once created. Users can then access meta functions at runtime by | ||||
|   searching for them by _name_.<br/> | ||||
|   The `func` function requires the identifier to use for the meta data function. | ||||
|   Users can then access it by _name_ at runtime.<br/> | ||||
|   Overloading of meta functions is supported. Overloaded functions are resolved | ||||
|   at runtime by the reflection system according to the types of the arguments. | ||||
|  | ||||
| * _Base classes_. A base class is such that the underlying type is actually | ||||
|   derived from it. In this case, the reflection system tracks the relationship | ||||
|   and allows for implicit casts at runtime when required.<br/> | ||||
|   Use the `base` member function for this purpose: | ||||
|   derived from it: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<derived_type>().base<base_type>(); | ||||
|   ``` | ||||
|  | ||||
|   From now on, wherever a `base_type` is required, an instance of `derived_type` | ||||
|   will also be accepted. | ||||
|   The reflection system tracks the relationship and allows for implicit casts at | ||||
|   runtime when required. In other terms, wherever a `base_type` is required, an | ||||
|   instance of `derived_type` is also accepted. | ||||
|  | ||||
| * _Conversion functions_. Actual types can be converted, this is a fact. Just | ||||
|   think of the relationship between a `double` and an `int` to see it. Similar | ||||
|   to bases, conversion functions allow users to define conversions that will be | ||||
|   implicitly performed by the reflection system when required.<br/> | ||||
|   Use the `conv` member function for this purpose: | ||||
| * _Conversion functions_. Conversion functions allow users to define conversions | ||||
|   that are implicitly performed by the reflection system when required: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<double>().conv<int>(); | ||||
|   ``` | ||||
|  | ||||
| That's all, everything users need to create meta types and enjoy the reflection | ||||
| system. At first glance it may not seem that much, but users usually learn to | ||||
| appreciate it over time.<br/> | ||||
| Also, do not forget what these few lines hide under the hood: a built-in, | ||||
| non-intrusive and macro-free system for reflection in C++. Features that are | ||||
| definitely worth the price, at least for me. | ||||
| This is everything users need to create meta types. Refer to the inline | ||||
| documentation for further details. | ||||
|  | ||||
| ## Any to the rescue | ||||
|  | ||||
| @@ -214,13 +195,13 @@ The API is very similar to that of the `any` type. The class `meta_any` _wraps_ | ||||
| many of the feature to infer a meta node, before forwarding some or all of the | ||||
| arguments to the underlying storage.<br/> | ||||
| Among the few relevant differences, `meta_any` adds support for containers and | ||||
| pointer-like types (see the following sections for more details), while `any` | ||||
| does not.<br/> | ||||
| Similar to `any`, this class can also be used to create _aliases_ for unmanaged | ||||
| pointer-like types, while `any` doesn't.<br/> | ||||
| Similar to `any`, this class is also used to create _aliases_ for unmanaged | ||||
| objects either with `forward_as_meta` or using the `std::in_place_type<T &>` | ||||
| disambiguation tag, as well as from an existing object by means of the `as_ref` | ||||
| member function. However, unlike `any`, `meta_any` treats an empty instance and | ||||
| one initialized with `void` differently: | ||||
| member function.<br/> | ||||
| Unlike `any` instead, `meta_any` treats an empty instance and one initialized | ||||
| with `void` differently: | ||||
|  | ||||
| ```cpp | ||||
| entt::meta_any empty{}; | ||||
| @@ -229,21 +210,19 @@ entt::meta_any other{std::in_place_type<void>}; | ||||
|  | ||||
| While `any` considers both as empty, `meta_any` treats objects initialized with | ||||
| `void` as if they were _valid_ ones. This allows to differentiate between failed | ||||
| function calls and function calls that are successful but return nothing.<br/> | ||||
| function calls and function calls that are successful but return nothing. | ||||
|  | ||||
| Finally, the member functions `try_cast`, `cast` and `allow_cast` are used to | ||||
| cast the underlying object to a given type (either a reference or a value type) | ||||
| or to _convert_ a `meta_any` in such a way that a cast becomes viable for the | ||||
| resulting object. There is in fact no `any_cast` equivalent for `meta_any`. | ||||
| resulting object.<br/> | ||||
| There is in fact no `any_cast` equivalent for `meta_any`. | ||||
|  | ||||
| ## Enjoy the runtime | ||||
|  | ||||
| Once the web of reflected types has been constructed, it's a matter of using it | ||||
| at runtime where required.<br/> | ||||
| All this has the great merit that the reflection system stands in fact as a | ||||
| non-intrusive tool for the runtime, unlike the vast majority of the things | ||||
| offered by this library and closely linked to the compile-time. | ||||
|  | ||||
| To search for a reflected type there are a few options: | ||||
| Once the web of reflected types is constructed, it's a matter of using it at | ||||
| runtime where required.<br/> | ||||
| There are a few options to search for a reflected type: | ||||
|  | ||||
| ```cpp | ||||
| // direct access to a reflected type | ||||
| @@ -257,8 +236,8 @@ auto by_type_id = entt::resolve(entt::type_id<my_type>()); | ||||
| ``` | ||||
|  | ||||
| There exists also an overload of the `resolve` function to use to iterate all | ||||
| the reflected types at once. It returns an iterable object that can be used in a | ||||
| range-for loop: | ||||
| reflected types at once. It returns an iterable object to be used in a range-for | ||||
| loop: | ||||
|  | ||||
| ```cpp | ||||
| for(auto &&[id, type]: entt::resolve()) { | ||||
| @@ -270,9 +249,7 @@ In all cases, the returned value is an instance of `meta_type` (possibly with | ||||
| its id). This kind of objects offer an API to know their _runtime identifiers_, | ||||
| to iterate all the meta objects associated with them and even to build instances | ||||
| of the underlying type.<br/> | ||||
| Refer to the inline documentation for all the details. | ||||
|  | ||||
| Meta data members and functions are accessed by name among the other things: | ||||
| Meta data members and functions are accessed by name: | ||||
|  | ||||
| * Meta data members: | ||||
|  | ||||
| @@ -297,11 +274,11 @@ Meta data members and functions are accessed by name among the other things: | ||||
|   A meta function object offers an API to query the underlying type (for | ||||
|   example, to know if it's a const or a static function), to know the number of | ||||
|   arguments, the meta return type and the meta types of the parameters. In | ||||
|   addition, a meta function object can be used to invoke the underlying function | ||||
|   and then get the return value in the form of a `meta_any` object. | ||||
|   addition, a meta function object is used to invoke the underlying function and | ||||
|   then get the return value in the form of a `meta_any` object. | ||||
|  | ||||
| All the meta objects thus obtained as well as the meta types can be explicitly | ||||
| converted to a boolean value to check if they are valid: | ||||
| All the meta objects thus obtained as well as the meta types explicitly convert | ||||
| to a boolean value to check for validity: | ||||
|  | ||||
| ```cpp | ||||
| if(auto func = entt::resolve<my_type>().func("member"_hs); func) { | ||||
| @@ -319,26 +296,23 @@ for(auto &&[id, type]: entt::resolve<my_type>().base()) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| A meta type can also be used to `construct` actual instances of the underlying | ||||
| Meta type are also used to `construct` actual instances of the underlying | ||||
| type.<br/> | ||||
| In particular, the `construct` member function accepts a variable number of | ||||
| arguments and searches for a match. It then returns a `meta_any` object that may | ||||
| or may not be initialized, depending on whether a suitable constructor has been | ||||
| found or not. | ||||
| or may not be initialized, depending on whether a suitable constructor was found | ||||
| or not. | ||||
|  | ||||
| There is no object that wraps the destructor of a meta type nor a `destroy` | ||||
| member function in its API. Destructors are invoked implicitly by `meta_any` | ||||
| behind the scenes and users have not to deal with them explicitly. Furthermore, | ||||
| they have no name, cannot be searched and wouldn't have member functions to | ||||
| expose anyway.<br/> | ||||
| Similarly, conversion functions aren't directly accessible. They are used | ||||
| they've no name, cannot be searched and wouldn't have member functions to expose | ||||
| anyway.<br/> | ||||
| Similarly, conversion functions aren't directly accessible. They're used | ||||
| internally by `meta_any` and the meta objects when needed. | ||||
|  | ||||
| Meta types and meta objects in general contain much more than what is said: a | ||||
| plethora of functions in addition to those listed whose purposes and uses go | ||||
| unfortunately beyond the scope of this document.<br/> | ||||
| I invite anyone interested in the subject to look at the code, experiment and | ||||
| read the inline documentation to get the best out of this powerful tool. | ||||
| Meta types and meta objects in general contain much more than what was said. | ||||
| Refer to the inline documentation for further details. | ||||
|  | ||||
| ## Container support | ||||
|  | ||||
| @@ -349,7 +323,7 @@ meta system in many cases. | ||||
|  | ||||
| To make a container be recognized as such by the meta system, users are required | ||||
| to provide specializations for either the `meta_sequence_container_traits` class | ||||
| or the `meta_associative_container_traits` class, according to the actual type | ||||
| or the `meta_associative_container_traits` class, according to the actual _type_ | ||||
| of the container.<br/> | ||||
| `EnTT` already exports the specializations for some common classes. In | ||||
| particular: | ||||
| @@ -386,11 +360,10 @@ if(any.type().is_sequence_container()) { | ||||
|  | ||||
| The method to use to get a proxy object for associative containers is | ||||
| `as_associative_container` instead.<br/> | ||||
| It goes without saying that it's not necessary to perform a double check. | ||||
| Instead, it's sufficient to query the meta type or verify that the proxy object | ||||
| is valid. In fact, proxies are contextually convertible to bool to know if they | ||||
| are valid. For example, invalid proxies are returned when the wrapped object | ||||
| isn't a container.<br/> | ||||
| It's not necessary to perform a double check actually. Instead, it's enough to | ||||
| query the meta type or verify that the proxy object is valid. In fact, proxies | ||||
| are contextually convertible to bool to check for validity. For example, invalid | ||||
| proxies are returned when the wrapped object isn't a container.<br/> | ||||
| In all cases, users aren't expected to _reflect_ containers explicitly. It's | ||||
| sufficient to assign a container for which a specialization of the traits | ||||
| classes exists to a `meta_any` object to be able to get its proxy object. | ||||
| @@ -402,32 +375,18 @@ to case. In particular: | ||||
| * The `value_type` member function returns the meta type of the elements. | ||||
|  | ||||
| * The `size` member function returns the number of elements in the container as | ||||
|   an unsigned integer value: | ||||
|  | ||||
|   ```cpp | ||||
|   const auto size = view.size(); | ||||
|   ``` | ||||
|   an unsigned integer value. | ||||
|  | ||||
| * The `resize` member function allows to resize the wrapped container and | ||||
|   returns true in case of success: | ||||
|  | ||||
|   ```cpp | ||||
|   const bool ok = view.resize(3u); | ||||
|   ``` | ||||
|  | ||||
|   returns true in case of success.<br/> | ||||
|   For example, it's not possible to resize fixed size containers. | ||||
|  | ||||
| * The `clear` member function allows to clear the wrapped container and returns | ||||
|   true in case of success: | ||||
|  | ||||
|   ```cpp | ||||
|   const bool ok = view.clear(); | ||||
|   ``` | ||||
|  | ||||
|   true in case of success.<br/> | ||||
|   For example, it's not possible to clear fixed size containers. | ||||
|  | ||||
| * The `begin` and `end` member functions return opaque iterators that can be | ||||
|   used to iterate the container directly: | ||||
| * The `begin` and `end` member functions return opaque iterators that is used to | ||||
|   iterate the container directly: | ||||
|  | ||||
|   ```cpp | ||||
|   for(entt::meta_any element: view) { | ||||
| @@ -441,7 +400,7 @@ to case. In particular: | ||||
|   All meta iterators are input iterators and don't offer an indirection operator | ||||
|   on purpose. | ||||
|  | ||||
| * The `insert` member function can be used to add elements to the container. It | ||||
| * The `insert` member function is used to add elements to the container. It | ||||
|   accepts a meta iterator and the element to insert: | ||||
|  | ||||
|   ```cpp | ||||
| @@ -451,15 +410,15 @@ to case. In particular: | ||||
|   ``` | ||||
|  | ||||
|   This function returns a meta iterator pointing to the inserted element and a | ||||
|   boolean value to indicate whether the operation was successful or not. Note | ||||
|   that a call to `insert` may silently fail in case of fixed size containers or | ||||
|   whether the arguments aren't at least convertible to the required types.<br/> | ||||
|   Since the meta iterators are contextually convertible to bool, users can rely | ||||
|   on them to know if the operation has failed on the actual container or | ||||
|   upstream, for example for an argument conversion problem. | ||||
|   boolean value to indicate whether the operation was successful or not. A call | ||||
|   to `insert` may silently fail in case of fixed size containers or whether the | ||||
|   arguments aren't at least convertible to the required types.<br/> | ||||
|   Since meta iterators are contextually convertible to bool, users can rely on | ||||
|   them to know if the operation failed on the actual container or upstream, for | ||||
|   example due to an argument conversion problem. | ||||
|  | ||||
| * The `erase` member function can be used to remove elements from the container. | ||||
|   It accepts a meta iterator to the element to remove: | ||||
| * The `erase` member function is used to remove elements from the container. It | ||||
|   accepts a meta iterator to the element to remove: | ||||
|  | ||||
|   ```cpp | ||||
|   auto first = view.begin(); | ||||
| @@ -468,11 +427,11 @@ to case. In particular: | ||||
|   ``` | ||||
|  | ||||
|   This function returns a meta iterator following the last removed element and a | ||||
|   boolean value to indicate whether the operation was successful or not. Note | ||||
|   that a call to `erase` may silently fail in case of fixed size containers. | ||||
|   boolean value to indicate whether the operation was successful or not. A call | ||||
|   to `erase` may silently fail in case of fixed size containers. | ||||
|  | ||||
| * The `operator[]` can be used to access elements in a container. It accepts a | ||||
|   single argument, that is the position of the element to return: | ||||
| * The `operator[]` is used to access container elements. It accepts a single | ||||
|   argument, the position of the element to return: | ||||
|  | ||||
|   ```cpp | ||||
|   for(std::size_t pos{}, last = view.size(); pos < last; ++pos) { | ||||
| @@ -482,8 +441,8 @@ to case. In particular: | ||||
|   ``` | ||||
|  | ||||
|   The function returns instances of `meta_any` that directly refer to the actual | ||||
|   elements. Modifying the returned object will then directly modify the element | ||||
|   inside the container.<br/> | ||||
|   elements. Modifying the returned object directly modifies the element inside | ||||
|   the container.<br/> | ||||
|   Depending on the underlying sequence container, this operation may not be as | ||||
|   efficient. For example, in the case of an `std::list`, a positional access | ||||
|   translates to a linear visit of the list itself (probably not what the user | ||||
| @@ -508,21 +467,13 @@ differences in behavior in the case of key-only containers. In particular: | ||||
|   `std::map<int, char>`. | ||||
|  | ||||
| * The `size` member function returns the number of elements in the container as | ||||
|   an unsigned integer value: | ||||
|  | ||||
|   ```cpp | ||||
|   const auto size = view.size(); | ||||
|   ``` | ||||
|   an unsigned integer value. | ||||
|  | ||||
| * The `clear` member function allows to clear the wrapped container and returns | ||||
|   true in case of success: | ||||
|   true in case of success. | ||||
|  | ||||
|   ```cpp | ||||
|   const bool ok = view.clear(); | ||||
|   ``` | ||||
|  | ||||
| * The `begin` and `end` member functions return opaque iterators that can be | ||||
|   used to iterate the container directly: | ||||
| * The `begin` and `end` member functions return opaque iterators that are used | ||||
|   to iterate the container directly: | ||||
|  | ||||
|   ```cpp | ||||
|   for(std::pair<entt::meta_any, entt::meta_any> element: view) { | ||||
| @@ -539,11 +490,11 @@ differences in behavior in the case of key-only containers. In particular: | ||||
|  | ||||
|   While the accessed key is usually constant in the associative containers and | ||||
|   is therefore returned by copy, the value (if any) is wrapped by an instance of | ||||
|   `meta_any` that directly refers to the actual element. Modifying it will then | ||||
|   directly modify the element inside the container. | ||||
|   `meta_any` that directly refers to the actual element. Modifying it directly | ||||
|   modifies the element inside the container. | ||||
|  | ||||
| * The `insert` member function can be used to add elements to the container. It | ||||
|   accepts two arguments, respectively the key and the value to be inserted: | ||||
| * The `insert` member function is used to add elements to a container. It gets | ||||
|   two arguments, respectively the key and the value to insert: | ||||
|  | ||||
|   ```cpp | ||||
|   auto last = view.end(); | ||||
| @@ -552,39 +503,39 @@ differences in behavior in the case of key-only containers. In particular: | ||||
|   ``` | ||||
|  | ||||
|   This function returns a boolean value to indicate whether the operation was | ||||
|   successful or not. Note that a call to `insert` may fail when the arguments | ||||
|   aren't at least convertible to the required types. | ||||
|   successful or not. A call to `insert` may fail when the arguments aren't at | ||||
|   least convertible to the required types. | ||||
|  | ||||
| * The `erase` member function can be used to remove elements from the container. | ||||
|   It accepts a single argument, that is the key to be removed: | ||||
| * The `erase` member function is used to remove elements from a container. It | ||||
|   gets a single argument, the key to remove: | ||||
|  | ||||
|   ```cpp | ||||
|   view.erase(42); | ||||
|   ``` | ||||
|  | ||||
|   This function returns a boolean value to indicate whether the operation was | ||||
|   successful or not. Note that a call to `erase` may fail when the argument | ||||
|   isn't at least convertible to the required type. | ||||
|   successful or not. A call to `erase` may fail when the argument isn't at least | ||||
|   convertible to the required type. | ||||
|  | ||||
| * The `operator[]` can be used to access elements in a container. It accepts a | ||||
|   single argument, that is the key of the element to return: | ||||
| * The `operator[]` is used to access elements in a container. It gets a single | ||||
|   argument, the key of the element to return: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta_any value = view[42]; | ||||
|   ``` | ||||
|  | ||||
|   The function returns instances of `meta_any` that directly refer to the actual | ||||
|   elements. Modifying the returned object will then directly modify the element | ||||
|   inside the container. | ||||
|   elements. Modifying the returned object directly modifies the element inside | ||||
|   the container. | ||||
|  | ||||
| Container support is minimal but likely sufficient to satisfy all needs. | ||||
|  | ||||
| ## Pointer-like types | ||||
|  | ||||
| As with containers, it's also possible to communicate to the meta system which | ||||
| types to consider _pointers_. This will allow to dereference instances of | ||||
| `meta_any`, thus obtaining light _references_ to the pointed objects that are | ||||
| also correctly associated with their meta types.<br/> | ||||
| As with containers, it's also possible to _tell_ to the meta system which types | ||||
| are _pointers_. This makes it possible to dereference instances of `meta_any`, | ||||
| thus obtaining light _references_ to pointed objects that are also correctly | ||||
| associated with their meta types.<br/> | ||||
| To make the meta system recognize a type as _pointer-like_, users can specialize | ||||
| the `is_meta_pointer_like` class. `EnTT` already exports the specializations for | ||||
| some common classes. In particular: | ||||
| @@ -614,13 +565,12 @@ if(any.type().is_pointer_like()) { | ||||
| } | ||||
| ``` | ||||
|  | ||||
| Of course, it's not necessary to perform a double check. Instead, it's enough to | ||||
| query the meta type or verify that the returned object is valid. For example, | ||||
| invalid instances are returned when the wrapped object isn't a pointer-like | ||||
| type.<br/> | ||||
| Note that dereferencing a pointer-like object returns an instance of `meta_any` | ||||
| which refers to the pointed object and allows users to modify it directly | ||||
| (unless the returned element is const, of course). | ||||
| It's not necessary to perform a double check. Instead, it's enough to query the | ||||
| meta type or verify that the returned object is valid. For example, invalid | ||||
| instances are returned when the wrapped object isn't a pointer-like type.<br/> | ||||
| Dereferencing a pointer-like object returns an instance of `meta_any` which | ||||
| _refers_ to the pointed object. Modifying it means modifying the pointed object | ||||
| directly (unless the returned element is const). | ||||
|  | ||||
| In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However, | ||||
| `EnTT` also supports classes that don't offer an `operator*`. In particular: | ||||
| @@ -648,12 +598,12 @@ In general, _dereferencing_ a pointer-like type boils down to a `*ptr`. However, | ||||
|   }; | ||||
|   ``` | ||||
|  | ||||
| In all other cases, that is, when dereferencing a pointer works as expected and | ||||
| regardless of the pointed type, no user intervention is required. | ||||
| In all other cases and when dereferencing a pointer works as expected regardless | ||||
| of the pointed type, no user intervention is required. | ||||
|  | ||||
| ## Template information | ||||
|  | ||||
| Meta types also provide a minimal set of information about the nature of the | ||||
| Meta types also provide a minimal set of information about the _nature_ of the | ||||
| original type in case it's a class template.<br/> | ||||
| By default, this works out of the box and requires no user action. However, it's | ||||
| important to include the header file `template.hpp` to make this information | ||||
| @@ -688,9 +638,9 @@ template<typename Ret, typename... Args> | ||||
| struct function_type<Ret(Args...)> {}; | ||||
| ``` | ||||
|  | ||||
| In this case, rather than the function type, the user might want the return type | ||||
| and unpacked arguments as if they were different template parameters for the | ||||
| original class template.<br/> | ||||
| In this case, rather than the function type, it might be useful to provide the | ||||
| return type and unpacked arguments as if they were different template parameters | ||||
| for the original class template.<br/> | ||||
| To achieve this, users must enter the library internals and provide their own | ||||
| specialization for the class template `entt::meta_template_traits`, such as: | ||||
|  | ||||
| @@ -704,8 +654,8 @@ struct entt::meta_template_traits<function_type<Ret(Args...)>> { | ||||
|  | ||||
| The reflection system doesn't verify the accuracy of the information nor infer a | ||||
| correspondence between real types and meta types.<br/> | ||||
| Therefore, the specialization will be used as is and the information it contains | ||||
| will be associated with the appropriate type when required. | ||||
| Therefore, the specialization is used as is and the information it contains is | ||||
| associated with the appropriate type when required. | ||||
|  | ||||
| ## Automatic conversions | ||||
|  | ||||
| @@ -752,29 +702,29 @@ any.allow_cast(type); | ||||
| int value = any.cast<int>(); | ||||
| ``` | ||||
|  | ||||
| This should make working with arithmetic types and scoped or unscoped enums as | ||||
| easy as it is in C++.<br/> | ||||
| It's also worth noting that it's still possible to set up conversion functions | ||||
| manually and these will always be preferred over the automatic ones. | ||||
| This makes working with arithmetic types and scoped or unscoped enums as easy as | ||||
| it is in C++.<br/> | ||||
| It's still possible to set up conversion functions manually and these are always | ||||
| preferred over the automatic ones. | ||||
|  | ||||
| ## Implicitly generated default constructor | ||||
|  | ||||
| In many cases, it's useful to be able to create objects of default constructible | ||||
| types through the reflection system, while not having to explicitly register the | ||||
| meta type or the default constructor.<br/> | ||||
| Creating objects of default constructible types through the reflection system | ||||
| while not having to explicitly register the meta type or its default constructor | ||||
| is also possible.<br/> | ||||
| For example, in the case of primitive types like `int` or `char`, but not just | ||||
| them. | ||||
|  | ||||
| For this reason and only for default constructible types, default constructors | ||||
| are automatically defined and associated with their meta types, whether they are | ||||
| explicitly or implicitly generated.<br/> | ||||
| For default constructible types only, default constructors are automatically | ||||
| defined and associated with their meta types, whether they are explicitly or | ||||
| implicitly generated.<br/> | ||||
| Therefore, this is all is needed to construct an integer from its meta type: | ||||
|  | ||||
| ```cpp | ||||
| entt::resolve<int>().construct(); | ||||
| ``` | ||||
|  | ||||
| Where the meta type can be for example the one returned from a meta container, | ||||
| Where the meta type is for example the one returned from a meta container, | ||||
| useful for building keys without knowing or having to register the actual types. | ||||
|  | ||||
| In all cases, when users register default constructors, they are preferred both | ||||
| @@ -783,8 +733,8 @@ during searches and when the `construct` member function is invoked. | ||||
| ## From void to any | ||||
|  | ||||
| Sometimes all a user has is an opaque pointer to an object of a known meta type. | ||||
| It would be handy in this case to be able to construct a `meta_any` object from | ||||
| them.<br/> | ||||
| It would be handy in this case to be able to construct a `meta_any` element from | ||||
| it.<br/> | ||||
| For this purpose, the `meta_type` class offers a `from_void` member function | ||||
| designed to convert an opaque pointer into a `meta_any`: | ||||
|  | ||||
| @@ -792,9 +742,8 @@ designed to convert an opaque pointer into a `meta_any`: | ||||
| entt::meta_any any = entt::resolve(id).from_void(element); | ||||
| ``` | ||||
|  | ||||
| It goes without saying that it's not possible to do a check on the actual type. | ||||
| Therefore, this call can be considered as a _static cast_ with all the problems | ||||
| and undefined behaviors of the case following errors.<br/> | ||||
| Unfortunately, it's not possible to do a check on the actual type. Therefore, | ||||
| this call can be considered as a _static cast_ with all its _problems_.<br/> | ||||
| On the other hand, the ability to construct a `meta_any` from an opaque pointer | ||||
| opens the door to some pretty interesting uses that are worth exploring. | ||||
|  | ||||
| @@ -826,17 +775,17 @@ There are a few alternatives available at the moment: | ||||
|   entt::meta<my_type>().func<&my_type::member_function, entt::as_void_t>("member"_hs); | ||||
|   ``` | ||||
|  | ||||
|   If the use with functions is obvious, it must be said that it's also possible | ||||
|   to use this policy with constructors and data members. In the first case, the | ||||
|   constructor will be invoked but the returned wrapper will actually be empty. | ||||
|   In the second case, instead, the property will not be accessible for reading. | ||||
|   If the use with functions is obvious, perhaps less so is use with constructors | ||||
|   and data members. In the first case, the returned wrapper is always empty even | ||||
|   though the constructor is still invoked. In the second case, the property | ||||
|   isn't accessible for reading instead. | ||||
|  | ||||
| * The _as-ref_ and _as-cref_ policies, associated with the types | ||||
|   `entt::as_ref_t` and `entt::as_cref_t`.<br/> | ||||
|   They allow to build wrappers that act as references to unmanaged objects. | ||||
|   Accessing the object contained in the wrapper for which the _reference_ was | ||||
|   requested will make it possible to directly access the instance used to | ||||
|   initialize the wrapper itself: | ||||
|   requested makes it possible to directly access the instance used to initialize | ||||
|   the wrapper itself: | ||||
|  | ||||
|   ```cpp | ||||
|   entt::meta<my_type>().data<&my_type::data_member, entt::as_ref_t>("member"_hs); | ||||
| @@ -854,21 +803,16 @@ obvious corner cases that can in turn be solved with the use of policies. | ||||
|  | ||||
| ## Named constants and enums | ||||
|  | ||||
| A special mention should be made for constant values and enums. It wouldn't be | ||||
| necessary, but it will help distracted readers. | ||||
|  | ||||
| As mentioned, the `data` member function can be used to reflect constants of any | ||||
| type among the other things.<br/> | ||||
| This allows users to create meta types for enums that will work exactly like any | ||||
| other meta type built from a class. Similarly, arithmetic types can be enriched | ||||
| As mentioned, the `data` member function is used to reflect constants of any | ||||
| type.<br/> | ||||
| This allows users to create meta types for enums that work exactly like any | ||||
| other meta type built from a class. Similarly, arithmetic types are _enriched_ | ||||
| with constants of special meaning where required.<br/> | ||||
| Personally, I find it very useful not to export what is the difference between | ||||
| enums and classes in C++ directly in the space of the reflected types. | ||||
| All values thus exported appear to users as if they were constant data members | ||||
| of the reflected types. This avoids the need to _export_ what is the difference | ||||
| between enums and classes in C++ directly in the space of the reflected types. | ||||
|  | ||||
| All the values thus exported will appear to users as if they were constant data | ||||
| members of the reflected types. | ||||
|  | ||||
| Exporting constant values or elements from an enum is as simple as ever: | ||||
| Exposing constant values or elements from an enum is quite simple: | ||||
|  | ||||
| ```cpp | ||||
| entt::meta<my_enum>() | ||||
| @@ -878,28 +822,22 @@ entt::meta<my_enum>() | ||||
| entt::meta<int>().data<2048>("max_int"_hs); | ||||
| ``` | ||||
|  | ||||
| It goes without saying that accessing them is trivial as well. It's a matter of | ||||
| doing the following, as with any other data member of a meta type: | ||||
| Accessing them is trivial as well. It's a matter of doing the following, as with | ||||
| any other data member of a meta type: | ||||
|  | ||||
| ```cpp | ||||
| auto value = entt::resolve<my_enum>().data("a_value"_hs).get({}).cast<my_enum>(); | ||||
| auto max = entt::resolve<int>().data("max_int"_hs).get({}).cast<int>(); | ||||
| ``` | ||||
|  | ||||
| As a side note, remember that all this happens behind the scenes without any | ||||
| allocation because of the small object optimization performed by the `meta_any` | ||||
| class. | ||||
| All this happens behind the scenes without any allocation because of the small | ||||
| object optimization performed by the `meta_any` class. | ||||
|  | ||||
| ## Properties and meta objects | ||||
|  | ||||
| Sometimes (for example, when it comes to creating an editor) it might be useful | ||||
| to attach properties to the meta objects created. Fortunately, this is possible | ||||
| for most of them.<br/> | ||||
| For the meta objects that support properties, the member functions of the | ||||
| factory used for registering them will return an extended version of the factory | ||||
| itself. The latter can be used to attach properties to the last created meta | ||||
| object.<br/> | ||||
| Apparently, it's more difficult to say than to do: | ||||
| for most of them: | ||||
|  | ||||
| ```cpp | ||||
| entt::meta<my_type>().type("reflected_type"_hs).prop("tooltip"_hs, "message"); | ||||
| @@ -914,10 +852,10 @@ Key only properties are also supported out of the box: | ||||
| entt::meta<my_type>().type("reflected_type"_hs).prop(my_enum::key_only); | ||||
| ``` | ||||
|  | ||||
| To attach multiple properties to a meta object, it's possible to invoke `prop` | ||||
| more than once.<br/> | ||||
| It's also possible to invoke `prop` at different times, as long as the factory | ||||
| is reset to the meta object of interest. | ||||
| 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/> | ||||
| @@ -940,7 +878,7 @@ form of a `meta_any` object. | ||||
|  | ||||
| ## Unregister types | ||||
|  | ||||
| A type registered with the reflection system can also be unregistered. This | ||||
| A type registered with the reflection system can also be _unregistered_. This | ||||
| means unregistering all its data members, member functions, conversion functions | ||||
| and so on. However, base classes aren't unregistered as well, since they don't | ||||
| necessarily depend on it.<br/> | ||||
| @@ -969,7 +907,7 @@ A type can be re-registered later with a completely different name and form. | ||||
| ## Meta context | ||||
|  | ||||
| All meta types and their parts are created at runtime and stored in a default | ||||
| _context_. This can be reached via a service locator as: | ||||
| _context_. This is obtained via a service locator as: | ||||
|  | ||||
| ```cpp | ||||
| auto &&context = entt::locator<entt::meta_context>::value_or(); | ||||
| @@ -984,8 +922,8 @@ auto &&context = entt::locator<entt::meta_context>::value_or(); | ||||
| std::swap(context, other); | ||||
| ``` | ||||
|  | ||||
| This can be useful for testing purposes or to define multiple contexts with | ||||
| different meta objects to be used as appropriate. | ||||
| This is useful for testing purposes or to define multiple context objects with | ||||
| different meta type to use as appropriate. | ||||
|  | ||||
| If _replacing_ the default context isn't enough, `EnTT` also offers the ability | ||||
| to use multiple and externally managed contexts with the runtime reflection | ||||
| @@ -998,16 +936,16 @@ entt::meta_ctx context{}; | ||||
| auto factory = entt::meta<my_type>(context).type("reflected_type"_hs); | ||||
| ``` | ||||
|  | ||||
| By doing so, the new meta type won't be available in the default context but | ||||
| will be usable by passing around the new context when needed, such as when | ||||
| creating a new `meta_any` object: | ||||
| By doing so, the new meta type isn't available in the default context but is | ||||
| usable by passing around the new context when needed, such as when creating a | ||||
| new `meta_any` object: | ||||
|  | ||||
| ```cpp | ||||
| entt::meta_any any{context, std::in_place_type<my_type>}; | ||||
| ``` | ||||
|  | ||||
| Similarly, to search for meta types in a context other than the default one, it | ||||
| will be necessary to pass it to the `resolve` function: | ||||
| Similarly, to search for meta types in a context other than the default one, | ||||
| it's necessary to pass it to the `resolve` function: | ||||
|  | ||||
| ```cpp | ||||
| entt::meta_type type = entt::resolve(context, "reflected_type"_hs) | ||||
|   | ||||
							
								
								
									
										78
									
								
								external/entt/entt/docs/md/poly.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										78
									
								
								external/entt/entt/docs/md/poly.md
									
									
									
									
										vendored
									
									
								
							| @@ -26,17 +26,16 @@ This module aims to make it simple and easy to use. | ||||
|  | ||||
| The library allows to define _concepts_ as interfaces to fulfill with concrete | ||||
| classes without having to inherit from a common base.<br/> | ||||
| This is, among others, one of the advantages of static polymorphism in general | ||||
| Among others, this is one of the advantages of static polymorphism in general | ||||
| and of a generic wrapper like that offered by the `poly` class template in | ||||
| particular.<br/> | ||||
| What users get is an object that can be passed around as such and not through a | ||||
| reference or a pointer, as happens when it comes to working with dynamic | ||||
| polymorphism. | ||||
| The result is an object to pass around as such and not through a reference or a | ||||
| pointer, as it happens when it comes to working with dynamic polymorphism. | ||||
|  | ||||
| Since the `poly` class template makes use of `entt::any` internally, it also | ||||
| supports most of its feature. Among the most important, the possibility to | ||||
| create aliases to existing and thus unmanaged objects. This allows users to | ||||
| exploit the static polymorphism while maintaining ownership of objects.<br/> | ||||
| supports most of its feature. For example, the possibility to create aliases to | ||||
| existing and thus unmanaged objects. This allows users to exploit the static | ||||
| polymorphism while maintaining ownership of objects.<br/> | ||||
| Likewise, the `poly` class template also benefits from the small buffer | ||||
| optimization offered by the `entt::any` class and therefore minimizes the number | ||||
| of allocations, avoiding them altogether where possible. | ||||
| @@ -44,7 +43,7 @@ of allocations, avoiding them altogether where possible. | ||||
| ## Other libraries | ||||
|  | ||||
| There are some very interesting libraries regarding static polymorphism.<br/> | ||||
| Among all, the two that I prefer are: | ||||
| The ones that I like more are: | ||||
|  | ||||
| * [`dyno`](https://github.com/ldionne/dyno): runtime polymorphism done right. | ||||
| * [`Poly`](https://github.com/facebook/folly/blob/master/folly/docs/Poly.md): | ||||
| @@ -69,18 +68,18 @@ use the terminology introduced by Eric Niebler) is to define a _concept_ that | ||||
| types will have to adhere to.<br/> | ||||
| For this purpose, the library offers a single class that supports both deduced | ||||
| and fully defined interfaces. Although having interfaces deduced automatically | ||||
| is convenient and allows users to write less code in most cases, this has some | ||||
| is convenient and allows users to write less code in most cases, it has some | ||||
| limitations and it's therefore useful to be able to get around the deduction by | ||||
| providing a custom definition for the static virtual table. | ||||
|  | ||||
| Once the interface is defined, it will be sufficient to provide a generic | ||||
| implementation to fulfill the concept.<br/> | ||||
| Once the interface is defined, a generic implementation is needed to fulfill the | ||||
| concept itself.<br/> | ||||
| Also in this case, the library allows customizations based on types or families | ||||
| of types, so as to be able to go beyond the generic case where necessary. | ||||
|  | ||||
| ## Deduced interface | ||||
|  | ||||
| This is how a concept with a deduced interface is introduced: | ||||
| This is how a concept with a deduced interface is defined: | ||||
|  | ||||
| ```cpp | ||||
| struct Drawable: entt::type_list<> { | ||||
| @@ -108,12 +107,12 @@ struct Drawable: entt::type_list<> { | ||||
| }; | ||||
| ``` | ||||
|  | ||||
| In this case, all parameters must be passed to `invoke` after the reference to | ||||
| In this case, all parameters are passed to `invoke` after the reference to | ||||
| `this` and the return value is whatever the internal call returns.<br/> | ||||
| As for `invoke`, this is a name that is injected into the _concept_ through | ||||
| `Base`, from which one must necessarily inherit. Since it's also a dependent | ||||
| name, the `this-> template` form is unfortunately necessary due to the rules of | ||||
| the language. However, there exists also an alternative that goes through an | ||||
| the language. However, there also exists an alternative that goes through an | ||||
| external call: | ||||
|  | ||||
| ```cpp | ||||
| @@ -165,12 +164,12 @@ struct Drawable: entt::type_list<bool(int) const> { | ||||
|  | ||||
| Why should a user fully define a concept if the function types are the same as | ||||
| the deduced ones?<br> | ||||
| Because, in fact, this is exactly the limitation that can be worked around by | ||||
| manually defining the static virtual table. | ||||
| In fact, this is the limitation that can be worked around by manually defining | ||||
| the static virtual table. | ||||
|  | ||||
| When things are deduced, there is an implicit constraint.<br/> | ||||
| If the concept exposes a member function called `draw` with function type | ||||
| `void()`, a concept can be satisfied: | ||||
| `void()`, a concept is satisfied: | ||||
|  | ||||
| * Either by a class that exposes a member function with the same name and the | ||||
|   same signature. | ||||
| @@ -179,7 +178,7 @@ If the concept exposes a member function called `draw` with function type | ||||
|   interface itself. | ||||
|  | ||||
| In other words, it's not possible to make use of functions not belonging to the | ||||
| interface, even if they are present in the types that fulfill the concept.<br/> | ||||
| interface, even if they're part of the types that fulfill the concept.<br/> | ||||
| Similarly, it's not possible to deduce a function in the static virtual table | ||||
| with a function type different from that of the associated member function in | ||||
| the interface itself. | ||||
| @@ -200,8 +199,8 @@ struct Drawable: entt::type_list<> { | ||||
| }; | ||||
| ``` | ||||
|  | ||||
| In this case, it's stated that the `draw` method of a generic type will be | ||||
| enough to satisfy the requirements of the `Drawable` concept.<br/> | ||||
| In this case, it's stated that the `draw` method of a generic type is enough to | ||||
| satisfy the requirements of the `Drawable` concept.<br/> | ||||
| Both member functions and free functions are supported to fulfill concepts: | ||||
|  | ||||
| ```cpp | ||||
| @@ -251,15 +250,15 @@ struct DrawableAndErasable: entt::type_list<> { | ||||
| ``` | ||||
|  | ||||
| The static virtual table is empty and must remain so.<br/> | ||||
| On the other hand, `type` no longer inherits from `Base` and instead forwards | ||||
| On the other hand, `type` no longer inherits from `Base`. Instead, it forwards | ||||
| its template parameter to the type exposed by the _base class_. Internally, the | ||||
| size of the static virtual table of the base class is used as an offset for the | ||||
| local indexes.<br/> | ||||
| _size_ of the static virtual table of the base class is used as an offset for | ||||
| the local indexes.<br/> | ||||
| Finally, by means of the `value_list_cat_t` utility, the implementation consists | ||||
| in appending the new functions to the previous list. | ||||
|  | ||||
| As for a defined concept instead, also the list of types must be extended, in a | ||||
| similar way to what is shown for the implementation of the above concept.<br/> | ||||
| As for a defined concept instead, the list of types is _extended_ in a similar | ||||
| way to what is shown for the implementation of the above concept.<br/> | ||||
| To do this, it's useful to declare a function that allows to convert a _concept_ | ||||
| into its underlying `type_list` object: | ||||
|  | ||||
| @@ -268,8 +267,8 @@ template<typename... Type> | ||||
| entt::type_list<Type...> as_type_list(const entt::type_list<Type...> &); | ||||
| ``` | ||||
|  | ||||
| The definition isn't strictly required, since the function will only be used | ||||
| through a `decltype` as it follows: | ||||
| The definition isn't strictly required, since the function is only used through | ||||
| a `decltype` as it follows: | ||||
|  | ||||
| ```cpp | ||||
| struct DrawableAndErasable: entt::type_list_cat_t< | ||||
| @@ -286,9 +285,8 @@ Everything else is the same as already shown instead. | ||||
|  | ||||
| # Static polymorphism in the wild | ||||
|  | ||||
| Once the _concept_ and implementation have been introduced, it will be possible | ||||
| to use the `poly` class template to contain instances that meet the | ||||
| requirements: | ||||
| Once the _concept_ and implementation are defined, it's possible to use the | ||||
| `poly` class template to _wrap_ instances that meet the requirements: | ||||
|  | ||||
| ```cpp | ||||
| using drawable = entt::poly<Drawable>; | ||||
| @@ -310,9 +308,9 @@ instance = square{}; | ||||
| instance->draw(); | ||||
| ``` | ||||
|  | ||||
| The `poly` class template offers a wide range of constructors, from the default | ||||
| one (which will return an uninitialized `poly` object) to the copy and move | ||||
| constructors, as well as the ability to create objects in-place.<br/> | ||||
| This class offers a wide range of constructors, from the default one (which | ||||
| returns an uninitialized `poly` object) to the copy and move constructors, as | ||||
| well as the ability to create objects in-place.<br/> | ||||
| Among others, there is also a constructor that allows users to wrap unmanaged | ||||
| objects in a `poly` instance (either const or non-const ones): | ||||
|  | ||||
| @@ -329,14 +327,14 @@ drawable other = instance.as_ref(); | ||||
| ``` | ||||
|  | ||||
| In both cases, although the interface of the `poly` object doesn't change, it | ||||
| won't construct any element or take care of destroying the referenced objects. | ||||
| doesn't construct any element or take care of destroying the referenced objects. | ||||
|  | ||||
| Note also how the underlying concept is accessed via a call to `operator->` and | ||||
| not directly as `instance.draw()`.<br/> | ||||
| This allows users to decouple the API of the wrapper from that of the concept. | ||||
| Therefore, where `instance.data()` will invoke the `data` member function of the | ||||
| poly object, `instance->data()` will map directly to the functionality exposed | ||||
| by the underlying concept. | ||||
| Therefore, where `instance.data()` invokes the `data` member function of the | ||||
| poly object, `instance->data()` maps directly to the functionality exposed by | ||||
| the underlying concept. | ||||
|  | ||||
| # Storage size and alignment requirement | ||||
|  | ||||
| @@ -351,9 +349,9 @@ entt::basic_poly<Drawable, sizeof(double[4]), alignof(double[4])> | ||||
|  | ||||
| The default size is `sizeof(double[2])`, which seems like a good compromise | ||||
| between a buffer that is too large and one unable to hold anything larger than | ||||
| an integer. The alignment requirement is optional instead and by default such | ||||
| that it's the most stringent (the largest) for any object whose size is at most | ||||
| equal to the one provided.<br/> | ||||
| an integer. The alignment requirement is optional and by default such that it's | ||||
| the most stringent (the largest) for any object whose size is at most equal to | ||||
| the one provided.<br/> | ||||
| It's worth noting that providing a size of 0 (which is an accepted value in all | ||||
| respects) will force the system to dynamically allocate the contained objects in | ||||
| all cases. | ||||
|   | ||||
							
								
								
									
										79
									
								
								external/entt/entt/docs/md/process.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										79
									
								
								external/entt/entt/docs/md/process.md
									
									
									
									
										vendored
									
									
								
							| @@ -15,18 +15,17 @@ | ||||
|  | ||||
| # Introduction | ||||
|  | ||||
| Sometimes processes are a useful tool to work around the strict definition of a | ||||
| system and introduce logic in a different way, usually without resorting to the | ||||
| introduction of other components. | ||||
|  | ||||
| `EnTT` offers a minimal support to this paradigm by introducing a few classes | ||||
| that users can use to define and execute cooperative processes. | ||||
| Processes are a useful tool to work around the strict definition of a system and | ||||
| introduce logic in a different way, usually without resorting to other component | ||||
| types.<br/> | ||||
| `EnTT` offers minimal support to this paradigm by introducing a few classes used | ||||
| to define and execute cooperative processes. | ||||
|  | ||||
| # The process | ||||
|  | ||||
| A typical process must inherit from the `process` class template that stays true | ||||
| to the CRTP idiom. Moreover, derived classes must specify what's the intended | ||||
| type for elapsed times. | ||||
| A typical task inherits from the `process` class template that stays true to the | ||||
| CRTP idiom. Moreover, derived classes specify what the intended type for elapsed | ||||
| times is. | ||||
|  | ||||
| A process should expose publicly the following member functions whether needed | ||||
| (note that it isn't required to define a function unless the derived class wants | ||||
| @@ -34,39 +33,38 @@ to _override_ the default behavior): | ||||
|  | ||||
| * `void update(Delta, void *);` | ||||
|  | ||||
|   It's invoked once per tick until a process is explicitly aborted or it | ||||
|   terminates either with or without errors. Even though it's not mandatory to | ||||
|   declare this member function, as a rule of thumb each process should at | ||||
|   least define it to work properly. The `void *` parameter is an opaque pointer | ||||
|   to user data (if any) forwarded directly to the process during an update. | ||||
|   This is invoked once per tick until a process is explicitly aborted or ends | ||||
|   either with or without errors. Even though it's not mandatory to declare this | ||||
|   member function, as a rule of thumb each process should at least define it to | ||||
|   work _properly_. The `void *` parameter is an opaque pointer to user data (if | ||||
|   any) forwarded directly to the process during an update. | ||||
|  | ||||
| * `void init();` | ||||
|  | ||||
|   It's invoked when the process joins the running queue of a scheduler. This | ||||
|   happens as soon as it's attached to the scheduler if the process is a top | ||||
|   level one, otherwise when it replaces its parent if the process is a | ||||
|   continuation. | ||||
|   This is invoked when the process joins the running queue of a scheduler. It | ||||
|   happens usually as soon as the process is attached to the scheduler if it's a | ||||
|   top level one, otherwise when it replaces its parent if it's a _continuation_. | ||||
|  | ||||
| * `void succeeded();` | ||||
|  | ||||
|   It's invoked in case of success, immediately after an update and during the | ||||
|   This is invoked in case of success, immediately after an update and during the | ||||
|   same tick. | ||||
|  | ||||
| * `void failed();` | ||||
|  | ||||
|   It's invoked in case of errors, immediately after an update and during the | ||||
|   This is invoked in case of errors, immediately after an update and during the | ||||
|   same tick. | ||||
|  | ||||
| * `void aborted();` | ||||
|  | ||||
|   It's invoked only if a process is explicitly aborted. There is no guarantee | ||||
|   that it executes in the same tick, this depends solely on whether the | ||||
|   process is aborted immediately or not. | ||||
|   This is invoked only if a process is explicitly aborted. There is no guarantee | ||||
|   that it executes in the same tick, it depends solely on whether the process is | ||||
|   aborted immediately or not. | ||||
|  | ||||
| Derived classes can also change the internal state of a process by invoking | ||||
| `succeed` and `fail`, as well as `pause` and `unpause` the process itself. All | ||||
| these are protected member functions made available to be able to manage the | ||||
| life cycle of a process from a derived class. | ||||
| `succeed` and `fail`, as well as `pause` and `unpause` the process itself.<br/> | ||||
| All these are protected member functions made available to manage the life cycle | ||||
| of a process from a derived class. | ||||
|  | ||||
| Here is a minimal example for the sake of curiosity: | ||||
|  | ||||
| @@ -95,14 +93,14 @@ private: | ||||
|  | ||||
| ## Adaptor | ||||
|  | ||||
| Lambdas and functors can't be used directly with a scheduler for they are not | ||||
| Lambdas and functors can't be used directly with a scheduler because they aren't | ||||
| properly defined processes with managed life cycles.<br/> | ||||
| This class helps in filling the gap and turning lambdas and functors into | ||||
| full-featured processes usable by a scheduler. | ||||
|  | ||||
| The function call operator has a signature similar to the one of the `update` | ||||
| function of a process but for the fact that it receives two extra arguments to | ||||
| call whenever a process is terminated with success or with an error: | ||||
| function of a process but for the fact that it receives two extra callbacks to | ||||
| invoke whenever a process terminates with success or with an error: | ||||
|  | ||||
| ```cpp | ||||
| void(Delta delta, void *data, auto succeed, auto fail); | ||||
| @@ -127,9 +125,9 @@ A cooperative scheduler runs different processes and helps managing their life | ||||
| cycles. | ||||
|  | ||||
| Each process is invoked once per tick. If it terminates, it's removed | ||||
| automatically from the scheduler and it's never invoked again. Otherwise it's | ||||
| automatically from the scheduler and it's never invoked again. Otherwise, it's | ||||
| a good candidate to run one more time the next tick.<br/> | ||||
| A process can also have a child. In this case, the parent process is replaced | ||||
| A process can also have a _child_. In this case, the parent process is replaced | ||||
| with its child when it terminates and only if it returns with success. In case | ||||
| of errors, both the parent process and its child are discarded. This way, it's | ||||
| easy to create chain of processes to run sequentially. | ||||
| @@ -138,18 +136,25 @@ Using a scheduler is straightforward. To create it, users must provide only the | ||||
| type for the elapsed times and no arguments at all: | ||||
|  | ||||
| ```cpp | ||||
| entt::scheduler<std::uint32_t> scheduler; | ||||
| entt::basic_scheduler<std::uint64_t> scheduler; | ||||
| ``` | ||||
|  | ||||
| It has member functions to query its internal data structures, like `empty` or | ||||
| `size`, as well as a `clear` utility to reset it to a clean state: | ||||
| Otherwise, the `scheduler` alias is also available for the most common cases. It | ||||
| uses `std::uint32_t` as a default type: | ||||
|  | ||||
| ```cpp | ||||
| entt::scheduler scheduler; | ||||
| ``` | ||||
|  | ||||
| The class has member functions to query its internal data structures, like | ||||
| `empty` or `size`, as well as a `clear` utility to reset it to a clean state: | ||||
|  | ||||
| ```cpp | ||||
| // checks if there are processes still running | ||||
| const auto empty = scheduler.empty(); | ||||
|  | ||||
| // gets the number of processes still running | ||||
| entt::scheduler<std::uint32_t>::size_type size = scheduler.size(); | ||||
| entt::scheduler::size_type size = scheduler.size(); | ||||
|  | ||||
| // resets the scheduler to its initial state and discards all the processes | ||||
| scheduler.clear(); | ||||
| @@ -173,7 +178,7 @@ To attach a process to a scheduler there are mainly two ways: | ||||
|   ``` | ||||
|  | ||||
| In both cases, the return value is an opaque object that offers a `then` member | ||||
| function to use to create chains of processes to run sequentially.<br/> | ||||
| function used to create chains of processes to run sequentially.<br/> | ||||
| As a minimal example of use: | ||||
|  | ||||
| ```cpp | ||||
| @@ -201,7 +206,7 @@ scheduler.update(delta, &data); | ||||
| ``` | ||||
|  | ||||
| In addition to these functions, the scheduler offers an `abort` member function | ||||
| that can be used to discard all the running processes at once: | ||||
| that is used to discard all the running processes at once: | ||||
|  | ||||
| ```cpp | ||||
| // aborts all the processes abruptly ... | ||||
|   | ||||
							
								
								
									
										28
									
								
								external/entt/entt/docs/md/reference.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								external/entt/entt/docs/md/reference.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,18 +1,35 @@ | ||||
| # Similar projects | ||||
|  | ||||
| <!-- | ||||
| @cond TURN_OFF_DOXYGEN | ||||
| --> | ||||
| # Table of Contents | ||||
|  | ||||
| * [Introduction](#introduction) | ||||
| * [Similar projects](#similar-projects) | ||||
| <!-- | ||||
| @endcond TURN_OFF_DOXYGEN | ||||
| --> | ||||
|  | ||||
| # Introduction | ||||
|  | ||||
| There are many projects similar to `EnTT`, both open source and not.<br/> | ||||
| Some even borrowed some ideas from this library and expressed them in different | ||||
| languages.<br/> | ||||
| Others developed different architectures from scratch and therefore offer | ||||
| alternative solutions with their pros and cons. | ||||
|  | ||||
| Below an incomplete list of those that I've come across so far.<br/> | ||||
| If you know of other similar projects out there, feel free to open an issue or a | ||||
| PR and I'll be glad to add them to this page.<br/> | ||||
| I hope the following lists can grow much more in the future. | ||||
|  | ||||
| # Similar projects | ||||
|  | ||||
| Below an incomplete list of similar projects that I've come across so far.<br/> | ||||
| If some terms or designs aren't clear, I recommend referring to the | ||||
| [_ECS Back and Forth_](https://skypjack.github.io/tags/#ecs) series for all the | ||||
| details. | ||||
|  | ||||
| I hope this list can grow much more in the future: | ||||
|  | ||||
| * C: | ||||
|   * [destral_ecs](https://github.com/roig/destral_ecs): a single-file ECS based | ||||
|     on sparse sets. | ||||
| @@ -34,6 +51,8 @@ I hope this list can grow much more in the future: | ||||
|     solution between an ECS and dynamic mixins. | ||||
|  | ||||
| * C# | ||||
|   * [Arch](https://github.com/genaray/Arch): a simple, fast and _unity entities_ | ||||
|     inspired archetype ECS with optional multithreading.  | ||||
|   * [Entitas](https://github.com/sschmid/Entitas-CSharp): the ECS framework for | ||||
|     C# and Unity, where _reactive systems_ were invented. | ||||
|   * [LeoECS](https://github.com/Leopotam/ecs): simple lightweight C# Entity | ||||
| @@ -70,6 +89,3 @@ I hope this list can grow much more in the future: | ||||
|  | ||||
| * Zig | ||||
|   * [zig-ecs](https://github.com/prime31/zig-ecs): a _zig-ification_ of `EnTT`. | ||||
|  | ||||
| If you know of other resources out there that can be of interest for the reader, | ||||
| feel free to open an issue or a PR and I'll be glad to add them to this page. | ||||
|   | ||||
							
								
								
									
										52
									
								
								external/entt/entt/docs/md/signal.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										52
									
								
								external/entt/entt/docs/md/signal.md
									
									
									
									
										vendored
									
									
								
							| @@ -9,6 +9,7 @@ | ||||
| * [Delegate](#delegate) | ||||
|   * [Runtime arguments](#runtime-arguments) | ||||
|   * [Lambda support](#lambda-support) | ||||
|   * [Raw access](#raw-access) | ||||
| * [Signals](#signals) | ||||
| * [Event dispatcher](#event-dispatcher) | ||||
|   * [Named queues](#named-queues) | ||||
| @@ -38,7 +39,7 @@ lightweight classes to solve the same and many other problems. | ||||
| # Delegate | ||||
|  | ||||
| A delegate can be used as a general purpose invoker with no memory overhead for | ||||
| free functions and member functions provided along with an instance on which to | ||||
| free functions, lambdas and members provided along with an instance on which to | ||||
| invoke them.<br/> | ||||
| It doesn't claim to be a drop-in replacement for an `std::function`, so don't | ||||
| expect to use it whenever an `std::function` fits well. That said, it's most | ||||
| @@ -92,9 +93,9 @@ delegate.connect<&g>(c); | ||||
| delegate(42); | ||||
| ``` | ||||
|  | ||||
| The function `g` is invoked with a reference to `c` and `42`. However, the | ||||
| function type of the delegate is still `void(int)`. This is also the signature | ||||
| of its function call operator.<br/> | ||||
| Function `g` is invoked with a reference to `c` and `42`. However, the function | ||||
| type of the delegate is still `void(int)`. This is also the signature of its | ||||
| function call operator.<br/> | ||||
| Another interesting aspect of the delegate class is that it accepts functions | ||||
| with a list of parameters that is shorter than that of its function type: | ||||
|  | ||||
| @@ -105,9 +106,15 @@ delegate(42); | ||||
| ``` | ||||
|  | ||||
| Where the function type of the delegate is `void(int)` as above. It goes without | ||||
| saying that the extra arguments are silently discarded internally.<br/> | ||||
| This is a nice-to-have feature in a lot of cases, as an example when the | ||||
| `delegate` class is used as a building block of a signal-slot system. | ||||
| saying that the extra arguments are silently discarded internally. This is a | ||||
| nice-to-have feature in a lot of cases, as an example when the `delegate` class | ||||
| is used as a building block of a signal-slot system.<br/> | ||||
| In fact, this filtering works both ways. The class tries to pass its first | ||||
| _count_ arguments **first**, then the last _count_. Watch out for conversion | ||||
| rules if in doubt when connecting a listener!<br/> | ||||
| Arbitrary functions that pull random arguments from the delegate list aren't | ||||
| supported instead. Other feature were preferred, such as support for functions | ||||
| with compatible argument lists although not equal to those of the delegate. | ||||
|  | ||||
| To create and initialize a delegate at once, there are a few specialized | ||||
| constructors. Because of the rules of the language, the listener is provided by | ||||
| @@ -231,6 +238,24 @@ As above, the first parameter (`const void *`) isn't part of the function type | ||||
| of the delegate and is used to dispatch arbitrary user data back and forth. In | ||||
| other terms, the function type of the delegate above is `int(int)`. | ||||
|  | ||||
| ## Raw access | ||||
|  | ||||
| While not recommended, a delegate also allows direct access to the stored | ||||
| callable function target and underlying data, if any.<br/> | ||||
| This makes it possible to bypass the behavior of the delegate itself and force | ||||
| calls on different instances: | ||||
|  | ||||
| ```cpp | ||||
| my_struct other; | ||||
| delegate.target(&other, 42); | ||||
| ``` | ||||
|  | ||||
| It goes without saying that this type of approach is **very** risky, especially | ||||
| since there is no way of knowing whether the contained function was originally a | ||||
| member function of some class, a free function or a lambda.<br/> | ||||
| Another possible (and meaningful) use of this feature is that of identifying a | ||||
| particular delegate through its descriptive _traits_ instead. | ||||
|  | ||||
| # Signals | ||||
|  | ||||
| Signal handlers work with references to classes, function pointers and pointers | ||||
| @@ -290,7 +315,7 @@ sink.disconnect<&foo>(); | ||||
| sink.disconnect<&listener::bar>(instance); | ||||
|  | ||||
| // disconnect all member functions of an instance, if any | ||||
| sink.disconnect(instance); | ||||
| sink.disconnect(&instance); | ||||
|  | ||||
| // discards all listeners at once | ||||
| sink.disconnect(); | ||||
| @@ -300,15 +325,6 @@ As shown above, listeners don't have to strictly follow the signature of the | ||||
| signal. As long as a listener can be invoked with the given arguments to yield a | ||||
| result that is convertible to the given return type, everything works just | ||||
| fine.<br/> | ||||
| It's also possible to connect a listener before other elements already contained | ||||
| by the signal. The `before` function returns a `sink` object that is correctly | ||||
| initialized for the purpose and can be used to connect one or more listeners in | ||||
| order and in the desired position: | ||||
|  | ||||
| ```cpp | ||||
| sink.before<&foo>().connect<&listener::bar>(instance); | ||||
| ``` | ||||
|  | ||||
| In all cases, the `connect` member function returns by default a `connection` | ||||
| object to be used as an alternative to break a connection by means of its | ||||
| `release` member function.<br/> | ||||
| @@ -409,7 +425,7 @@ of them at once: | ||||
|  | ||||
| ```cpp | ||||
| dispatcher.sink<an_event>().disconnect<&listener::receive>(listener); | ||||
| dispatcher.sink<another_event>().disconnect(listener); | ||||
| dispatcher.sink<another_event>().disconnect(&listener); | ||||
| ``` | ||||
|  | ||||
| The `trigger` member function serves the purpose of sending an immediate event | ||||
|   | ||||
							
								
								
									
										74
									
								
								external/entt/entt/natvis/entt/entity.natvis
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										74
									
								
								external/entt/entt/natvis/entt/entity.natvis
									
									
									
									
										vendored
									
									
								
							| @@ -1,62 +1,31 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010"> | ||||
| 	<Type Name="entt::basic_registry<*>"> | ||||
| 		<Intrinsic Name="pools_size" Expression="pools.packed.first_base::value.size()"/> | ||||
| 		<Intrinsic Name="vars_size" Expression="vars.ctx.packed.first_base::value.size()"/> | ||||
| 		<Intrinsic Name="to_entity" Expression="*((entity_traits::entity_type *)&entity) & entity_traits::entity_mask"> | ||||
| 			<Parameter Name="entity" Type="entity_traits::value_type &"/> | ||||
| 		<Intrinsic Name="to_entity" Expression="*((traits_type::entity_type *)&entity) & traits_type::entity_mask"> | ||||
| 			<Parameter Name="entity" Type="traits_type::value_type &"/> | ||||
| 		</Intrinsic> | ||||
| 		<DisplayString>{{ size={ epool.size() } }}</DisplayString> | ||||
| 		<DisplayString>{{ pools={ pools.size() } }}</DisplayString> | ||||
| 		<Expand> | ||||
| 			<Item IncludeView="simple" Name="[epool]">epool,view(simple)nr</Item> | ||||
| 			<Synthetic Name="[epool]" ExcludeView="simple"> | ||||
| 				<DisplayString>{ epool.size() }</DisplayString> | ||||
| 				<Expand> | ||||
| 					<CustomListItems> | ||||
| 						<Variable Name="pos" InitialValue="0" /> | ||||
| 						<Variable Name="last" InitialValue="epool.size()"/> | ||||
| 						<Loop> | ||||
| 							<Break Condition="pos == last"/> | ||||
| 							<If Condition="to_entity(epool[pos]) == pos"> | ||||
| 								<Item Name="[{ pos }]">epool[pos]</Item> | ||||
| 							</If> | ||||
| 							<Exec>++pos</Exec> | ||||
| 						</Loop> | ||||
| 					</CustomListItems> | ||||
| 				</Expand> | ||||
| 			</Synthetic> | ||||
| 			<Synthetic Name="[destroyed]" ExcludeView="simple"> | ||||
| 				<DisplayString>{ to_entity(free_list) != entity_traits::entity_mask }</DisplayString> | ||||
| 				<Expand> | ||||
| 					<CustomListItems> | ||||
| 						<Variable Name="it" InitialValue="to_entity(free_list)" /> | ||||
| 						<Loop> | ||||
| 							<Break Condition="it == entity_traits::entity_mask"/> | ||||
| 							<Item Name="[{ it }]">epool[it]</Item> | ||||
| 							<Exec>it = to_entity(epool[it])</Exec> | ||||
| 						</Loop> | ||||
| 					</CustomListItems> | ||||
| 				</Expand> | ||||
| 			</Synthetic> | ||||
| 			<Item Name="[entities]">entities</Item> | ||||
| 			<Synthetic Name="[pools]"> | ||||
| 				<DisplayString>{ pools_size() }</DisplayString> | ||||
| 				<DisplayString>{ pools.size() }</DisplayString> | ||||
| 				<Expand> | ||||
| 					<IndexListItems ExcludeView="simple"> | ||||
| 						<Size>pools_size()</Size> | ||||
| 						<Size>pools.size()</Size> | ||||
| 						<ValueNode>*pools.packed.first_base::value[$i].element.second</ValueNode> | ||||
| 					</IndexListItems> | ||||
| 					<IndexListItems IncludeView="simple"> | ||||
| 						<Size>pools_size()</Size> | ||||
| 						<Size>pools.size()</Size> | ||||
| 						<ValueNode>*pools.packed.first_base::value[$i].element.second,view(simple)</ValueNode> | ||||
| 					</IndexListItems> | ||||
| 				</Expand> | ||||
| 			</Synthetic> | ||||
| 			<Item Name="[groups]" ExcludeView="simple">groups.size()</Item> | ||||
| 			<Synthetic Name="[vars]"> | ||||
| 				<DisplayString>{ vars_size() }</DisplayString> | ||||
| 				<DisplayString>{ vars.ctx.size() }</DisplayString> | ||||
| 				<Expand> | ||||
| 					<IndexListItems> | ||||
| 						<Size>vars_size()</Size> | ||||
| 						<Size>vars.ctx.size()</Size> | ||||
| 						<ValueNode>vars.ctx.packed.first_base::value[$i].element.second</ValueNode> | ||||
| 					</IndexListItems> | ||||
| 				</Expand> | ||||
| @@ -69,20 +38,20 @@ | ||||
| 			<Item Name="[capacity]" ExcludeView="simple">packed.capacity()</Item> | ||||
| 			<Item Name="[policy]">mode,en</Item> | ||||
| 			<Synthetic Name="[sparse]"> | ||||
| 				<DisplayString>{ sparse.size() * entity_traits::page_size }</DisplayString> | ||||
| 				<DisplayString>{ sparse.size() * traits_type::page_size }</DisplayString> | ||||
| 				<Expand> | ||||
| 					<ExpandedItem IncludeView="simple">sparse,view(simple)</ExpandedItem> | ||||
| 					<CustomListItems ExcludeView="simple"> | ||||
| 						<Variable Name="pos" InitialValue="0"/> | ||||
| 						<Variable Name="page" InitialValue="0"/> | ||||
| 						<Variable Name="offset" InitialValue="0"/> | ||||
| 						<Variable Name="last" InitialValue="sparse.size() * entity_traits::page_size"/> | ||||
| 						<Variable Name="last" InitialValue="sparse.size() * traits_type::page_size"/> | ||||
| 						<Loop> | ||||
| 							<Break Condition="pos == last"/> | ||||
| 							<Exec>page = pos / entity_traits::page_size</Exec> | ||||
| 							<Exec>offset = pos & (entity_traits::page_size - 1)</Exec> | ||||
| 							<If Condition="sparse[page] && (*((entity_traits::entity_type *)&sparse[page][offset]) < ~entity_traits::entity_mask)"> | ||||
| 								<Item Name="[{ pos }]">*((entity_traits::entity_type *)&sparse[page][offset]) & entity_traits::entity_mask</Item> | ||||
| 							<Exec>page = pos / traits_type::page_size</Exec> | ||||
| 							<Exec>offset = pos & (traits_type::page_size - 1)</Exec> | ||||
| 							<If Condition="sparse[page] && (*((traits_type::entity_type *)&sparse[page][offset]) < ~traits_type::entity_mask)"> | ||||
| 								<Item Name="[{ pos }]">*((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask</Item> | ||||
| 							</If> | ||||
| 							<Exec>++pos</Exec> | ||||
| 						</Loop> | ||||
| @@ -98,7 +67,7 @@ | ||||
| 						<Variable Name="last" InitialValue="packed.size()"/> | ||||
| 						<Loop> | ||||
| 							<Break Condition="pos == last"/> | ||||
| 							<If Condition="*((entity_traits::entity_type *)&packed[pos]) < ~entity_traits::entity_mask"> | ||||
| 							<If Condition="*((traits_type::entity_type *)&packed[pos]) < ~traits_type::entity_mask"> | ||||
| 								<Item Name="[{ pos }]">packed[pos]</Item> | ||||
| 							</If> | ||||
| 							<Exec>++pos</Exec> | ||||
| @@ -111,18 +80,19 @@ | ||||
| 	<Type Name="entt::basic_storage<*>"> | ||||
| 		<DisplayString>{{ size={ base_type::packed.size() }, type={ base_type::info->alias,na } }}</DisplayString> | ||||
| 		<Expand> | ||||
| 			<Item Name="[capacity]" Optional="true" ExcludeView="simple">packed.first_base::value.capacity() * comp_traits::page_size</Item> | ||||
| 			<Item Name="[page size]" Optional="true" ExcludeView="simple">comp_traits::page_size</Item> | ||||
| 			<Item Name="[capacity]" Optional="true" ExcludeView="simple">payload.capacity() * traits_type::page_size</Item> | ||||
| 			<Item Name="[page size]" Optional="true" ExcludeView="simple">traits_type::page_size</Item> | ||||
| 			<Item Name="[length]" Optional="true" ExcludeView="simple">length</Item> | ||||
| 			<Item Name="[base]" ExcludeView="simple">(base_type*)this,nand</Item> | ||||
| 			<Item Name="[base]" IncludeView="simple">(base_type*)this,view(simple)nand</Item> | ||||
| 			<!-- having SFINAE-like techniques in natvis is priceless :) --> | ||||
| 			<CustomListItems Condition="packed.first_base::value.size() != 0" Optional="true"> | ||||
| 			<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="*((base_type::entity_traits::entity_type *)&base_type::packed[pos]) < ~base_type::entity_traits::entity_mask"> | ||||
| 						<Item Name="[{ pos }:{ base_type::packed[pos] }]">packed.first_base::value[pos / comp_traits::page_size][pos & (comp_traits::page_size - 1)]</Item> | ||||
| 					<If Condition="*((base_type::traits_type::entity_type *)&base_type::packed[pos]) < ~base_type::traits_type::entity_mask"> | ||||
| 						<Item Name="[{ pos }:{ base_type::packed[pos] }]">payload[pos / traits_type::page_size][pos & (traits_type::page_size - 1)]</Item> | ||||
| 					</If> | ||||
| 					<Exec>++pos</Exec> | ||||
| 				</Loop> | ||||
|   | ||||
							
								
								
									
										3
									
								
								external/entt/entt/natvis/entt/signal.natvis
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								external/entt/entt/natvis/entt/signal.natvis
									
									
									
									
										vendored
									
									
								
							| @@ -8,7 +8,7 @@ | ||||
| 		</Expand> | ||||
| 	</Type> | ||||
| 	<Type Name="entt::basic_dispatcher<*>"> | ||||
| 		<Intrinsic Name="size" Expression="pools.first_base::value.packed.first_base::value.size()"/> | ||||
| 		<Intrinsic Name="size" Expression="pools.first_base::value.size()"/> | ||||
| 		<DisplayString>{{ size={ size() } }}</DisplayString> | ||||
| 		<Expand> | ||||
| 			<Synthetic Name="[pools]"> | ||||
| @@ -50,7 +50,6 @@ | ||||
| 		<DisplayString>{{ type={ "$T1" } }}</DisplayString> | ||||
| 		<Expand> | ||||
| 			<Item Name="[signal]">signal,na</Item> | ||||
| 			<Item Name="[offset]">offset</Item> | ||||
| 		</Expand> | ||||
| 	</Type> | ||||
| </AutoVisualizer> | ||||
|   | ||||
							
								
								
									
										23377
									
								
								external/entt/entt/single_include/entt/entt.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										23377
									
								
								external/entt/entt/single_include/entt/entt.hpp
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										2
									
								
								external/entt/entt/src/entt/config/config.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								external/entt/entt/src/entt/config/config.h
									
									
									
									
										vendored
									
									
								
							| @@ -50,6 +50,8 @@ | ||||
| #    define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) | ||||
| #endif | ||||
|  | ||||
| #define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); | ||||
|  | ||||
| #ifdef ENTT_NO_ETO | ||||
| #    define ENTT_ETO_TYPE(Type) void | ||||
| #else | ||||
|   | ||||
							
								
								
									
										4
									
								
								external/entt/entt/src/entt/config/version.h
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								external/entt/entt/src/entt/config/version.h
									
									
									
									
										vendored
									
									
								
							| @@ -4,8 +4,8 @@ | ||||
| #include "macro.h" | ||||
|  | ||||
| #define ENTT_VERSION_MAJOR 3 | ||||
| #define ENTT_VERSION_MINOR 11 | ||||
| #define ENTT_VERSION_PATCH 1 | ||||
| #define ENTT_VERSION_MINOR 12 | ||||
| #define ENTT_VERSION_PATCH 2 | ||||
|  | ||||
| #define ENTT_VERSION \ | ||||
|     ENTT_XSTR(ENTT_VERSION_MAJOR) \ | ||||
|   | ||||
| @@ -128,51 +128,51 @@ public: | ||||
|         return {it->element.first, it->element.second}; | ||||
|     } | ||||
|  | ||||
|     template<typename ILhs, typename IRhs> | ||||
|     friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept; | ||||
|     template<typename Lhs, typename Rhs> | ||||
|     friend constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept; | ||||
|  | ||||
|     template<typename ILhs, typename IRhs> | ||||
|     friend constexpr bool operator==(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept; | ||||
|     template<typename Lhs, typename Rhs> | ||||
|     friend constexpr bool operator==(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept; | ||||
|  | ||||
|     template<typename ILhs, typename IRhs> | ||||
|     friend constexpr bool operator<(const dense_map_iterator<ILhs> &, const dense_map_iterator<IRhs> &) noexcept; | ||||
|     template<typename Lhs, typename Rhs> | ||||
|     friend constexpr bool operator<(const dense_map_iterator<Lhs> &, const dense_map_iterator<Rhs> &) noexcept; | ||||
|  | ||||
| private: | ||||
|     It it; | ||||
| }; | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.it - rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.it == rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator<(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator<(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.it < rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator>(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator>(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return rhs < lhs; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs > rhs); | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const dense_map_iterator<ILhs> &lhs, const dense_map_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const dense_map_iterator<Lhs> &lhs, const dense_map_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs < rhs); | ||||
| } | ||||
|  | ||||
| @@ -230,13 +230,13 @@ private: | ||||
|     std::size_t offset; | ||||
| }; | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.index() == rhs.index(); | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<ILhs> &lhs, const dense_map_local_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator<Lhs> &lhs, const dense_map_local_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| @@ -266,7 +266,7 @@ class dense_map { | ||||
|     static constexpr std::size_t minimum_capacity = 8u; | ||||
|  | ||||
|     using node_type = internal::dense_map_node<Key, Type>; | ||||
|     using alloc_traits = typename std::allocator_traits<Allocator>; | ||||
|     using alloc_traits = std::allocator_traits<Allocator>; | ||||
|     static_assert(std::is_same_v<typename alloc_traits::value_type, std::pair<const Key, Type>>, "Invalid value type"); | ||||
|     using sparse_container_type = std::vector<std::size_t, typename alloc_traits::template rebind_alloc<std::size_t>>; | ||||
|     using packed_container_type = std::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>; | ||||
| @@ -464,7 +464,6 @@ public: | ||||
|     /** | ||||
|      * @brief Returns an iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first instance of the internal array. | ||||
|      * If the array is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first instance of the internal array. | ||||
| @@ -485,11 +484,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last instance | ||||
|      * of the internal array. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last instance of the | ||||
|      * internal array. | ||||
|      */ | ||||
| @@ -838,7 +832,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc equal_range */ | ||||
|     template<class Other> | ||||
|     template<typename Other> | ||||
|     [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>> | ||||
|     equal_range(const Other &key) const { | ||||
|         const auto it = find(key); | ||||
|   | ||||
| @@ -96,51 +96,51 @@ public: | ||||
|         return *operator->(); | ||||
|     } | ||||
|  | ||||
|     template<typename ILhs, typename IRhs> | ||||
|     friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept; | ||||
|     template<typename Lhs, typename Rhs> | ||||
|     friend constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept; | ||||
|  | ||||
|     template<typename ILhs, typename IRhs> | ||||
|     friend constexpr bool operator==(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept; | ||||
|     template<typename Lhs, typename Rhs> | ||||
|     friend constexpr bool operator==(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept; | ||||
|  | ||||
|     template<typename ILhs, typename IRhs> | ||||
|     friend constexpr bool operator<(const dense_set_iterator<ILhs> &, const dense_set_iterator<IRhs> &) noexcept; | ||||
|     template<typename Lhs, typename Rhs> | ||||
|     friend constexpr bool operator<(const dense_set_iterator<Lhs> &, const dense_set_iterator<Rhs> &) noexcept; | ||||
|  | ||||
| private: | ||||
|     It it; | ||||
| }; | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.it - rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.it == rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator<(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator<(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.it < rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator>(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator>(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return rhs < lhs; | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs > rhs); | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const dense_set_iterator<ILhs> &lhs, const dense_set_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const dense_set_iterator<Lhs> &lhs, const dense_set_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs < rhs); | ||||
| } | ||||
|  | ||||
| @@ -195,13 +195,13 @@ private: | ||||
|     std::size_t offset; | ||||
| }; | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept { | ||||
|     return lhs.index() == rhs.index(); | ||||
| } | ||||
|  | ||||
| template<typename ILhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<ILhs> &lhs, const dense_set_local_iterator<IRhs> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator<Lhs> &lhs, const dense_set_local_iterator<Rhs> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| @@ -410,7 +410,6 @@ public: | ||||
|     /** | ||||
|      * @brief Returns an iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first instance of the internal array. | ||||
|      * If the array is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first instance of the internal array. | ||||
| @@ -431,11 +430,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last instance | ||||
|      * of the internal array. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last instance of the | ||||
|      * internal array. | ||||
|      */ | ||||
| @@ -691,7 +685,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc equal_range */ | ||||
|     template<class Other> | ||||
|     template<typename Other> | ||||
|     [[nodiscard]] std::enable_if_t<is_transparent_v<hasher> && is_transparent_v<key_equal>, std::conditional_t<false, Other, std::pair<const_iterator, const_iterator>>> | ||||
|     equal_range(const Other &value) const { | ||||
|         const auto it = find(value); | ||||
|   | ||||
| @@ -95,14 +95,15 @@ struct radix_sort { | ||||
|     template<typename It, typename Getter = identity> | ||||
|     void operator()(It first, It last, Getter getter = Getter{}) const { | ||||
|         if(first < last) { | ||||
|             static constexpr auto mask = (1 << Bit) - 1; | ||||
|             static constexpr auto buckets = 1 << Bit; | ||||
|             static constexpr auto passes = N / Bit; | ||||
|             constexpr auto passes = N / Bit; | ||||
|  | ||||
|             using value_type = typename std::iterator_traits<It>::value_type; | ||||
|             std::vector<value_type> aux(std::distance(first, last)); | ||||
|  | ||||
|             auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { | ||||
|                 constexpr auto mask = (1 << Bit) - 1; | ||||
|                 constexpr auto buckets = 1 << Bit; | ||||
|  | ||||
|                 std::size_t index[buckets]{}; | ||||
|                 std::size_t count[buckets]{}; | ||||
|  | ||||
|   | ||||
							
								
								
									
										22
									
								
								external/entt/entt/src/entt/core/any.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								external/entt/entt/src/entt/core/any.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -59,7 +59,7 @@ class basic_any { | ||||
|     }; | ||||
|  | ||||
|     template<typename Type> | ||||
|     static constexpr bool in_situ = Len && 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) { | ||||
| @@ -128,17 +128,17 @@ class basic_any { | ||||
|             vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>; | ||||
|  | ||||
|             if constexpr(std::is_lvalue_reference_v<Type>) { | ||||
|                 static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments"); | ||||
|                 static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); | ||||
|                 mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref; | ||||
|                 instance = (std::addressof(args), ...); | ||||
|             } else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) { | ||||
|                 if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<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 { | ||||
|                     new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...); | ||||
|                 } | ||||
|             } else { | ||||
|                 if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<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>>>)) { | ||||
|                     instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...}; | ||||
|                 } else { | ||||
|                     instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...); | ||||
| @@ -428,7 +428,7 @@ private: | ||||
|  * @return The element converted to the requested type. | ||||
|  */ | ||||
| template<typename Type, std::size_t Len, std::size_t Align> | ||||
| 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); | ||||
| @@ -436,7 +436,7 @@ Type any_cast(const basic_any<Len, Align> &data) noexcept { | ||||
|  | ||||
| /*! @copydoc any_cast */ | ||||
| template<typename Type, std::size_t Len, std::size_t Align> | ||||
| 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"); | ||||
| @@ -445,7 +445,7 @@ Type any_cast(basic_any<Len, Align> &data) noexcept { | ||||
|  | ||||
| /*! @copydoc any_cast */ | ||||
| template<typename Type, std::size_t Len, std::size_t Align> | ||||
| 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)); | ||||
| @@ -461,14 +461,14 @@ Type any_cast(basic_any<Len, Align> &&data) noexcept { | ||||
|  | ||||
| /*! @copydoc any_cast */ | ||||
| template<typename Type, std::size_t Len, std::size_t Align> | ||||
| const Type *any_cast(const basic_any<Len, Align> *data) noexcept { | ||||
| [[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept { | ||||
|     const auto &info = type_id<std::remove_cv_t<Type>>(); | ||||
|     return static_cast<const Type *>(data->data(info)); | ||||
| } | ||||
|  | ||||
| /*! @copydoc any_cast */ | ||||
| template<typename Type, std::size_t Len, std::size_t Align> | ||||
| Type *any_cast(basic_any<Len, Align> *data) noexcept { | ||||
| [[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept { | ||||
|     if constexpr(std::is_const_v<Type>) { | ||||
|         // last attempt to make wrappers for const references return their values | ||||
|         return any_cast<Type>(&std::as_const(*data)); | ||||
| @@ -488,7 +488,7 @@ Type *any_cast(basic_any<Len, Align> *data) noexcept { | ||||
|  * @return A properly initialized wrapper for an object of the given type. | ||||
|  */ | ||||
| template<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args> | ||||
| basic_any<Len, Align> make_any(Args &&...args) { | ||||
| [[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) { | ||||
|     return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...}; | ||||
| } | ||||
|  | ||||
| @@ -501,7 +501,7 @@ basic_any<Len, Align> make_any(Args &&...args) { | ||||
|  * @return A properly initialized and not necessarily owning wrapper. | ||||
|  */ | ||||
| template<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type> | ||||
| basic_any<Len, Align> forward_as_any(Type &&value) { | ||||
| [[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) { | ||||
|     return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)}; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -67,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 hs_traits = internal::fnv1a_traits<id_type>; | ||||
|     using traits_type = internal::fnv1a_traits<id_type>; | ||||
|  | ||||
|     struct const_wrapper { | ||||
|         // non-explicit constructor on purpose | ||||
| @@ -79,10 +79,10 @@ class basic_hashed_string: internal::basic_hashed_string<Char> { | ||||
|  | ||||
|     // Fowler–Noll–Vo hash function v. 1a - the good | ||||
|     [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { | ||||
|         base_type base{str, 0u, hs_traits::offset}; | ||||
|         base_type base{str, 0u, traits_type::offset}; | ||||
|  | ||||
|         for(; str[base.length]; ++base.length) { | ||||
|             base.hash = (base.hash ^ static_cast<hs_traits::type>(str[base.length])) * hs_traits::prime; | ||||
|             base.hash = (base.hash ^ static_cast<traits_type::type>(str[base.length])) * traits_type::prime; | ||||
|         } | ||||
|  | ||||
|         return base; | ||||
| @@ -90,10 +90,10 @@ class basic_hashed_string: internal::basic_hashed_string<Char> { | ||||
|  | ||||
|     // Fowler–Noll–Vo 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, hs_traits::offset}; | ||||
|         base_type base{str, len, traits_type::offset}; | ||||
|  | ||||
|         for(size_type pos{}; pos < len; ++pos) { | ||||
|             base.hash = (base.hash ^ static_cast<hs_traits::type>(str[pos])) * hs_traits::prime; | ||||
|             base.hash = (base.hash ^ static_cast<traits_type::type>(str[pos])) * traits_type::prime; | ||||
|         } | ||||
|  | ||||
|         return base; | ||||
|   | ||||
							
								
								
									
										4
									
								
								external/entt/entt/src/entt/core/memory.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								external/entt/entt/src/entt/core/memory.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -106,7 +106,7 @@ constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[ma | ||||
|  | ||||
| /** | ||||
|  * @brief Deleter for allocator-aware unique pointers (waiting for C++20). | ||||
|  * @tparam Args Types of arguments to use to construct the object. | ||||
|  * @tparam Allocator Type of allocator used to manage memory and elements. | ||||
|  */ | ||||
| template<typename Allocator> | ||||
| struct allocation_deleter: private Allocator { | ||||
| @@ -127,7 +127,7 @@ struct allocation_deleter: private Allocator { | ||||
|      * @param ptr A valid pointer to an object of the given type. | ||||
|      */ | ||||
|     constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v<typename allocator_type::value_type>) { | ||||
|         using alloc_traits = typename std::allocator_traits<Allocator>; | ||||
|         using alloc_traits = std::allocator_traits<Allocator>; | ||||
|         alloc_traits::destroy(*this, to_address(ptr)); | ||||
|         alloc_traits::deallocate(*this, ptr, 1u); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										6
									
								
								external/entt/entt/src/entt/core/tuple.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								external/entt/entt/src/entt/core/tuple.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -69,7 +69,7 @@ struct forward_apply: private Func { | ||||
|      * @tparam Args Types of arguments to use to construct the new instance. | ||||
|      * @param args Parameters to use to construct the instance. | ||||
|      */ | ||||
|     template<class... Args> | ||||
|     template<typename... Args> | ||||
|     constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v<Func, Args...>) | ||||
|         : Func{std::forward<Args>(args)...} {} | ||||
|  | ||||
| @@ -79,13 +79,13 @@ struct forward_apply: private Func { | ||||
|      * @param args Parameters to forward to the underlying function. | ||||
|      * @return Return value of the underlying function, if any. | ||||
|      */ | ||||
|     template<class Type> | ||||
|     template<typename Type> | ||||
|     constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval<Func &>(), args))) { | ||||
|         return std::apply(static_cast<Func &>(*this), std::forward<Type>(args)); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc operator()() */ | ||||
|     template<class Type> | ||||
|     template<typename Type> | ||||
|     constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval<const Func &>(), args))) { | ||||
|         return std::apply(static_cast<const Func &>(*this), std::forward<Type>(args)); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										168
									
								
								external/entt/entt/src/entt/core/type_traits.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										168
									
								
								external/entt/entt/src/entt/core/type_traits.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,7 @@ | ||||
|  | ||||
| #include <cstddef> | ||||
| #include <iterator> | ||||
| #include <tuple> | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include "../config/config.h" | ||||
| @@ -55,7 +56,6 @@ using type_identity_t = typename type_identity<Type>::type; | ||||
| /** | ||||
|  * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. | ||||
|  * @tparam Type The type of which to return the size. | ||||
|  * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. | ||||
|  */ | ||||
| template<typename Type, typename = void> | ||||
| struct size_of: std::integral_constant<std::size_t, 0u> {}; | ||||
| @@ -297,7 +297,8 @@ struct type_list_contains; | ||||
|  * @tparam Other Type to look for. | ||||
|  */ | ||||
| template<typename... Type, typename Other> | ||||
| struct type_list_contains<type_list<Type...>, Other>: std::disjunction<std::is_same<Type, Other>...> {}; | ||||
| struct type_list_contains<type_list<Type...>, Other> | ||||
|     : std::bool_constant<(std::is_same_v<Type, Other> || ...)> {}; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper variable template. | ||||
| @@ -385,10 +386,20 @@ struct value_list_element<Index, value_list<Value, Other...>> | ||||
|  */ | ||||
| template<auto Value, auto... Other> | ||||
| struct value_list_element<0u, value_list<Value, Other...>> { | ||||
|     /*! @brief Searched type. */ | ||||
|     using type = decltype(Value); | ||||
|     /*! @brief Searched value. */ | ||||
|     static constexpr auto value = Value; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper type. | ||||
|  * @tparam Index Index of the type to return. | ||||
|  * @tparam List Value list to search into. | ||||
|  */ | ||||
| template<std::size_t Index, typename List> | ||||
| using value_list_element_t = typename value_list_element<Index, List>::type; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper type. | ||||
|  * @tparam Index Index of the value to return. | ||||
| @@ -397,6 +408,58 @@ struct value_list_element<0u, value_list<Value, Other...>> { | ||||
| template<std::size_t Index, typename List> | ||||
| inline constexpr auto value_list_element_v = value_list_element<Index, List>::value; | ||||
|  | ||||
| /*! @brief Primary template isn't defined on purpose. */ | ||||
| template<auto, typename> | ||||
| struct value_list_index; | ||||
|  | ||||
| /** | ||||
|  * @brief Provides compile-time type access to the values of a value list. | ||||
|  * @tparam Value Value to look for and for which to return the index. | ||||
|  * @tparam First First value provided by the value list. | ||||
|  * @tparam Other Other values provided by the value list. | ||||
|  */ | ||||
| template<auto Value, auto First, auto... Other> | ||||
| struct value_list_index<Value, value_list<First, Other...>> { | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using value_type = std::size_t; | ||||
|     /*! @brief Compile-time position of the given value in the sublist. */ | ||||
|     static constexpr value_type value = 1u + value_list_index<Value, value_list<Other...>>::value; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Provides compile-time type access to the values of a value list. | ||||
|  * @tparam Value Value to look for and for which to return the index. | ||||
|  * @tparam Other Other values provided by the value list. | ||||
|  */ | ||||
| template<auto Value, auto... Other> | ||||
| struct value_list_index<Value, value_list<Value, Other...>> { | ||||
|     static_assert(value_list_index<Value, value_list<Other...>>::value == sizeof...(Other), "Non-unique type"); | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using value_type = std::size_t; | ||||
|     /*! @brief Compile-time position of the given value in the sublist. */ | ||||
|     static constexpr value_type value = 0u; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Provides compile-time type access to the values of a value list. | ||||
|  * @tparam Value Value to look for and for which to return the index. | ||||
|  */ | ||||
| template<auto Value> | ||||
| struct value_list_index<Value, value_list<>> { | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using value_type = std::size_t; | ||||
|     /*! @brief Compile-time position of the given type in the sublist. */ | ||||
|     static constexpr value_type value = 0u; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper variable template. | ||||
|  * @tparam List Value list. | ||||
|  * @tparam Value Value to look for and for which to return the index. | ||||
|  */ | ||||
| template<auto Value, typename List> | ||||
| inline constexpr std::size_t value_list_index_v = value_list_index<Value, List>::value; | ||||
|  | ||||
| /** | ||||
|  * @brief Concatenates multiple value lists. | ||||
|  * @tparam Value Values provided by the first value list. | ||||
| @@ -448,6 +511,89 @@ struct value_list_cat<value_list<Value...>> { | ||||
| template<typename... List> | ||||
| using value_list_cat_t = typename value_list_cat<List...>::type; | ||||
|  | ||||
| /*! @brief Primary template isn't defined on purpose. */ | ||||
| template<typename> | ||||
| struct value_list_unique; | ||||
|  | ||||
| /** | ||||
|  * @brief Removes duplicates values from a value list. | ||||
|  * @tparam Value One of the values provided by the given value list. | ||||
|  * @tparam Other The other values provided by the given value list. | ||||
|  */ | ||||
| template<auto Value, auto... Other> | ||||
| struct value_list_unique<value_list<Value, Other...>> { | ||||
|     /*! @brief A value list without duplicate types. */ | ||||
|     using type = std::conditional_t< | ||||
|         ((Value == Other) || ...), | ||||
|         typename value_list_unique<value_list<Other...>>::type, | ||||
|         value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>; | ||||
| }; | ||||
|  | ||||
| /*! @brief Removes duplicates values from a value list. */ | ||||
| template<> | ||||
| struct value_list_unique<value_list<>> { | ||||
|     /*! @brief A value list without duplicate types. */ | ||||
|     using type = value_list<>; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper type. | ||||
|  * @tparam Type A value list. | ||||
|  */ | ||||
| template<typename Type> | ||||
| using value_list_unique_t = typename value_list_unique<Type>::type; | ||||
|  | ||||
| /** | ||||
|  * @brief Provides the member constant `value` to true if a value list contains | ||||
|  * a given value, false otherwise. | ||||
|  * @tparam List Value list. | ||||
|  * @tparam Value Value to look for. | ||||
|  */ | ||||
| template<typename List, auto Value> | ||||
| struct value_list_contains; | ||||
|  | ||||
| /** | ||||
|  * @copybrief value_list_contains | ||||
|  * @tparam Value Values provided by the value list. | ||||
|  * @tparam Other Value to look for. | ||||
|  */ | ||||
| template<auto... Value, auto Other> | ||||
| struct value_list_contains<value_list<Value...>, Other> | ||||
|     : std::bool_constant<((Value == Other) || ...)> {}; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper variable template. | ||||
|  * @tparam List Value list. | ||||
|  * @tparam Value Value to look for. | ||||
|  */ | ||||
| template<typename List, auto Value> | ||||
| inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value; | ||||
|  | ||||
| /*! @brief Primary template isn't defined on purpose. */ | ||||
| template<typename...> | ||||
| class value_list_diff; | ||||
|  | ||||
| /** | ||||
|  * @brief Computes the difference between two value lists. | ||||
|  * @tparam Value Values provided by the first value list. | ||||
|  * @tparam Other Values provided by the second value list. | ||||
|  */ | ||||
| template<auto... Value, auto... Other> | ||||
| 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<v141_toolset_workaround, Value>, value_list<>, value_list<Value>>...>; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper type. | ||||
|  * @tparam List Value lists between which to compute the difference. | ||||
|  */ | ||||
| template<typename... List> | ||||
| using value_list_diff_t = typename value_list_diff<List...>::type; | ||||
|  | ||||
| /*! @brief Same as std::is_invocable, but with tuples. */ | ||||
| template<typename, typename> | ||||
| struct is_applicable: std::false_type {}; | ||||
| @@ -568,7 +714,7 @@ inline constexpr bool is_iterator_v = is_iterator<Type>::value; | ||||
|  */ | ||||
| template<typename Type> | ||||
| struct is_ebco_eligible | ||||
|     : std::conjunction<std::is_empty<Type>, std::negation<std::is_final<Type>>> {}; | ||||
|     : std::bool_constant<std::is_empty_v<Type> && !std::is_final_v<Type>> {}; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper variable template. | ||||
| @@ -659,6 +805,10 @@ template<typename 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. | ||||
|  * @tparam Type The type to test. | ||||
| @@ -755,4 +905,16 @@ using nth_argument_t = typename nth_argument<Index, Candidate>::type; | ||||
|  | ||||
| } // namespace entt | ||||
|  | ||||
| template<typename... Type> | ||||
| struct std::tuple_size<entt::type_list<Type...>>: std::integral_constant<std::size_t, entt::type_list<Type...>::size> {}; | ||||
|  | ||||
| template<std::size_t Index, typename... Type> | ||||
| struct std::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {}; | ||||
|  | ||||
| template<auto... Value> | ||||
| struct std::tuple_size<entt::value_list<Value...>>: std::integral_constant<std::size_t, entt::value_list<Value...>::size> {}; | ||||
|  | ||||
| template<std::size_t Index, auto... Value> | ||||
| struct std::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {}; | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										12
									
								
								external/entt/entt/src/entt/core/utility.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								external/entt/entt/src/entt/core/utility.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -17,7 +17,7 @@ struct identity { | ||||
|      * @param value The actual argument. | ||||
|      * @return The submitted value as-is. | ||||
|      */ | ||||
|     template<class Type> | ||||
|     template<typename Type> | ||||
|     [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { | ||||
|         return std::forward<Type>(value); | ||||
|     } | ||||
| @@ -50,7 +50,7 @@ template<typename Func> | ||||
|  * @brief Helper type for visitors. | ||||
|  * @tparam Func Types of function objects. | ||||
|  */ | ||||
| template<class... Func> | ||||
| template<typename... Func> | ||||
| struct overloaded: Func... { | ||||
|     using Func::operator()...; | ||||
| }; | ||||
| @@ -59,14 +59,14 @@ struct overloaded: Func... { | ||||
|  * @brief Deduction guide. | ||||
|  * @tparam Func Types of function objects. | ||||
|  */ | ||||
| template<class... Func> | ||||
| template<typename... Func> | ||||
| overloaded(Func...) -> overloaded<Func...>; | ||||
|  | ||||
| /** | ||||
|  * @brief Basic implementation of a y-combinator. | ||||
|  * @tparam Func Type of a potentially recursive function. | ||||
|  */ | ||||
| template<class Func> | ||||
| template<typename Func> | ||||
| struct y_combinator { | ||||
|     /** | ||||
|      * @brief Constructs a y-combinator from a given function. | ||||
| @@ -81,13 +81,13 @@ struct y_combinator { | ||||
|      * @param args Parameters to use to invoke the underlying function. | ||||
|      * @return Return value of the underlying function, if any. | ||||
|      */ | ||||
|     template<class... Args> | ||||
|     template<typename... Args> | ||||
|     constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) { | ||||
|         return func(*this, std::forward<Args>(args)...); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc operator()() */ | ||||
|     template<class... Args> | ||||
|     template<typename... Args> | ||||
|     constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v<Func, y_combinator &, Args...>) { | ||||
|         return func(*this, std::forward<Args>(args)...); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										14
									
								
								external/entt/entt/src/entt/entity/component.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								external/entt/entt/src/entt/entity/component.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -4,6 +4,7 @@ | ||||
| #include <cstddef> | ||||
| #include <type_traits> | ||||
| #include "../config/config.h" | ||||
| #include "fwd.hpp" | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| @@ -17,6 +18,9 @@ namespace internal { | ||||
| template<typename Type, typename = void> | ||||
| struct in_place_delete: std::bool_constant<!(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>)> {}; | ||||
|  | ||||
| template<> | ||||
| struct in_place_delete<void>: std::false_type {}; | ||||
|  | ||||
| template<typename Type> | ||||
| struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>> | ||||
|     : std::true_type {}; | ||||
| @@ -24,6 +28,9 @@ struct in_place_delete<Type, std::enable_if_t<Type::in_place_delete>> | ||||
| template<typename Type, typename = void> | ||||
| struct page_size: std::integral_constant<std::size_t, !std::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {}; | ||||
|  | ||||
| template<> | ||||
| struct page_size<void>: std::integral_constant<std::size_t, 0u> {}; | ||||
|  | ||||
| template<typename Type> | ||||
| 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> {}; | ||||
| @@ -52,13 +59,6 @@ struct component_traits { | ||||
|     static constexpr std::size_t page_size = internal::page_size<Type>::value; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper variable template. | ||||
|  * @tparam Type Type of component. | ||||
|  */ | ||||
| template<class Type> | ||||
| inline constexpr bool ignore_as_empty_v = (std::is_void_v<Type> || component_traits<Type>::page_size == 0u); | ||||
|  | ||||
| } // namespace entt | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										98
									
								
								external/entt/entt/src/entt/entity/entity.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										98
									
								
								external/entt/entt/src/entt/entity/entity.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -16,35 +16,47 @@ namespace entt { | ||||
|  | ||||
| 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; | ||||
|  | ||||
| template<typename Type> | ||||
| struct entt_traits<Type, std::enable_if_t<std::is_enum_v<Type>>> | ||||
|     : entt_traits<std::underlying_type_t<Type>> {}; | ||||
|     : entt_traits<std::underlying_type_t<Type>> { | ||||
|     using value_type = Type; | ||||
| }; | ||||
|  | ||||
| template<typename Type> | ||||
| struct entt_traits<Type, std::enable_if_t<std::is_class_v<Type>>> | ||||
|     : entt_traits<typename Type::entity_type> {}; | ||||
|     : entt_traits<typename Type::entity_type> { | ||||
|     using value_type = Type; | ||||
| }; | ||||
|  | ||||
| template<> | ||||
| struct entt_traits<std::uint32_t> { | ||||
|     using value_type = std::uint32_t; | ||||
|  | ||||
|     using entity_type = std::uint32_t; | ||||
|     using version_type = std::uint16_t; | ||||
|  | ||||
|     static constexpr entity_type entity_mask = 0xFFFFF; | ||||
|     static constexpr entity_type version_mask = 0xFFF; | ||||
|     static constexpr std::size_t entity_shift = 20u; | ||||
| }; | ||||
|  | ||||
| template<> | ||||
| struct entt_traits<std::uint64_t> { | ||||
|     using value_type = std::uint64_t; | ||||
|  | ||||
|     using entity_type = std::uint64_t; | ||||
|     using version_type = std::uint32_t; | ||||
|  | ||||
|     static constexpr entity_type entity_mask = 0xFFFFFFFF; | ||||
|     static constexpr entity_type version_mask = 0xFFFFFFFF; | ||||
|     static constexpr std::size_t entity_shift = 32u; | ||||
| }; | ||||
|  | ||||
| } // namespace internal | ||||
| @@ -55,24 +67,28 @@ struct entt_traits<std::uint64_t> { | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * @brief Entity traits. | ||||
|  * @tparam Type Type of identifier. | ||||
|  * @brief Common basic entity traits implementation. | ||||
|  * @tparam Traits Actual entity traits to use. | ||||
|  */ | ||||
| template<typename Type> | ||||
| class entt_traits: internal::entt_traits<Type> { | ||||
|     using base_type = internal::entt_traits<Type>; | ||||
| template<typename Traits> | ||||
| class basic_entt_traits { | ||||
|     static constexpr auto length = internal::popcount(Traits::entity_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. */ | ||||
|     using value_type = Type; | ||||
|     using value_type = typename Traits::value_type; | ||||
|     /*! @brief Underlying entity type. */ | ||||
|     using entity_type = typename base_type::entity_type; | ||||
|     using entity_type = typename Traits::entity_type; | ||||
|     /*! @brief Underlying version type. */ | ||||
|     using version_type = typename base_type::version_type; | ||||
|     /*! @brief Reserved identifier. */ | ||||
|     static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift); | ||||
|     /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ | ||||
|     static constexpr auto page_size = ENTT_SPARSE_PAGE; | ||||
|     using version_type = typename Traits::version_type; | ||||
|  | ||||
|     /*! @brief Entity mask size. */ | ||||
|     static constexpr entity_type entity_mask = Traits::entity_mask; | ||||
|     /*! @brief Version mask size */ | ||||
|     static constexpr entity_type version_mask = Traits::version_mask; | ||||
|  | ||||
|     /** | ||||
|      * @brief Converts an entity to its underlying type. | ||||
| @@ -89,7 +105,7 @@ public: | ||||
|      * @return The integral representation of the entity part. | ||||
|      */ | ||||
|     [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { | ||||
|         return (to_integral(value) & base_type::entity_mask); | ||||
|         return (to_integral(value) & entity_mask); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -98,7 +114,17 @@ public: | ||||
|      * @return The integral representation of the version part. | ||||
|      */ | ||||
|     [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { | ||||
|         return (to_integral(value) >> base_type::entity_shift); | ||||
|         return static_cast<version_type>(to_integral(value) >> length); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the successor of a given identifier. | ||||
|      * @param value The identifier of which to return the successor. | ||||
|      * @return The successor of the given identifier. | ||||
|      */ | ||||
|     [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { | ||||
|         const auto vers = to_version(value) + 1; | ||||
|         return construct(to_entity(value), static_cast<version_type>(vers + (vers == version_mask))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -112,7 +138,7 @@ public: | ||||
|      * @return A properly constructed identifier. | ||||
|      */ | ||||
|     [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { | ||||
|         return value_type{(entity & base_type::entity_mask) | (static_cast<entity_type>(version) << base_type::entity_shift)}; | ||||
|         return value_type{(entity & entity_mask) | (static_cast<entity_type>(version) << length)}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -126,11 +152,23 @@ public: | ||||
|      * @return A properly constructed identifier. | ||||
|      */ | ||||
|     [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { | ||||
|         constexpr auto mask = (base_type::version_mask << base_type::entity_shift); | ||||
|         return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; | ||||
|         constexpr auto mask = (version_mask << length); | ||||
|         return value_type{(lhs & entity_mask) | (rhs & mask)}; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Entity traits. | ||||
|  * @tparam Type Type of identifier. | ||||
|  */ | ||||
| template<typename Type> | ||||
| struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> { | ||||
|     /*! @brief Base type. */ | ||||
|     using base_type = basic_entt_traits<internal::entt_traits<Type>>; | ||||
|     /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ | ||||
|     static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @copydoc entt_traits<Entity>::to_integral | ||||
|  * @tparam Entity The value type. | ||||
| @@ -167,8 +205,9 @@ struct null_t { | ||||
|      */ | ||||
|     template<typename Entity> | ||||
|     [[nodiscard]] constexpr operator Entity() const noexcept { | ||||
|         using entity_traits = entt_traits<Entity>; | ||||
|         return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); | ||||
|         using traits_type = entt_traits<Entity>; | ||||
|         constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -197,8 +236,8 @@ struct null_t { | ||||
|      */ | ||||
|     template<typename Entity> | ||||
|     [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { | ||||
|         using entity_traits = entt_traits<Entity>; | ||||
|         return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); | ||||
|         using traits_type = entt_traits<Entity>; | ||||
|         return traits_type::to_entity(entity) == traits_type::to_entity(*this); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -246,8 +285,9 @@ struct tombstone_t { | ||||
|      */ | ||||
|     template<typename Entity> | ||||
|     [[nodiscard]] constexpr operator Entity() const noexcept { | ||||
|         using entity_traits = entt_traits<Entity>; | ||||
|         return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); | ||||
|         using traits_type = entt_traits<Entity>; | ||||
|         constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -276,8 +316,8 @@ struct tombstone_t { | ||||
|      */ | ||||
|     template<typename Entity> | ||||
|     [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { | ||||
|         using entity_traits = entt_traits<Entity>; | ||||
|         return entity_traits::to_version(entity) == entity_traits::to_version(*this); | ||||
|         using traits_type = entt_traits<Entity>; | ||||
|         return traits_type::to_version(entity) == traits_type::to_version(*this); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										67
									
								
								external/entt/entt/src/entt/entity/fwd.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										67
									
								
								external/entt/entt/src/entt/entity/fwd.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,7 @@ | ||||
| #ifndef ENTT_ENTITY_FWD_HPP | ||||
| #define ENTT_ENTITY_FWD_HPP | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <memory> | ||||
| #include <type_traits> | ||||
| #include "../core/fwd.hpp" | ||||
| @@ -11,6 +12,14 @@ namespace entt { | ||||
| /*! @brief Default entity identifier. */ | ||||
| enum class entity : id_type {}; | ||||
|  | ||||
| /*! @brief Storage deletion policy. */ | ||||
| enum class deletion_policy : std::uint8_t { | ||||
|     /*! @brief Swap-and-pop deletion policy. */ | ||||
|     swap_and_pop = 0u, | ||||
|     /*! @brief In-place deletion policy. */ | ||||
|     in_place = 1u | ||||
| }; | ||||
|  | ||||
| template<typename Entity = entity, typename = std::allocator<Entity>> | ||||
| class basic_sparse_set; | ||||
|  | ||||
| @@ -18,18 +27,18 @@ template<typename Type, typename = entity, typename = std::allocator<Type>, type | ||||
| class basic_storage; | ||||
|  | ||||
| template<typename Type> | ||||
| class sigh_storage_mixin; | ||||
| class sigh_mixin; | ||||
|  | ||||
| /** | ||||
|  * @brief Provides a common way to define storage types. | ||||
|  * @tparam Type Storage value type. | ||||
|  * @tparam Entity A valid entity type (see entt_traits for more details). | ||||
|  * @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_storage_mixin<basic_storage<Type, Entity, Allocator>>; | ||||
|     using type = sigh_mixin<basic_storage<Type, Entity, Allocator>>; | ||||
| }; | ||||
|  | ||||
| /** | ||||
| @@ -42,7 +51,7 @@ 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 (see entt_traits for more details). | ||||
|  * @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>>> | ||||
| @@ -70,7 +79,7 @@ class basic_runtime_view; | ||||
| template<typename, typename, typename> | ||||
| class basic_group; | ||||
|  | ||||
| template<typename> | ||||
| template<typename, typename Mask = std::uint32_t, typename = std::allocator<Mask>> | ||||
| class basic_observer; | ||||
|  | ||||
| template<typename> | ||||
| @@ -93,7 +102,10 @@ class basic_continuous_loader; | ||||
|  * @tparam Type List of types. | ||||
|  */ | ||||
| template<typename... Type> | ||||
| using exclude_t = type_list<Type...>; | ||||
| struct exclude_t final: type_list<Type...> { | ||||
|     /*! @brief Default constructor. */ | ||||
|     explicit constexpr exclude_t() {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Variable template for exclusion lists. | ||||
| @@ -107,7 +119,10 @@ inline constexpr exclude_t<Type...> exclude{}; | ||||
|  * @tparam Type List of types. | ||||
|  */ | ||||
| template<typename... Type> | ||||
| using get_t = type_list<Type...>; | ||||
| struct get_t final: type_list<Type...> { | ||||
|     /*! @brief Default constructor. */ | ||||
|     explicit constexpr get_t() {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Variable template for lists of observed components. | ||||
| @@ -121,7 +136,10 @@ inline constexpr get_t<Type...> get{}; | ||||
|  * @tparam Type List of types. | ||||
|  */ | ||||
| template<typename... Type> | ||||
| using owned_t = type_list<Type...>; | ||||
| struct owned_t final: type_list<Type...> { | ||||
|     /*! @brief Default constructor. */ | ||||
|     explicit constexpr owned_t() {} | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Variable template for lists of owned components. | ||||
| @@ -130,6 +148,39 @@ using owned_t = type_list<Type...>; | ||||
| 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<>; | ||||
|  | ||||
|   | ||||
							
								
								
									
										566
									
								
								external/entt/entt/src/entt/entity/group.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										566
									
								
								external/entt/entt/src/entt/entity/group.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -5,9 +5,10 @@ | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include "../config/config.h" | ||||
| #include "../core/fwd.hpp" | ||||
| #include "../core/iterator.hpp" | ||||
| #include "../core/type_info.hpp" | ||||
| #include "../core/type_traits.hpp" | ||||
| #include "component.hpp" | ||||
| #include "entity.hpp" | ||||
| #include "fwd.hpp" | ||||
| #include "sparse_set.hpp" | ||||
| @@ -29,7 +30,7 @@ template<typename It, typename... Owned, typename... Get> | ||||
| class extended_group_iterator<It, owned_t<Owned...>, get_t<Get...>> { | ||||
|     template<typename Type> | ||||
|     auto index_to_element([[maybe_unused]] Type &cpool) const { | ||||
|         if constexpr(ignore_as_empty_v<typename Type::value_type>) { | ||||
|         if constexpr(Type::traits_type::page_size == 0u) { | ||||
|             return std::make_tuple(); | ||||
|         } else { | ||||
|             return std::forward_as_tuple(cpool.rbegin()[it.index()]); | ||||
| @@ -37,6 +38,7 @@ 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>; | ||||
| @@ -68,6 +70,10 @@ public: | ||||
|         return operator*(); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] constexpr iterator_type base() const noexcept { | ||||
|         return it; | ||||
|     } | ||||
|  | ||||
|     template<typename... Lhs, typename... Rhs> | ||||
|     friend constexpr bool operator==(const extended_group_iterator<Lhs...> &, const extended_group_iterator<Rhs...> &) noexcept; | ||||
|  | ||||
| @@ -86,6 +92,164 @@ template<typename... Lhs, typename... Rhs> | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| struct group_descriptor { | ||||
|     using size_type = std::size_t; | ||||
|     virtual ~group_descriptor() = default; | ||||
|     virtual size_type owned(const id_type *, const size_type) const noexcept { | ||||
|         return 0u; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| 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) { | ||||
|         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, 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, 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(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { | ||||
|             swap_elements(--len, entt); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 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 = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { | ||||
|             push_on_construct(*first); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     size_type owned(const id_type *elem, const size_type length) const noexcept final { | ||||
|         size_type cnt = 0u; | ||||
|  | ||||
|         for(auto pos = 0u; pos < length; ++pos) { | ||||
|             cnt += ((elem[pos] == entt::type_hash<typename Owned::value_type>::value()) || ...); | ||||
|         } | ||||
|  | ||||
|         return cnt; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] size_type length() const noexcept { | ||||
|         return len; | ||||
|     } | ||||
|  | ||||
|     template<typename Type> | ||||
|     Type pools_as() const noexcept { | ||||
|         return pools; | ||||
|     } | ||||
|  | ||||
|     template<typename Type> | ||||
|     Type filter_as() const noexcept { | ||||
|         return filter; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::tuple<Owned *..., Get *...> pools; | ||||
|     std::tuple<Exclude *...> filter; | ||||
|     std::size_t len; | ||||
| }; | ||||
|  | ||||
| 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) | ||||
|            && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) | ||||
|            && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { | ||||
|             elem.push(entt); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void push_on_destroy(const entity_type entt) { | ||||
|         if(!elem.contains(entt) | ||||
|            && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) | ||||
|            && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { | ||||
|             elem.push(entt); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void remove_if(const entity_type entt) { | ||||
|         elem.remove(entt); | ||||
|     } | ||||
|  | ||||
| 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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     common_type &handle() noexcept { | ||||
|         return elem; | ||||
|     } | ||||
|  | ||||
|     const common_type &handle() const noexcept { | ||||
|         return elem; | ||||
|     } | ||||
|  | ||||
|     template<typename Type> | ||||
|     Type pools_as() const noexcept { | ||||
|         return pools; | ||||
|     } | ||||
|  | ||||
|     template<typename Type> | ||||
|     Type filter_as() const noexcept { | ||||
|         return filter; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::tuple<Get *...> pools; | ||||
|     std::tuple<Exclude *...> filter; | ||||
|     base_type elem; | ||||
| }; | ||||
|  | ||||
| } // namespace internal | ||||
|  | ||||
| /** | ||||
| @@ -119,18 +283,28 @@ class basic_group; | ||||
|  * * The entity currently pointed is destroyed. | ||||
|  * | ||||
|  * In all other cases, modifying the pools iterated by the group in any way | ||||
|  * invalidates all the iterators and using them results in undefined behavior. | ||||
|  * invalidates all the iterators. | ||||
|  * | ||||
|  * @tparam Get Types of storage _observed_ by the group. | ||||
|  * @tparam Exclude Types of storage used to filter the group. | ||||
|  */ | ||||
| template<typename... Get, typename... Exclude> | ||||
| class basic_group<owned_t<>, get_t<Get...>, exclude_t<Exclude...>> { | ||||
|     using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>; | ||||
|     using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>; | ||||
|     using base_type = std::common_type_t<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 Get::value_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...>>; | ||||
|  | ||||
|     auto pools() const noexcept { | ||||
|         using return_type = std::tuple<Get *...>; | ||||
|         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: | ||||
|     /*! @brief Underlying entity identifier. */ | ||||
| @@ -138,53 +312,59 @@ public: | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using size_type = std::size_t; | ||||
|     /*! @brief Common type among all storage types. */ | ||||
|     using base_type = basic_common_type; | ||||
|     using common_type = base_type; | ||||
|     /*! @brief Random access iterator type. */ | ||||
|     using iterator = typename base_type::iterator; | ||||
|     using iterator = typename common_type::iterator; | ||||
|     /*! @brief Reversed iterator type. */ | ||||
|     using reverse_iterator = typename base_type::reverse_iterator; | ||||
|     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<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 | ||||
|         : handler{} {} | ||||
|         : descriptor{} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a group from a set of storage classes. | ||||
|      * @param ref The actual entities to iterate. | ||||
|      * @param gpool Storage types to iterate _observed_ by the group. | ||||
|      * @param ref A reference to a group handler. | ||||
|      */ | ||||
|     basic_group(basic_common_type &ref, Get &...gpool) noexcept | ||||
|         : handler{&ref}, | ||||
|           pools{&gpool...} {} | ||||
|     basic_group(handler &ref) noexcept | ||||
|         : descriptor{&ref} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a const reference to the underlying handler. | ||||
|      * @return A const reference to the underlying handler. | ||||
|      * @brief Returns the leading storage of a group. | ||||
|      * @return The leading storage of the group. | ||||
|      */ | ||||
|     [[nodiscard]] const base_type &handle() const noexcept { | ||||
|         return *handler; | ||||
|     [[nodiscard]] const common_type &handle() const noexcept { | ||||
|         return descriptor->handle(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given component 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]] decltype(auto) storage() const noexcept { | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         return storage<index_of<Type>>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given index. | ||||
|      * @brief Returns the storage for a given index, if any. | ||||
|      * @tparam Index Index of the storage to return. | ||||
|      * @return The storage for the given index. | ||||
|      */ | ||||
|     template<std::size_t Index> | ||||
|     [[nodiscard]] decltype(auto) storage() const noexcept { | ||||
|         return *std::get<Index>(pools); | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         constexpr auto offset = sizeof...(Get); | ||||
|  | ||||
|         if constexpr(Index < offset) { | ||||
|             return std::get<Index>(pools()); | ||||
|         } else { | ||||
|             return std::get<Index - offset>(filter()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -192,7 +372,7 @@ public: | ||||
|      * @return Number of entities that are part of the group. | ||||
|      */ | ||||
|     [[nodiscard]] size_type size() const noexcept { | ||||
|         return *this ? handler->size() : size_type{}; | ||||
|         return *this ? handle().size() : size_type{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -201,13 +381,13 @@ public: | ||||
|      * @return Capacity of the group. | ||||
|      */ | ||||
|     [[nodiscard]] size_type capacity() const noexcept { | ||||
|         return *this ? handler->capacity() : size_type{}; | ||||
|         return *this ? handle().capacity() : size_type{}; | ||||
|     } | ||||
|  | ||||
|     /*! @brief Requests the removal of unused capacity. */ | ||||
|     void shrink_to_fit() { | ||||
|         if(*this) { | ||||
|             handler->shrink_to_fit(); | ||||
|             descriptor->handle().shrink_to_fit(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -216,60 +396,48 @@ public: | ||||
|      * @return True if the group is empty, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool empty() const noexcept { | ||||
|         return !*this || handler->empty(); | ||||
|         return !*this || handle().empty(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the group. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the group. If the | ||||
|      * group is empty, the returned iterator will be equal to `end()`. | ||||
|      * If the group is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the group. | ||||
|      */ | ||||
|     [[nodiscard]] iterator begin() const noexcept { | ||||
|         return *this ? handler->begin() : iterator{}; | ||||
|         return *this ? handle().begin() : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the group. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the group. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the | ||||
|      * group. | ||||
|      */ | ||||
|     [[nodiscard]] iterator end() const noexcept { | ||||
|         return *this ? handler->end() : iterator{}; | ||||
|         return *this ? handle().end() : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the reversed group. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the reversed group. | ||||
|      * If the group is empty, the returned iterator will be equal to `rend()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the reversed group. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterator rbegin() const noexcept { | ||||
|         return *this ? handler->rbegin() : reverse_iterator{}; | ||||
|         return *this ? handle().rbegin() : reverse_iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the reversed | ||||
|      * group. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the reversed group. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the | ||||
|      * reversed group. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterator rend() const noexcept { | ||||
|         return *this ? handler->rend() : reverse_iterator{}; | ||||
|         return *this ? handle().rend() : reverse_iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -299,8 +467,7 @@ public: | ||||
|      * iterator otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] iterator find(const entity_type entt) const noexcept { | ||||
|         const auto it = *this ? handler->find(entt) : iterator{}; | ||||
|         return it != end() && *it == entt ? it : end(); | ||||
|         return *this ? handle().find(entt) : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -317,7 +484,7 @@ public: | ||||
|      * @return True if the group is properly initialized, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] explicit operator bool() const noexcept { | ||||
|         return handler != nullptr; | ||||
|         return descriptor != nullptr; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -326,32 +493,47 @@ public: | ||||
|      * @return True if the group contains the given entity, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool contains(const entity_type entt) const noexcept { | ||||
|         return *this && handler->contains(entt); | ||||
|         return *this && handle().contains(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the components assigned to the given entity. | ||||
|      * | ||||
|      * Prefer this function instead of `registry::get` during iterations. It has | ||||
|      * far better performance than its counterpart. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to use an invalid component type results in a compilation | ||||
|      * error. Attempting to use an entity that doesn't belong to the group | ||||
|      * results in undefined behavior. | ||||
|      * Attempting to use an entity that doesn't belong to the group results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam Type Types of components to get. | ||||
|      * @tparam Type Type of the component to get. | ||||
|      * @tparam Other Other types of components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<typename... Type> | ||||
|     template<typename Type, typename... Other> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         if constexpr(sizeof...(Type) == 0) { | ||||
|             return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); | ||||
|         } else if constexpr(sizeof...(Type) == 1) { | ||||
|             return (std::get<index_of<Type>>(pools)->get(entt), ...); | ||||
|         return get<index_of<Type>, index_of<Other>...>(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the components assigned to the given entity. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to use an entity that doesn't belong to the groups results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam Index Indexes of the components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<std::size_t... Index> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         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); | ||||
|         } else if constexpr(sizeof...(Index) == 1) { | ||||
|             return (std::get<Index>(cpools)->get(entt), ...); | ||||
|         } else { | ||||
|             return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...); | ||||
|             return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -402,16 +584,13 @@ public: | ||||
|      * @return An iterable object to use to _visit_ the group. | ||||
|      */ | ||||
|     [[nodiscard]] iterable each() const noexcept { | ||||
|         return iterable{{begin(), pools}, {end(), pools}}; | ||||
|         const auto cpools = pools(); | ||||
|         return iterable{{begin(), cpools}, {end(), cpools}}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sort a group according to the given comparison function. | ||||
|      * | ||||
|      * Sort the group so that iterating it with a couple of iterators returns | ||||
|      * entities and components in the expected order. See `begin` and `end` for | ||||
|      * more details. | ||||
|      * | ||||
|      * The comparison function object must return `true` if the first element | ||||
|      * is _less_ than the second one, `false` otherwise. The signature of the | ||||
|      * comparison function should be equivalent to one of the following: | ||||
| @@ -433,7 +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 types of components 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. | ||||
| @@ -441,52 +621,60 @@ public: | ||||
|      * @param algo A valid sort function object. | ||||
|      * @param args Arguments to forward to the sort function object, if any. | ||||
|      */ | ||||
|     template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args> | ||||
|     template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args> | ||||
|     void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { | ||||
|         sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sort a group according to the given comparison function. | ||||
|      * | ||||
|      * @sa sort | ||||
|      * | ||||
|      * @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. | ||||
|      * @param compare A valid comparison function object. | ||||
|      * @param algo A valid sort function object. | ||||
|      * @param args Arguments to forward to the sort function object, if any. | ||||
|      */ | ||||
|     template<std::size_t... Index, typename Compare, typename Sort = std_sort, typename... Args> | ||||
|     void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { | ||||
|         if(*this) { | ||||
|             if constexpr(sizeof...(Type) == 0) { | ||||
|             if constexpr(sizeof...(Index) == 0) { | ||||
|                 static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); | ||||
|                 handler->sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); | ||||
|                 descriptor->handle().sort(std::move(compare), std::move(algo), std::forward<Args>(args)...); | ||||
|             } else { | ||||
|                 auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { | ||||
|                     if constexpr(sizeof...(Type) == 1) { | ||||
|                         return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(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 { | ||||
|                         return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...)); | ||||
|                         return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...)); | ||||
|                     } | ||||
|                 }; | ||||
|  | ||||
|                 handler->sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); | ||||
|                 descriptor->handle().sort(std::move(comp), std::move(algo), std::forward<Args>(args)...); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sort the shared pool of entities according to the given component. | ||||
|      * @brief Sort the shared pool of entities according to a given storage. | ||||
|      * | ||||
|      * Non-owning groups of the same type share with the registry a pool of | ||||
|      * entities with its own order that doesn't depend on the order of any pool | ||||
|      * of components. Users can order the underlying data structure so that it | ||||
|      * respects the order of the pool of the given component. | ||||
|      * | ||||
|      * @note | ||||
|      * The shared pool of entities and thus its order is affected by the changes | ||||
|      * to each and every pool that it tracks. Therefore changes to those pools | ||||
|      * can quickly ruin the order imposed to the pool of entities shared between | ||||
|      * the non-owning groups. | ||||
|      * to each and every pool that it tracks. | ||||
|      * | ||||
|      * @tparam Type Type of component to use to impose the order. | ||||
|      * @param other The storage to use to impose the order. | ||||
|      */ | ||||
|     template<typename Type> | ||||
|     void sort() const { | ||||
|     void sort_as(const common_type &other) const { | ||||
|         if(*this) { | ||||
|             handler->respect(*std::get<index_of<Type>>(pools)); | ||||
|             descriptor->handle().sort_as(other); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     base_type *const handler; | ||||
|     const std::tuple<Get *...> pools; | ||||
|     handler *descriptor; | ||||
| }; | ||||
|  | ||||
| /** | ||||
| @@ -514,7 +702,7 @@ private: | ||||
|  * * The entity currently pointed is destroyed. | ||||
|  * | ||||
|  * In all other cases, modifying the pools iterated by the group in any way | ||||
|  * invalidates all the iterators and using them results in undefined behavior. | ||||
|  * invalidates all the iterators. | ||||
|  * | ||||
|  * @tparam Owned Types of storage _owned_ by the group. | ||||
|  * @tparam Get Types of storage _observed_ by the group. | ||||
| @@ -522,11 +710,21 @@ private: | ||||
|  */ | ||||
| template<typename... Owned, typename... Get, typename... Exclude> | ||||
| class basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>> { | ||||
|     using underlying_type = std::common_type_t<typename Owned::entity_type..., typename Get::entity_type..., typename Exclude::entity_type...>; | ||||
|     using basic_common_type = std::common_type_t<typename Owned::base_type..., typename Get::base_type..., typename Exclude::base_type...>; | ||||
|     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::value_type..., typename Get::value_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...>>; | ||||
|  | ||||
|     auto pools() const noexcept { | ||||
|         using return_type = std::tuple<Owned *..., Get *...>; | ||||
|         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: | ||||
|     /*! @brief Underlying entity identifier. */ | ||||
| @@ -534,46 +732,59 @@ public: | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using size_type = std::size_t; | ||||
|     /*! @brief Common type among all storage types. */ | ||||
|     using base_type = basic_common_type; | ||||
|     using common_type = base_type; | ||||
|     /*! @brief Random access iterator type. */ | ||||
|     using iterator = typename base_type::iterator; | ||||
|     using iterator = typename common_type::iterator; | ||||
|     /*! @brief Reversed iterator type. */ | ||||
|     using reverse_iterator = typename base_type::reverse_iterator; | ||||
|     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<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 | ||||
|         : length{} {} | ||||
|         : descriptor{} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a group from a set of storage classes. | ||||
|      * @param extent The actual number of entities to iterate. | ||||
|      * @param opool Storage types to iterate _owned_ by the group. | ||||
|      * @param gpool Storage types to iterate _observed_ by the group. | ||||
|      * @param ref A reference to a group handler. | ||||
|      */ | ||||
|     basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept | ||||
|         : pools{&opool..., &gpool...}, | ||||
|           length{&extent} {} | ||||
|     basic_group(handler &ref) noexcept | ||||
|         : descriptor{&ref} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given component type. | ||||
|      * @brief Returns the leading storage of a group. | ||||
|      * @return The leading storage of the group. | ||||
|      */ | ||||
|     [[nodiscard]] const common_type &handle() const noexcept { | ||||
|         return *storage<0>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @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]] decltype(auto) storage() const noexcept { | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         return storage<index_of<Type>>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given index. | ||||
|      * @brief Returns the storage for a given index, if any. | ||||
|      * @tparam Index Index of the storage to return. | ||||
|      * @return The storage for the given index. | ||||
|      */ | ||||
|     template<std::size_t Index> | ||||
|     [[nodiscard]] decltype(auto) storage() const noexcept { | ||||
|         return *std::get<Index>(pools); | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         constexpr auto offset = sizeof...(Owned) + sizeof...(Get); | ||||
|  | ||||
|         if constexpr(Index < offset) { | ||||
|             return std::get<Index>(pools()); | ||||
|         } else { | ||||
|             return std::get<Index - offset>(filter()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -581,7 +792,7 @@ public: | ||||
|      * @return Number of entities that that are part of the group. | ||||
|      */ | ||||
|     [[nodiscard]] size_type size() const noexcept { | ||||
|         return *this ? *length : size_type{}; | ||||
|         return *this ? descriptor->length() : size_type{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -589,60 +800,48 @@ public: | ||||
|      * @return True if the group is empty, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool empty() const noexcept { | ||||
|         return !*this || !*length; | ||||
|         return !*this || !descriptor->length(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the group. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the group. If the | ||||
|      * group is empty, the returned iterator will be equal to `end()`. | ||||
|      * If the group is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the group. | ||||
|      */ | ||||
|     [[nodiscard]] iterator begin() const noexcept { | ||||
|         return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; | ||||
|         return *this ? (handle().end() - descriptor->length()) : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the group. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the group. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the | ||||
|      * group. | ||||
|      */ | ||||
|     [[nodiscard]] iterator end() const noexcept { | ||||
|         return *this ? std::get<0>(pools)->base_type::end() : iterator{}; | ||||
|         return *this ? handle().end() : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the reversed group. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the reversed group. | ||||
|      * If the group is empty, the returned iterator will be equal to `rend()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the reversed group. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterator rbegin() const noexcept { | ||||
|         return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; | ||||
|         return *this ? handle().rbegin() : reverse_iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the reversed | ||||
|      * group. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the reversed group. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the | ||||
|      * reversed group. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterator rend() const noexcept { | ||||
|         return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; | ||||
|         return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -672,8 +871,8 @@ public: | ||||
|      * iterator otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] iterator find(const entity_type entt) const noexcept { | ||||
|         const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; | ||||
|         return it != end() && it >= begin() && *it == entt ? it : end(); | ||||
|         const auto it = *this ? handle().find(entt) : iterator{}; | ||||
|         return it >= begin() ? it : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -690,7 +889,7 @@ public: | ||||
|      * @return True if the group is properly initialized, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] explicit operator bool() const noexcept { | ||||
|         return length != nullptr; | ||||
|         return descriptor != nullptr; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -699,32 +898,47 @@ public: | ||||
|      * @return True if the group contains the given entity, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool contains(const entity_type entt) const noexcept { | ||||
|         return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); | ||||
|         return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the components assigned to the given entity. | ||||
|      * | ||||
|      * Prefer this function instead of `registry::get` during iterations. It has | ||||
|      * far better performance than its counterpart. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to use an invalid component type results in a compilation | ||||
|      * error. Attempting to use an entity that doesn't belong to the group | ||||
|      * results in undefined behavior. | ||||
|      * Attempting to use an entity that doesn't belong to the group results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam Type Types of components to get. | ||||
|      * @tparam Type Type of the component to get. | ||||
|      * @tparam Other Other types of components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<typename... Type> | ||||
|     template<typename Type, typename... Other> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         if constexpr(sizeof...(Type) == 0) { | ||||
|             return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); | ||||
|         } else if constexpr(sizeof...(Type) == 1) { | ||||
|             return (std::get<index_of<Type>>(pools)->get(entt), ...); | ||||
|         return get<index_of<Type>, index_of<Other>...>(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the components assigned to the given entity. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to use an entity that doesn't belong to the groups results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam Index Indexes of the components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<std::size_t... Index> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         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); | ||||
|         } else if constexpr(sizeof...(Index) == 1) { | ||||
|             return (std::get<Index>(cpools)->get(entt), ...); | ||||
|         } else { | ||||
|             return std::tuple_cat(std::get<index_of<Type>>(pools)->get_as_tuple(entt)...); | ||||
|             return std::tuple_cat(std::get<Index>(cpools)->get_as_tuple(entt)...); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -775,16 +989,13 @@ public: | ||||
|      * @return An iterable object to use to _visit_ the group. | ||||
|      */ | ||||
|     [[nodiscard]] iterable each() const noexcept { | ||||
|         return {{begin(), pools}, {end(), pools}}; | ||||
|         const auto cpools = pools(); | ||||
|         return {{begin(), cpools}, {end(), cpools}}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sort a group according to the given comparison function. | ||||
|      * | ||||
|      * Sort the group so that iterating it with a couple of iterators returns | ||||
|      * entities and components in the expected order. See `begin` and `end` for | ||||
|      * more details. | ||||
|      * | ||||
|      * The comparison function object must return `true` if the first element | ||||
|      * is _less_ than the second one, `false` otherwise. The signature of the | ||||
|      * comparison function should be equivalent to one of the following: | ||||
| @@ -807,7 +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 types of components 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. | ||||
| @@ -815,36 +1027,56 @@ public: | ||||
|      * @param algo A valid sort function object. | ||||
|      * @param args Arguments to forward to the sort function object, if any. | ||||
|      */ | ||||
|     template<typename... Type, typename Compare, typename Sort = std_sort, typename... Args> | ||||
|     template<typename Type, typename... Other, typename Compare, typename Sort = std_sort, typename... Args> | ||||
|     void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { | ||||
|         if constexpr(sizeof...(Type) == 0) { | ||||
|         sort<index_of<Type>, index_of<Other>...>(std::move(compare), std::move(algo), std::forward<Args>(args)...); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sort a group according to the given comparison function. | ||||
|      * | ||||
|      * @sa sort | ||||
|      * | ||||
|      * @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. | ||||
|      * @param compare A valid comparison function object. | ||||
|      * @param algo A valid sort function object. | ||||
|      * @param args Arguments to forward to the sort function object, if any. | ||||
|      */ | ||||
|     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(); | ||||
|  | ||||
|         if constexpr(sizeof...(Index) == 0) { | ||||
|             static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>, "Invalid comparison function"); | ||||
|             std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward<Args>(args)...); | ||||
|             storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward<Args>(args)...); | ||||
|         } else { | ||||
|             auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { | ||||
|                 if constexpr(sizeof...(Type) == 1) { | ||||
|                     return compare((std::get<index_of<Type>>(pools)->get(lhs), ...), (std::get<index_of<Type>>(pools)->get(rhs), ...)); | ||||
|             auto comp = [&compare, &cpools](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 { | ||||
|                     return compare(std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(lhs)...), std::forward_as_tuple(std::get<index_of<Type>>(pools)->get(rhs)...)); | ||||
|                     return compare(std::forward_as_tuple(std::get<Index>(cpools)->get(lhs)...), std::forward_as_tuple(std::get<Index>(cpools)->get(rhs)...)); | ||||
|                 } | ||||
|             }; | ||||
|  | ||||
|             std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward<Args>(args)...); | ||||
|             storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward<Args>(args)...); | ||||
|         } | ||||
|  | ||||
|         std::apply([this](auto *head, auto *...other) { | ||||
|             for(auto next = *length; next; --next) { | ||||
|         auto cb = [this](auto *head, auto *...other) { | ||||
|             for(auto next = descriptor->length(); next; --next) { | ||||
|                 const auto pos = next - 1; | ||||
|                 [[maybe_unused]] const auto entt = head->data()[pos]; | ||||
|                 (other->swap_elements(other->data()[pos], entt), ...); | ||||
|             } | ||||
|         }, | ||||
|                    pools); | ||||
|         }; | ||||
|  | ||||
|         std::apply(cb, cpools); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     const std::tuple<Owned *..., Get *...> pools; | ||||
|     const size_type *const length; | ||||
|     handler *descriptor; | ||||
| }; | ||||
|  | ||||
| } // namespace entt | ||||
|   | ||||
							
								
								
									
										10
									
								
								external/entt/entt/src/entt/entity/handle.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								external/entt/entt/src/entt/entity/handle.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -43,7 +43,9 @@ public: | ||||
|         : entt{value}, | ||||
|           it{from}, | ||||
|           last{to} { | ||||
|         while(it != last && !it->second.contains(entt)) { ++it; } | ||||
|         while(it != last && !it->second.contains(entt)) { | ||||
|             ++it; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     constexpr handle_storage_iterator &operator++() noexcept { | ||||
| @@ -141,7 +143,7 @@ struct basic_handle { | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a const handle from a non-const one. | ||||
|      * @tparam Other A valid entity type (see entt_traits for more details). | ||||
|      * @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. | ||||
| @@ -196,7 +198,7 @@ struct basic_handle { | ||||
|  | ||||
|     /*! @brief Destroys the entity associated with a handle. */ | ||||
|     void destroy() { | ||||
|         reg->destroy(entt); | ||||
|         reg->destroy(std::exchange(entt, null)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -204,7 +206,7 @@ struct basic_handle { | ||||
|      * @param version A desired version upon destruction. | ||||
|      */ | ||||
|     void destroy(const version_type version) { | ||||
|         reg->destroy(entt, version); | ||||
|         reg->destroy(std::exchange(entt, null), version); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										128
									
								
								external/entt/entt/src/entt/entity/helper.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										128
									
								
								external/entt/entt/src/entt/entity/helper.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -3,10 +3,10 @@ | ||||
|  | ||||
| #include <memory> | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include "../core/fwd.hpp" | ||||
| #include "../core/type_traits.hpp" | ||||
| #include "../signal/delegate.hpp" | ||||
| #include "component.hpp" | ||||
| #include "fwd.hpp" | ||||
| #include "group.hpp" | ||||
| #include "view.hpp" | ||||
| @@ -28,7 +28,7 @@ public: | ||||
|     /*! @brief Type of registry to convert. */ | ||||
|     using registry_type = Registry; | ||||
|     /*! @brief Underlying entity identifier. */ | ||||
|     using entity_type = std::remove_const_t<typename registry_type::entity_type>; | ||||
|     using entity_type = typename registry_type::entity_type; | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a converter for a given registry. | ||||
| @@ -71,7 +71,7 @@ public: | ||||
|     /*! @brief Type of registry to convert. */ | ||||
|     using registry_type = Registry; | ||||
|     /*! @brief Underlying entity identifier. */ | ||||
|     using entity_type = std::remove_const_t<typename registry_type::entity_type>; | ||||
|     using entity_type = typename registry_type::entity_type; | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a converter for a given registry. | ||||
| @@ -126,19 +126,133 @@ void invoke(Registry ®, const typename Registry::entity_type entt) { | ||||
|  */ | ||||
| template<typename Registry, typename Component> | ||||
| typename Registry::entity_type to_entity(const Registry ®, const Component &instance) { | ||||
|     const auto &storage = reg.template storage<Component>(); | ||||
|     const typename Registry::base_type &base = storage; | ||||
|     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 += component_traits<Component>::page_size) { | ||||
|         if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(component_traits<Component>::page_size)) { | ||||
|         for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { | ||||
|             if(const auto dist = (addr - std::addressof(storage->get(*it))); dist >= 0 && dist < static_cast<decltype(dist)>(page_size)) { | ||||
|                 return *(it + dist); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return null; | ||||
| } | ||||
|  | ||||
| /*! @brief Primary template isn't defined on purpose. */ | ||||
| template<typename...> | ||||
| struct sigh_helper; | ||||
|  | ||||
| /** | ||||
|  * @brief Signal connection helper for registries. | ||||
|  * @tparam Registry Basic registry type. | ||||
|  */ | ||||
| template<typename Registry> | ||||
| struct sigh_helper<Registry> { | ||||
|     /*! @brief Registry type. */ | ||||
|     using registry_type = Registry; | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a helper for a given registry. | ||||
|      * @param ref A valid reference to a registry. | ||||
|      */ | ||||
|     sigh_helper(registry_type &ref) | ||||
|         : bucket{&ref} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Binds a properly initialized helper to a given signal type. | ||||
|      * @tparam Type Type of signal to bind the helper to. | ||||
|      * @param id Optional name for the underlying storage to use. | ||||
|      * @return A helper for a given registry and signal type. | ||||
|      */ | ||||
|     template<typename Type> | ||||
|     auto with(const id_type id = type_hash<Type>::value()) noexcept { | ||||
|         return sigh_helper<registry_type, Type>{*bucket, id}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reference to the underlying registry. | ||||
|      * @return A reference to the underlying registry. | ||||
|      */ | ||||
|     [[nodiscard]] registry_type ®istry() noexcept { | ||||
|         return *bucket; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     registry_type *bucket; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Signal connection helper for registries. | ||||
|  * @tparam Registry Basic registry type. | ||||
|  * @tparam Type Type of signal to connect listeners to. | ||||
|  */ | ||||
| template<typename Registry, typename Type> | ||||
| struct sigh_helper<Registry, Type> final: sigh_helper<Registry> { | ||||
|     /*! @brief Registry type. */ | ||||
|     using registry_type = Registry; | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a helper for a given registry. | ||||
|      * @param ref A valid reference to a registry. | ||||
|      * @param id Optional name for the underlying storage to use. | ||||
|      */ | ||||
|     sigh_helper(registry_type &ref, const id_type id = type_hash<Type>::value()) | ||||
|         : sigh_helper<Registry>{ref}, | ||||
|           name{id} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Forwards the call to `on_construct` on the underlying storage. | ||||
|      * @tparam Candidate Function or member to connect. | ||||
|      * @tparam Args Type of class or type of payload, if any. | ||||
|      * @param args A valid object that fits the purpose, if any. | ||||
|      * @return This helper. | ||||
|      */ | ||||
|     template<auto Candidate, typename... Args> | ||||
|     auto on_construct(Args &&...args) { | ||||
|         this->registry().template on_construct<Type>(name).template connect<Candidate>(std::forward<Args>(args)...); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Forwards the call to `on_update` on the underlying storage. | ||||
|      * @tparam Candidate Function or member to connect. | ||||
|      * @tparam Args Type of class or type of payload, if any. | ||||
|      * @param args A valid object that fits the purpose, if any. | ||||
|      * @return This helper. | ||||
|      */ | ||||
|     template<auto Candidate, typename... Args> | ||||
|     auto on_update(Args &&...args) { | ||||
|         this->registry().template on_update<Type>(name).template connect<Candidate>(std::forward<Args>(args)...); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Forwards the call to `on_destroy` on the underlying storage. | ||||
|      * @tparam Candidate Function or member to connect. | ||||
|      * @tparam Args Type of class or type of payload, if any. | ||||
|      * @param args A valid object that fits the purpose, if any. | ||||
|      * @return This helper. | ||||
|      */ | ||||
|     template<auto Candidate, typename... Args> | ||||
|     auto on_destroy(Args &&...args) { | ||||
|         this->registry().template on_destroy<Type>(name).template connect<Candidate>(std::forward<Args>(args)...); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     id_type name; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Deduction guide. | ||||
|  * @tparam Registry Basic registry type. | ||||
|  */ | ||||
| template<typename Registry> | ||||
| sigh_helper(Registry &) -> sigh_helper<Registry>; | ||||
|  | ||||
| } // namespace entt | ||||
|  | ||||
| #endif | ||||
|   | ||||
							
								
								
									
										293
									
								
								external/entt/entt/src/entt/entity/mixin.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								external/entt/entt/src/entt/entity/mixin.hpp
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| #ifndef ENTT_ENTITY_MIXIN_HPP | ||||
| #define ENTT_ENTITY_MIXIN_HPP | ||||
|  | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include "../config/config.h" | ||||
| #include "../core/any.hpp" | ||||
| #include "../signal/sigh.hpp" | ||||
| #include "entity.hpp" | ||||
| #include "fwd.hpp" | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| /** | ||||
|  * @brief Mixin type used to add signal support to storage types. | ||||
|  * | ||||
|  * The function type of a listener is equivalent to: | ||||
|  * | ||||
|  * @code{.cpp} | ||||
|  * void(basic_registry<entity_type> &, entity_type); | ||||
|  * @endcode | ||||
|  * | ||||
|  * This applies to all signals made available. | ||||
|  * | ||||
|  * @tparam Type The type of the underlying storage. | ||||
|  */ | ||||
| template<typename Type> | ||||
| class sigh_mixin final: public Type { | ||||
|     using underlying_type = 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; | ||||
|  | ||||
|     basic_registry_type &owner_or_assert() const noexcept { | ||||
|         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); | ||||
|         return *owner; | ||||
|     } | ||||
|  | ||||
|     void pop(underlying_iterator first, underlying_iterator last) final { | ||||
|         if(auto ® = owner_or_assert(); destruction.empty()) { | ||||
|             underlying_type::pop(first, last); | ||||
|         } else { | ||||
|             for(; first != last; ++first) { | ||||
|                 const auto entt = *first; | ||||
|                 destruction.publish(reg, entt); | ||||
|                 const auto it = underlying_type::find(entt); | ||||
|                 underlying_type::pop(it, it + 1u); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void pop_all() final { | ||||
|         if(auto ® = owner_or_assert(); !destruction.empty()) { | ||||
|             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))); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         underlying_type::pop_all(); | ||||
|     } | ||||
|  | ||||
|     underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { | ||||
|         const auto it = underlying_type::try_emplace(entt, force_back, value); | ||||
|  | ||||
|         if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { | ||||
|             construction.publish(reg, *it); | ||||
|         } | ||||
|  | ||||
|         return it; | ||||
|     } | ||||
|  | ||||
| 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 = basic_registry_type; | ||||
|  | ||||
|     /*! @brief Default constructor. */ | ||||
|     sigh_mixin() | ||||
|         : sigh_mixin{allocator_type{}} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs an empty storage with a given allocator. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     explicit sigh_mixin(const allocator_type &allocator) | ||||
|         : underlying_type{allocator}, | ||||
|           owner{}, | ||||
|           construction{allocator}, | ||||
|           destruction{allocator}, | ||||
|           update{allocator} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Move constructor. | ||||
|      * @param other The instance to move from. | ||||
|      */ | ||||
|     sigh_mixin(sigh_mixin &&other) noexcept | ||||
|         : underlying_type{std::move(other)}, | ||||
|           owner{other.owner}, | ||||
|           construction{std::move(other.construction)}, | ||||
|           destruction{std::move(other.destruction)}, | ||||
|           update{std::move(other.update)} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Allocator-extended move constructor. | ||||
|      * @param other The instance to move from. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     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 Move assignment operator. | ||||
|      * @param other The instance to move from. | ||||
|      * @return This storage. | ||||
|      */ | ||||
|     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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Exchanges the contents with those of a given storage. | ||||
|      * @param other Storage to exchange the content with. | ||||
|      */ | ||||
|     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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink object. | ||||
|      * | ||||
|      * The sink returned by this function can be used to receive notifications | ||||
|      * whenever a new instance is created and assigned to an entity.<br/> | ||||
|      * Listeners are invoked after the object has been assigned to the entity. | ||||
|      * | ||||
|      * @sa sink | ||||
|      * | ||||
|      * @return A temporary sink object. | ||||
|      */ | ||||
|     [[nodiscard]] auto on_construct() noexcept { | ||||
|         return sink{construction}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink object. | ||||
|      * | ||||
|      * The sink returned by this function can be used to receive notifications | ||||
|      * whenever an instance is explicitly updated.<br/> | ||||
|      * Listeners are invoked after the object has been updated. | ||||
|      * | ||||
|      * @sa sink | ||||
|      * | ||||
|      * @return A temporary sink object. | ||||
|      */ | ||||
|     [[nodiscard]] auto on_update() noexcept { | ||||
|         return sink{update}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink object. | ||||
|      * | ||||
|      * The sink returned by this function can be used to receive notifications | ||||
|      * whenever an instance is removed from an entity and thus destroyed.<br/> | ||||
|      * Listeners are invoked before the object has been removed from the entity. | ||||
|      * | ||||
|      * @sa sink | ||||
|      * | ||||
|      * @return A temporary sink object. | ||||
|      */ | ||||
|     [[nodiscard]] auto on_destroy() noexcept { | ||||
|         return sink{destruction}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Emplace elements into a storage. | ||||
|      * | ||||
|      * The behavior of this operation depends on the underlying storage type | ||||
|      * (for example, components vs entities).<br/> | ||||
|      * Refer to the specific documentation for more details. | ||||
|      * | ||||
|      * @return A return value as returned by the underlying storage. | ||||
|      */ | ||||
|     auto emplace() { | ||||
|         const auto entt = underlying_type::emplace(); | ||||
|         construction.publish(owner_or_assert(), entt); | ||||
|         return entt; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Emplace elements into a storage. | ||||
|      * | ||||
|      * The behavior of this operation depends on the underlying storage type | ||||
|      * (for example, components vs entities).<br/> | ||||
|      * Refer to the specific documentation for more details. | ||||
|      * | ||||
|      * @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 A return value as returned by the underlying storage. | ||||
|      */ | ||||
|     template<typename... Args> | ||||
|     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; | ||||
|         } else { | ||||
|             underlying_type::emplace(hint, std::forward<Args>(args)...); | ||||
|             construction.publish(owner_or_assert(), hint); | ||||
|             return this->get(hint); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Patches the given instance for an entity. | ||||
|      * @tparam Func Types of the function objects to invoke. | ||||
|      * @param entt A valid identifier. | ||||
|      * @param func Valid function objects. | ||||
|      * @return A reference to the patched instance. | ||||
|      */ | ||||
|     template<typename... Func> | ||||
|     decltype(auto) patch(const entity_type entt, Func &&...func) { | ||||
|         underlying_type::patch(entt, std::forward<Func>(func)...); | ||||
|         update.publish(owner_or_assert(), entt); | ||||
|         return this->get(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Emplace elements into a storage. | ||||
|      * | ||||
|      * The behavior of this operation depends on the underlying storage type | ||||
|      * (for example, components vs entities).<br/> | ||||
|      * Refer to the specific documentation for more details. | ||||
|      * | ||||
|      * @tparam It Iterator type (as required by the underlying storage type). | ||||
|      * @tparam Args Types of arguments to forward to the underlying storage. | ||||
|      * @param first An iterator to the first element of the range. | ||||
|      * @param last An iterator past the last element of the range. | ||||
|      * @param args Parameters to use to forward to the underlying storage. | ||||
|      */ | ||||
|     template<typename It, typename... Args> | ||||
|     void insert(It first, It last, Args &&...args) { | ||||
|         underlying_type::insert(first, last, std::forward<Args>(args)...); | ||||
|  | ||||
|         if(auto ® = owner_or_assert(); !construction.empty()) { | ||||
|             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; | ||||
|     sigh_type destruction; | ||||
|     sigh_type update; | ||||
| }; | ||||
|  | ||||
| } // namespace entt | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										68
									
								
								external/entt/entt/src/entt/entity/observer.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										68
									
								
								external/entt/entt/src/entt/entity/observer.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -43,7 +43,7 @@ struct basic_collector<> { | ||||
|      * @return The updated collector. | ||||
|      */ | ||||
|     template<typename... AllOf, typename... NoneOf> | ||||
|     static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept { | ||||
|     static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept { | ||||
|         return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>>{}; | ||||
|     } | ||||
|  | ||||
| @@ -78,7 +78,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule | ||||
|      * @return The updated collector. | ||||
|      */ | ||||
|     template<typename... AllOf, typename... NoneOf> | ||||
|     static constexpr auto group(exclude_t<NoneOf...> = {}) noexcept { | ||||
|     static constexpr auto group(exclude_t<NoneOf...> = exclude_t{}) noexcept { | ||||
|         return basic_collector<matcher<type_list<>, type_list<>, type_list<NoneOf...>, AllOf...>, current_type, Other...>{}; | ||||
|     } | ||||
|  | ||||
| @@ -99,7 +99,7 @@ struct basic_collector<matcher<type_list<Reject...>, type_list<Require...>, Rule | ||||
|      * @return The updated collector. | ||||
|      */ | ||||
|     template<typename... AllOf, typename... NoneOf> | ||||
|     static constexpr auto where(exclude_t<NoneOf...> = {}) noexcept { | ||||
|     static constexpr auto where(exclude_t<NoneOf...> = exclude_t{}) noexcept { | ||||
|         using extended_type = matcher<type_list<Reject..., NoneOf...>, type_list<Require..., AllOf...>, Rule...>; | ||||
|         return basic_collector<extended_type, Other...>{}; | ||||
|     } | ||||
| @@ -146,8 +146,7 @@ inline constexpr basic_collector<> collector{}; | ||||
|  * * The entity currently pointed is destroyed. | ||||
|  * | ||||
|  * In all the other cases, modifying the pools of the given components in any | ||||
|  * way invalidates all the iterators and using them results in undefined | ||||
|  * behavior. | ||||
|  * way invalidates all the iterators. | ||||
|  * | ||||
|  * @warning | ||||
|  * Lifetime of an observer doesn't necessarily have to overcome that of the | ||||
| @@ -156,10 +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> | ||||
| class basic_observer: private basic_storage<std::uint32_t, typename Registry::entity_type> { | ||||
|     using base_type = basic_storage<std::uint32_t, typename Registry::entity_type>; | ||||
| 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; | ||||
| @@ -193,10 +194,10 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en | ||||
|         } | ||||
|  | ||||
|         static void disconnect(basic_observer &obs, Registry ®) { | ||||
|             (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); | ||||
|             (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); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -239,12 +240,12 @@ class basic_observer: private basic_storage<std::uint32_t, typename Registry::en | ||||
|         } | ||||
|  | ||||
|         static void disconnect(basic_observer &obs, Registry ®) { | ||||
|             (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), ...); | ||||
|             (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), ...); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
| @@ -267,15 +268,26 @@ public: | ||||
|     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::base_type::iterator; | ||||
|     using iterator = typename registry_type::common_type::iterator; | ||||
|  | ||||
|     /*! @brief Default constructor. */ | ||||
|     basic_observer() | ||||
|         : release{} {} | ||||
|         : basic_observer{allocator_type{}} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs an empty storage with a given allocator. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     explicit basic_observer(const allocator_type &allocator) | ||||
|         : base_type{allocator}, | ||||
|           release{} {} | ||||
|  | ||||
|     /*! @brief Default copy constructor, deleted on purpose. */ | ||||
|     basic_observer(const basic_observer &) = delete; | ||||
|  | ||||
|     /*! @brief Default move constructor, deleted on purpose. */ | ||||
|     basic_observer(basic_observer &&) = delete; | ||||
|  | ||||
| @@ -283,16 +295,14 @@ public: | ||||
|      * @brief Creates an observer and connects it to a given registry. | ||||
|      * @tparam Matcher Types of matchers to use to initialize the observer. | ||||
|      * @param reg A valid reference to a registry. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     template<typename... Matcher> | ||||
|     basic_observer(registry_type ®, basic_collector<Matcher...>) | ||||
|         : basic_observer{} { | ||||
|     basic_observer(registry_type ®, basic_collector<Matcher...>, const allocator_type &allocator = allocator_type{}) | ||||
|         : 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. | ||||
| @@ -360,8 +370,7 @@ public: | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the observer. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the observer. If the | ||||
|      * container is empty, the returned iterator will be equal to `end()`. | ||||
|      * If the observer is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the observer. | ||||
|      */ | ||||
| @@ -371,11 +380,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the observer. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the observer. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the | ||||
|      * observer. | ||||
|      */ | ||||
|   | ||||
| @@ -126,7 +126,7 @@ class basic_organizer final { | ||||
|         if constexpr(std::is_same_v<Type, Registry>) { | ||||
|             return reg; | ||||
|         } else if constexpr(internal::is_view_v<Type>) { | ||||
|             return as_view{reg}; | ||||
|             return static_cast<Type>(as_view{reg}); | ||||
|         } else { | ||||
|             return reg.ctx().template emplace<std::remove_reference_t<Type>>(); | ||||
|         } | ||||
|   | ||||
							
								
								
									
										706
									
								
								external/entt/entt/src/entt/entity/registry.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										706
									
								
								external/entt/entt/src/entt/entity/registry.hpp
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -104,38 +104,22 @@ private: | ||||
| /** | ||||
|  * @brief Generic runtime view. | ||||
|  * | ||||
|  * Runtime views iterate over those entities that have at least all the given | ||||
|  * components in their bags. During initialization, a runtime view looks at the | ||||
|  * number of entities available for each component and picks up a reference to | ||||
|  * the smallest set of candidate entities in order to get a performance boost | ||||
|  * when iterate.<br/> | ||||
|  * Order of elements during iterations are highly dependent on the order of the | ||||
|  * underlying data structures. See sparse_set and its specializations for more | ||||
|  * details. | ||||
|  * 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 component and uses the smallest set in order to | ||||
|  * get a performance boost when iterating. | ||||
|  * | ||||
|  * @b Important | ||||
|  * | ||||
|  * Iterators aren't invalidated if: | ||||
|  * | ||||
|  * * New instances of the given components are created and assigned to entities. | ||||
|  * * The entity currently pointed is modified (as an example, if one of the | ||||
|  *   given components is removed from the entity to which the iterator points). | ||||
|  * * New elements are added to the storage. | ||||
|  * * The entity currently pointed is modified (for example, components are added | ||||
|  *   or removed from it). | ||||
|  * * The entity currently pointed is destroyed. | ||||
|  * | ||||
|  * In all the other cases, modifying the pools of the given components in any | ||||
|  * way invalidates all the iterators and using them results in undefined | ||||
|  * behavior. | ||||
|  * | ||||
|  * @note | ||||
|  * Views share references to the underlying data structures of the registry that | ||||
|  * generated them. Therefore any change to the entities and to the components | ||||
|  * made by means of the registry are immediately reflected by the views, unless | ||||
|  * a pool was missing when the view was built (in this case, the view won't | ||||
|  * have a valid reference and won't be updated accordingly). | ||||
|  * | ||||
|  * @warning | ||||
|  * Lifetime of a view must not overcome that of the registry that generated it. | ||||
|  * In any other case, attempting to use a view results in undefined behavior. | ||||
|  * In all other cases, modifying the storage iterated by the view in any way | ||||
|  * invalidates all the iterators. | ||||
|  * | ||||
|  * @tparam Type Common base type. | ||||
|  * @tparam Allocator Type of allocator used to manage memory and elements. | ||||
| @@ -154,9 +138,9 @@ public: | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using size_type = std::size_t; | ||||
|     /*! @brief Common type among all storage types. */ | ||||
|     using base_type = Type; | ||||
|     using common_type = Type; | ||||
|     /*! @brief Bidirectional iterator type. */ | ||||
|     using iterator = internal::runtime_view_iterator<base_type>; | ||||
|     using iterator = internal::runtime_view_iterator<common_type>; | ||||
|  | ||||
|     /*! @brief Default constructor to use to create empty, invalid views. */ | ||||
|     basic_runtime_view() noexcept | ||||
| @@ -235,7 +219,7 @@ public: | ||||
|      * @param base An opaque reference to a storage object. | ||||
|      * @return This runtime view. | ||||
|      */ | ||||
|     basic_runtime_view &iterate(base_type &base) { | ||||
|     basic_runtime_view &iterate(common_type &base) { | ||||
|         if(pools.empty() || !(base.size() < pools[0u]->size())) { | ||||
|             pools.push_back(&base); | ||||
|         } else { | ||||
| @@ -250,7 +234,7 @@ public: | ||||
|      * @param base An opaque reference to a storage object. | ||||
|      * @return This runtime view. | ||||
|      */ | ||||
|     basic_runtime_view &exclude(base_type &base) { | ||||
|     basic_runtime_view &exclude(common_type &base) { | ||||
|         filter.push_back(&base); | ||||
|         return *this; | ||||
|     } | ||||
| @@ -267,9 +251,7 @@ public: | ||||
|      * @brief Returns an iterator to the first entity that has the given | ||||
|      * components. | ||||
|      * | ||||
|      * The returned iterator points to the first entity that has the given | ||||
|      * components. If the view is empty, the returned iterator will be equal to | ||||
|      * `end()`. | ||||
|      * If the view is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity that has the given components. | ||||
|      */ | ||||
| @@ -280,11 +262,6 @@ public: | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity that has the | ||||
|      * given components. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity that | ||||
|      * has the given components. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity that has the | ||||
|      * given components. | ||||
|      */ | ||||
| @@ -307,8 +284,7 @@ public: | ||||
|      * @brief Iterates entities and applies the given function object to them. | ||||
|      * | ||||
|      * The function object is invoked for each entity. It is provided only with | ||||
|      * the entity itself. To get the components, users can use the registry with | ||||
|      * which the view was built.<br/> | ||||
|      * the entity itself.<br/> | ||||
|      * The signature of the function should be equivalent to the following: | ||||
|      * | ||||
|      * @code{.cpp} | ||||
|   | ||||
							
								
								
									
										529
									
								
								external/entt/entt/src/entt/entity/snapshot.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										529
									
								
								external/entt/entt/src/entt/entity/snapshot.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -1,7 +1,6 @@ | ||||
| #ifndef ENTT_ENTITY_SNAPSHOT_HPP | ||||
| #define ENTT_ENTITY_SNAPSHOT_HPP | ||||
|  | ||||
| #include <array> | ||||
| #include <cstddef> | ||||
| #include <iterator> | ||||
| #include <tuple> | ||||
| @@ -11,13 +10,37 @@ | ||||
| #include "../config/config.h" | ||||
| #include "../container/dense_map.hpp" | ||||
| #include "../core/type_traits.hpp" | ||||
| #include "component.hpp" | ||||
| #include "entity.hpp" | ||||
| #include "fwd.hpp" | ||||
| #include "view.hpp" | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| /** | ||||
|  * @cond TURN_OFF_DOXYGEN | ||||
|  * Internal details not to be documented. | ||||
|  */ | ||||
|  | ||||
| namespace internal { | ||||
|  | ||||
| template<typename Registry> | ||||
| void orphans(Registry ®istry) { | ||||
|     auto view = registry.template view<typename Registry::entity_type>(); | ||||
|  | ||||
|     for(auto entt: view) { | ||||
|         if(registry.orphan(entt)) { | ||||
|             view.storage()->erase(entt); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace internal | ||||
|  | ||||
| /** | ||||
|  * Internal details not to be documented. | ||||
|  * @endcond | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * @brief Utility class to create snapshots from a registry. | ||||
|  * | ||||
| @@ -30,34 +53,8 @@ namespace entt { | ||||
|  */ | ||||
| template<typename Registry> | ||||
| class basic_snapshot { | ||||
|     using entity_traits = entt_traits<typename Registry::entity_type>; | ||||
|  | ||||
|     template<typename Component, typename Archive, typename It> | ||||
|     void get(Archive &archive, std::size_t sz, It first, It last) const { | ||||
|         const auto view = reg->template view<const Component>(); | ||||
|         archive(typename entity_traits::entity_type(sz)); | ||||
|  | ||||
|         while(first != last) { | ||||
|             const auto entt = *(first++); | ||||
|  | ||||
|             if(reg->template all_of<Component>(entt)) { | ||||
|                 std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt))); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     template<typename... Component, typename Archive, typename It, std::size_t... Index> | ||||
|     void component(Archive &archive, It first, It last, std::index_sequence<Index...>) const { | ||||
|         std::array<std::size_t, sizeof...(Index)> size{}; | ||||
|         auto begin = first; | ||||
|  | ||||
|         while(begin != last) { | ||||
|             const auto entt = *(begin++); | ||||
|             ((reg->template all_of<Component>(entt) ? ++size[Index] : 0u), ...); | ||||
|         } | ||||
|  | ||||
|         (get<Component>(archive, size[Index], first, last), ...); | ||||
|     } | ||||
|     static_assert(!std::is_const_v<Registry>, "Non-const registry type required"); | ||||
|     using traits_type = typename Registry::traits_type; | ||||
|  | ||||
| public: | ||||
|     /*! Basic registry type. */ | ||||
| @@ -79,58 +76,96 @@ public: | ||||
|     basic_snapshot &operator=(basic_snapshot &&) noexcept = default; | ||||
|  | ||||
|     /** | ||||
|      * @brief Puts aside all the entities from the underlying registry. | ||||
|      * | ||||
|      * Entities are serialized along with their versions. Destroyed entities are | ||||
|      * taken in consideration as well by this function. | ||||
|      * | ||||
|      * @brief Serializes all elements of a type with associated identifiers. | ||||
|      * @tparam Type Type of elements to serialize. | ||||
|      * @tparam Archive Type of output archive. | ||||
|      * @param archive A valid reference to an output archive. | ||||
|      * @param id Optional name used to map the storage within the registry. | ||||
|      * @return An object of this type to continue creating the snapshot. | ||||
|      */ | ||||
|     template<typename Archive> | ||||
|     const basic_snapshot &entities(Archive &archive) const { | ||||
|         const auto sz = reg->size(); | ||||
|     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) { | ||||
|             archive(static_cast<typename traits_type::entity_type>(storage->size())); | ||||
|  | ||||
|         archive(typename entity_traits::entity_type(sz + 1u)); | ||||
|         archive(reg->released()); | ||||
|             if constexpr(std::is_same_v<Type, entity_type>) { | ||||
|                 archive(static_cast<typename traits_type::entity_type>(storage->in_use())); | ||||
|  | ||||
|         for(auto first = reg->data(), last = first + sz; first != last; ++first) { | ||||
|                 for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) { | ||||
|                     archive(*first); | ||||
|                 } | ||||
|             } else { | ||||
|                 for(auto elem: storage->reach()) { | ||||
|                     std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, elem); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             archive(typename traits_type::entity_type{}); | ||||
|         } | ||||
|  | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Puts aside the given components. | ||||
|      * | ||||
|      * Each instance is serialized together with the entity to which it belongs. | ||||
|      * Entities are serialized along with their versions. | ||||
|      * | ||||
|      * @brief Serializes all elements of a type with associated identifiers for | ||||
|      * the entities in a range. | ||||
|      * @tparam Type Type of elements 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. | ||||
|      * @param id Optional name used to map the storage within the registry. | ||||
|      * @return An object of this type to continue creating the snapshot. | ||||
|      */ | ||||
|     template<typename Type, typename Archive, typename It> | ||||
|     const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash<Type>::value()) const { | ||||
|         static_assert(!std::is_same_v<Type, entity_type>, "Entity types not supported"); | ||||
|  | ||||
|         if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) { | ||||
|             archive(static_cast<typename traits_type::entity_type>(std::distance(first, last))); | ||||
|  | ||||
|             for(; first != last; ++first) { | ||||
|                 if(const auto entt = *first; storage->contains(entt)) { | ||||
|                     archive(entt); | ||||
|                     std::apply([&archive](auto &&...args) { (archive(std::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt)); | ||||
|                 } else { | ||||
|                     archive(static_cast<entity_type>(null)); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             archive(typename traits_type::entity_type{}); | ||||
|         } | ||||
|  | ||||
|         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> | ||||
|     const basic_snapshot &component(Archive &archive) const { | ||||
|         if constexpr(sizeof...(Component) == 1u) { | ||||
|             const auto view = reg->template view<const Component...>(); | ||||
|             (component<Component>(archive, view.rbegin(), view.rend()), ...); | ||||
|             return *this; | ||||
|         } else { | ||||
|             (component<Component>(archive), ...); | ||||
|             return *this; | ||||
|         } | ||||
|     [[deprecated("use .get<Type>(archive) instead")]] const basic_snapshot &component(Archive &archive) const { | ||||
|         return (get<Component>(archive), ...); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Puts aside the given components for the entities in a range. | ||||
|      * | ||||
|      * Each instance is serialized together with the entity to which it belongs. | ||||
|      * Entities are serialized along with their versions. | ||||
|      * | ||||
|      * @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. | ||||
| @@ -140,9 +175,8 @@ public: | ||||
|      * @return An object of this type to continue creating the snapshot. | ||||
|      */ | ||||
|     template<typename... Component, typename Archive, typename It> | ||||
|     const basic_snapshot &component(Archive &archive, It first, It last) const { | ||||
|         component<Component...>(archive, first, last, std::index_sequence_for<Component...>{}); | ||||
|         return *this; | ||||
|     [[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: | ||||
| @@ -161,33 +195,8 @@ private: | ||||
|  */ | ||||
| template<typename Registry> | ||||
| class basic_snapshot_loader { | ||||
|     using entity_traits = entt_traits<typename Registry::entity_type>; | ||||
|  | ||||
|     template<typename Component, typename Archive> | ||||
|     void assign(Archive &archive) const { | ||||
|         typename entity_traits::entity_type length{}; | ||||
|         entity_type entt; | ||||
|  | ||||
|         archive(length); | ||||
|  | ||||
|         if constexpr(ignore_as_empty_v<Component>) { | ||||
|             while(length--) { | ||||
|                 archive(entt); | ||||
|                 const auto entity = reg->valid(entt) ? entt : reg->create(entt); | ||||
|                 ENTT_ASSERT(entity == entt, "Entity not available for use"); | ||||
|                 reg->template emplace<Component>(entt); | ||||
|             } | ||||
|         } else { | ||||
|             Component instance; | ||||
|  | ||||
|             while(length--) { | ||||
|                 archive(entt, instance); | ||||
|                 const auto entity = reg->valid(entt) ? entt : reg->create(entt); | ||||
|                 ENTT_ASSERT(entity == entt, "Entity not available for use"); | ||||
|                 reg->template emplace<Component>(entt, std::move(instance)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     static_assert(!std::is_const_v<Registry>, "Non-const registry type required"); | ||||
|     using traits_type = typename Registry::traits_type; | ||||
|  | ||||
| public: | ||||
|     /*! Basic registry type. */ | ||||
| @@ -202,7 +211,9 @@ public: | ||||
|     basic_snapshot_loader(registry_type &source) noexcept | ||||
|         : reg{&source} { | ||||
|         // restoring a snapshot as a whole requires a clean registry | ||||
|         ENTT_ASSERT(reg->empty(), "Registry must be empty"); | ||||
|         for([[maybe_unused]] auto elem: source.storage()) { | ||||
|             ENTT_ASSERT(elem.second.empty(), "Registry must be empty"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @brief Default move constructor. */ | ||||
| @@ -212,48 +223,80 @@ public: | ||||
|     basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; | ||||
|  | ||||
|     /** | ||||
|      * @brief Restores entities that were in use during serialization. | ||||
|      * | ||||
|      * This function restores the entities that were in use during serialization | ||||
|      * and gives them the versions they originally had. | ||||
|      * | ||||
|      * @brief Restores all elements of a type with associated identifiers. | ||||
|      * @tparam Type Type of elements to restore. | ||||
|      * @tparam Archive Type of input archive. | ||||
|      * @param archive A valid reference to an input archive. | ||||
|      * @param id Optional name used to map the storage within the registry. | ||||
|      * @return A valid loader to continue restoring data. | ||||
|      */ | ||||
|     template<typename Archive> | ||||
|     const basic_snapshot_loader &entities(Archive &archive) const { | ||||
|         typename entity_traits::entity_type length{}; | ||||
|     template<typename Type, typename Archive> | ||||
|     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); | ||||
|         std::vector<entity_type> all(length); | ||||
|  | ||||
|         for(std::size_t pos{}; pos < length; ++pos) { | ||||
|             archive(all[pos]); | ||||
|         if constexpr(std::is_same_v<Type, entity_type>) { | ||||
|             typename traits_type::entity_type in_use{}; | ||||
|  | ||||
|             storage.reserve(length); | ||||
|             archive(in_use); | ||||
|  | ||||
|             for(entity_type entity = null; length; --length) { | ||||
|                 archive(entity); | ||||
|                 storage.emplace(entity); | ||||
|             } | ||||
|  | ||||
|         reg->assign(++all.cbegin(), all.cend(), all[0u]); | ||||
|             storage.in_use(in_use); | ||||
|         } else { | ||||
|             auto &other = reg->template storage<entity_type>(); | ||||
|             entity_type entt{null}; | ||||
|  | ||||
|             while(length--) { | ||||
|                 if(archive(entt); entt != null) { | ||||
|                     const auto entity = other.contains(entt) ? entt : other.emplace(entt); | ||||
|                     ENTT_ASSERT(entity == entt, "Entity not available for use"); | ||||
|  | ||||
|                     if constexpr(Registry::template storage_for_type<Type>::traits_type::page_size == 0u) { | ||||
|                         storage.emplace(entity); | ||||
|                     } else { | ||||
|                         Type elem{}; | ||||
|                         archive(elem); | ||||
|                         storage.emplace(entity, std::move(elem)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Restores components and assigns them to the right entities. | ||||
|      * @brief Restores all identifiers, including those to be recycled. | ||||
|      * @tparam Archive Type of input archive. | ||||
|      * @param archive A valid reference to an input archive. | ||||
|      * @return A valid loader to continue restoring data. | ||||
|      */ | ||||
|     template<typename Archive> | ||||
|     [[deprecated("use .get<Entity>(archive) instead")]] basic_snapshot_loader &entities(Archive &archive) { | ||||
|         return get<entity_type>(archive); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Restores all elements of a type with associated identifiers. | ||||
|      * | ||||
|      * The template parameter list must be exactly the same used during | ||||
|      * serialization. In the event that the entity to which the component is | ||||
|      * assigned doesn't exist yet, the loader will take care to create it with | ||||
|      * the version it originally had. | ||||
|      * serialization. | ||||
|      * | ||||
|      * @tparam Component Types of components to restore. | ||||
|      * @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> | ||||
|     const basic_snapshot_loader &component(Archive &archive) const { | ||||
|         (assign<Component>(archive), ...); | ||||
|         return *this; | ||||
|     [[deprecated("use .get<Type>(archive) instead")]] basic_snapshot_loader &component(Archive &archive) { | ||||
|         return (get<Component>(archive), ...); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -262,17 +305,12 @@ public: | ||||
|      * 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 functions helps to identify and destroy those entities. | ||||
|      * This function helps to identify and destroy those entities. | ||||
|      * | ||||
|      * @return A valid loader to continue restoring data. | ||||
|      */ | ||||
|     const basic_snapshot_loader &orphans() const { | ||||
|         reg->each([this](const auto entt) { | ||||
|             if(reg->orphan(entt)) { | ||||
|                 reg->release(entt); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|     basic_snapshot_loader &orphans() { | ||||
|         internal::orphans(*reg); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
| @@ -290,7 +328,7 @@ private: | ||||
|  * Identifiers that entities originally had are not transferred to the target. | ||||
|  * Instead, the loader maps remote identifiers to local ones while restoring a | ||||
|  * snapshot.<br/> | ||||
|  * An example of use is the implementation of a client-server applications with | ||||
|  * An example of use is the implementation of a client-server application with | ||||
|  * the requirement of transferring somehow parts of the representation side to | ||||
|  * side. | ||||
|  * | ||||
| @@ -298,29 +336,16 @@ private: | ||||
|  */ | ||||
| template<typename Registry> | ||||
| class basic_continuous_loader { | ||||
|     using entity_traits = entt_traits<typename Registry::entity_type>; | ||||
|  | ||||
|     void destroy(typename Registry::entity_type entt) { | ||||
|         if(const auto it = remloc.find(entt); it == remloc.cend()) { | ||||
|             const auto local = reg->create(); | ||||
|             remloc.emplace(entt, std::make_pair(local, true)); | ||||
|             reg->destroy(local); | ||||
|         } | ||||
|     } | ||||
|     static_assert(!std::is_const_v<Registry>, "Non-const registry type required"); | ||||
|     using traits_type = typename Registry::traits_type; | ||||
|  | ||||
|     void restore(typename Registry::entity_type entt) { | ||||
|         const auto it = remloc.find(entt); | ||||
|  | ||||
|         if(it == remloc.cend()) { | ||||
|             const auto local = reg->create(); | ||||
|             remloc.emplace(entt, std::make_pair(local, true)); | ||||
|         } else { | ||||
|             if(!reg->valid(remloc[entt].first)) { | ||||
|                 remloc[entt].first = reg->create(); | ||||
|         if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) { | ||||
|             if(!reg->valid(remloc[entity].second)) { | ||||
|                 remloc[entity].second = reg->create(); | ||||
|             } | ||||
|  | ||||
|             // set the dirty flag | ||||
|             remloc[entt].second = true; | ||||
|         } else { | ||||
|             remloc.insert_or_assign(entity, std::make_pair(entt, reg->create())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -369,42 +394,6 @@ class basic_continuous_loader { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     template<typename Component> | ||||
|     void remove_if_exists() { | ||||
|         for(auto &&ref: remloc) { | ||||
|             const auto local = ref.second.first; | ||||
|  | ||||
|             if(reg->valid(local)) { | ||||
|                 reg->template remove<Component>(local); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     template<typename Component, typename Archive, typename... Other, typename... Member> | ||||
|     void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) { | ||||
|         typename entity_traits::entity_type length{}; | ||||
|         entity_type entt; | ||||
|  | ||||
|         archive(length); | ||||
|  | ||||
|         if constexpr(ignore_as_empty_v<Component>) { | ||||
|             while(length--) { | ||||
|                 archive(entt); | ||||
|                 restore(entt); | ||||
|                 reg->template emplace_or_replace<Component>(map(entt)); | ||||
|             } | ||||
|         } else { | ||||
|             Component instance; | ||||
|  | ||||
|             while(length--) { | ||||
|                 archive(entt, instance); | ||||
|                 (update(instance, member), ...); | ||||
|                 restore(entt); | ||||
|                 reg->template emplace_or_replace<Component>(map(entt), std::move(instance)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     /*! Basic registry type. */ | ||||
|     using registry_type = Registry; | ||||
| @@ -416,7 +405,8 @@ public: | ||||
|      * @param source A valid reference to a registry. | ||||
|      */ | ||||
|     basic_continuous_loader(registry_type &source) noexcept | ||||
|         : reg{&source} {} | ||||
|         : remloc{source.get_allocator()}, | ||||
|           reg{&source} {} | ||||
|  | ||||
|     /*! @brief Default move constructor. */ | ||||
|     basic_continuous_loader(basic_continuous_loader &&) = default; | ||||
| @@ -425,90 +415,142 @@ public: | ||||
|     basic_continuous_loader &operator=(basic_continuous_loader &&) = default; | ||||
|  | ||||
|     /** | ||||
|      * @brief Restores entities that were in use during serialization. | ||||
|      * @brief Restores all elements of a type with associated identifiers. | ||||
|      * | ||||
|      * This function restores the entities that were in use during serialization | ||||
|      * and creates local counterparts for them if required. | ||||
|      * 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 Type Type of elements to restore. | ||||
|      * @tparam Archive Type of input archive. | ||||
|      * @param archive A valid reference to an input archive. | ||||
|      * @param id Optional name used to map the storage within the registry. | ||||
|      * @return A valid loader to continue restoring data. | ||||
|      */ | ||||
|     template<typename Type, typename Archive> | ||||
|     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}; | ||||
|  | ||||
|         archive(length); | ||||
|  | ||||
|         if constexpr(std::is_same_v<Type, entity_type>) { | ||||
|             typename traits_type::entity_type in_use{}; | ||||
|  | ||||
|             storage.reserve(length); | ||||
|             archive(in_use); | ||||
|  | ||||
|             for(std::size_t pos{}; pos < in_use; ++pos) { | ||||
|                 archive(entt); | ||||
|                 restore(entt); | ||||
|             } | ||||
|  | ||||
|             for(std::size_t pos = in_use; pos < length; ++pos) { | ||||
|                 archive(entt); | ||||
|  | ||||
|                 if(const auto entity = to_entity(entt); remloc.contains(entity)) { | ||||
|                     if(reg->valid(remloc[entity].second)) { | ||||
|                         reg->destroy(remloc[entity].second); | ||||
|                     } | ||||
|  | ||||
|                     remloc.erase(entity); | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             for(auto &&ref: remloc) { | ||||
|                 storage.remove(ref.second.second); | ||||
|             } | ||||
|  | ||||
|             while(length--) { | ||||
|                 if(archive(entt); entt != null) { | ||||
|                     restore(entt); | ||||
|  | ||||
|                     if constexpr(Registry::template storage_for_type<Type>::traits_type::page_size == 0u) { | ||||
|                         storage.emplace(map(entt)); | ||||
|                     } else { | ||||
|                         Type elem{}; | ||||
|                         archive(elem); | ||||
|                         storage.emplace(map(entt), std::move(elem)); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Restores all identifiers, including those to be recycled. | ||||
|      * | ||||
|      * It creates local counterparts for remote elements as needed. | ||||
|      * | ||||
|      * @tparam Archive Type of input archive. | ||||
|      * @param archive A valid reference to an input archive. | ||||
|      * @return A non-const reference to this loader. | ||||
|      */ | ||||
|     template<typename Archive> | ||||
|     basic_continuous_loader &entities(Archive &archive) { | ||||
|         typename entity_traits::entity_type length{}; | ||||
|         entity_type entt{}; | ||||
|  | ||||
|         archive(length); | ||||
|         // discards the head of the list of destroyed entities | ||||
|         archive(entt); | ||||
|  | ||||
|         for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) { | ||||
|             archive(entt); | ||||
|  | ||||
|             if(const auto entity = entity_traits::to_entity(entt); entity == pos) { | ||||
|                 restore(entt); | ||||
|             } else { | ||||
|                 destroy(entt); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return *this; | ||||
|     [[deprecated("use .get<Entity>(archive) instead")]] basic_continuous_loader &entities(Archive &archive) { | ||||
|         return get<entity_type>(archive); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Restores components and assigns them to the right entities. | ||||
|      * @brief Serializes all elements of a type with associated identifiers. | ||||
|      * | ||||
|      * The template parameter list must be exactly the same used during | ||||
|      * serialization. In the event that the entity to which the component is | ||||
|      * assigned doesn't exist yet, the loader will take care to create a local | ||||
|      * counterpart for it.<br/> | ||||
|      * Members can be either data members of type entity_type or containers of | ||||
|      * entities. In both cases, the loader will visit them and update the | ||||
|      * entities by replacing each one with its local counterpart. | ||||
|      * 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 Other Types of components to update with local counterparts. | ||||
|      * @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... Other, typename... Member> | ||||
|     basic_continuous_loader &component(Archive &archive, Member Other::*...member) { | ||||
|         (remove_if_exists<Component>(), ...); | ||||
|         (assign<Component>(archive, member...), ...); | ||||
|     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 conterpart. | ||||
|      * @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. | ||||
|      */ | ||||
|     basic_continuous_loader &shrink() { | ||||
|         auto it = remloc.begin(); | ||||
|  | ||||
|         while(it != remloc.cend()) { | ||||
|             const auto local = it->second.first; | ||||
|             bool &dirty = it->second.second; | ||||
|  | ||||
|             if(dirty) { | ||||
|                 dirty = false; | ||||
|                 ++it; | ||||
|             } else { | ||||
|                 if(reg->valid(local)) { | ||||
|                     reg->destroy(local); | ||||
|                 } | ||||
|  | ||||
|                 it = remloc.erase(it); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     [[deprecated("use .get<Entity>(archive) instead")]] basic_continuous_loader &shrink() { | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
| @@ -518,17 +560,12 @@ public: | ||||
|      * 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 functions helps to identify and destroy those entities. | ||||
|      * This function helps to identify and destroy those entities. | ||||
|      * | ||||
|      * @return A non-const reference to this loader. | ||||
|      */ | ||||
|     basic_continuous_loader &orphans() { | ||||
|         reg->each([this](const auto entt) { | ||||
|             if(reg->orphan(entt)) { | ||||
|                 reg->release(entt); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         internal::orphans(*reg); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
| @@ -538,7 +575,8 @@ public: | ||||
|      * @return True if `entity` is managed by the loader, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool contains(entity_type entt) const noexcept { | ||||
|         return (remloc.find(entt) != remloc.cend()); | ||||
|         const auto it = remloc.find(to_entity(entt)); | ||||
|         return it != remloc.cend() && it->second.first == entt; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -547,18 +585,15 @@ public: | ||||
|      * @return The local identifier if any, the null entity otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] entity_type map(entity_type entt) const noexcept { | ||||
|         const auto it = remloc.find(entt); | ||||
|         entity_type other = null; | ||||
|  | ||||
|         if(it != remloc.cend()) { | ||||
|             other = it->second.first; | ||||
|         if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) { | ||||
|             return it->second.second; | ||||
|         } | ||||
|  | ||||
|         return other; | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     dense_map<entity_type, std::pair<entity_type, bool>> remloc; | ||||
|     dense_map<typename traits_type::entity_type, std::pair<entity_type, entity_type>> remloc; | ||||
|     registry_type *reg; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										333
									
								
								external/entt/entt/src/entt/entity/sparse_set.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										333
									
								
								external/entt/entt/src/entt/entity/sparse_set.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -88,6 +88,10 @@ struct sparse_set_iterator final { | ||||
|         return *operator->(); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] constexpr pointer data() const noexcept { | ||||
|         return packed ? packed->data() : nullptr; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] constexpr difference_type index() const noexcept { | ||||
|         return offset - 1; | ||||
|     } | ||||
| @@ -97,38 +101,38 @@ private: | ||||
|     difference_type offset; | ||||
| }; | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return rhs.index() - lhs.index(); | ||||
| } | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr bool operator==(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return lhs.index() == rhs.index(); | ||||
| } | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr bool operator!=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr bool operator<(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return lhs.index() > rhs.index(); | ||||
| } | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
|     return lhs.index() < rhs.index(); | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr bool operator>(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return rhs < lhs; | ||||
| } | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr bool operator<=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return !(lhs > rhs); | ||||
| } | ||||
|  | ||||
| template<typename Type, typename Other> | ||||
| [[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Type> &lhs, const sparse_set_iterator<Other> &rhs) noexcept { | ||||
| template<typename Container> | ||||
| [[nodiscard]] constexpr bool operator>=(const sparse_set_iterator<Container> &lhs, const sparse_set_iterator<Container> &rhs) noexcept { | ||||
|     return !(lhs < rhs); | ||||
| } | ||||
|  | ||||
| @@ -139,14 +143,6 @@ template<typename Type, typename Other> | ||||
|  * @endcond | ||||
|  */ | ||||
|  | ||||
| /*! @brief Sparse set deletion policy. */ | ||||
| enum class deletion_policy : std::uint8_t { | ||||
|     /*! @brief Swap-and-pop deletion policy. */ | ||||
|     swap_and_pop = 0u, | ||||
|     /*! @brief In-place deletion policy. */ | ||||
|     in_place = 1u | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Basic sparse set implementation. | ||||
|  * | ||||
| @@ -154,10 +150,6 @@ enum class deletion_policy : std::uint8_t { | ||||
|  * Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a | ||||
|  * _packed_ one; one used for direct access through contiguous memory, the other | ||||
|  * one used to get the data through an extra level of indirection.<br/> | ||||
|  * This is largely used by the registry to offer users the fastest access ever | ||||
|  * to the components. Views and groups in general are almost entirely designed | ||||
|  * around sparse sets. | ||||
|  * | ||||
|  * This type of data structure is widely documented in the literature and on the | ||||
|  * web. This is nothing more than a customized implementation suitable for the | ||||
|  * purpose of the framework. | ||||
| @@ -167,7 +159,7 @@ enum class deletion_policy : std::uint8_t { | ||||
|  * no guarantees that entities are returned in the insertion order when iterate | ||||
|  * a sparse set. Do not make assumption on the order in any case. | ||||
|  * | ||||
|  * @tparam Entity A valid entity type (see entt_traits for more details). | ||||
|  * @tparam Entity A valid entity type. | ||||
|  * @tparam Allocator Type of allocator used to manage memory and elements. | ||||
|  */ | ||||
| template<typename Entity, typename Allocator> | ||||
| @@ -176,23 +168,26 @@ 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 entity_traits = entt_traits<Entity>; | ||||
|  | ||||
|     [[nodiscard]] auto sparse_ptr(const Entity entt) const { | ||||
|         const auto pos = static_cast<size_type>(entity_traits::to_entity(entt)); | ||||
|         const auto page = pos / entity_traits::page_size; | ||||
|         return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr; | ||||
|         const auto pos = static_cast<size_type>(traits_type::to_entity(entt)); | ||||
|         const auto page = pos / traits_type::page_size; | ||||
|         return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] auto &sparse_ref(const Entity entt) const { | ||||
|         ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); | ||||
|         const auto pos = static_cast<size_type>(entity_traits::to_entity(entt)); | ||||
|         return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)]; | ||||
|         const auto pos = static_cast<size_type>(traits_type::to_entity(entt)); | ||||
|         return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] auto to_iterator(const Entity entt) const { | ||||
|         return --(end() - index(entt)); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] auto &assure_at_least(const Entity entt) { | ||||
|         const auto pos = static_cast<size_type>(entity_traits::to_entity(entt)); | ||||
|         const auto page = pos / entity_traits::page_size; | ||||
|         const auto pos = static_cast<size_type>(traits_type::to_entity(entt)); | ||||
|         const auto page = pos / traits_type::page_size; | ||||
|  | ||||
|         if(!(page < sparse.size())) { | ||||
|             sparse.resize(page + 1u, nullptr); | ||||
| @@ -200,11 +195,11 @@ class basic_sparse_set { | ||||
|  | ||||
|         if(!sparse[page]) { | ||||
|             auto page_allocator{packed.get_allocator()}; | ||||
|             sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size); | ||||
|             std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null); | ||||
|             sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); | ||||
|             std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, null); | ||||
|         } | ||||
|  | ||||
|         auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; | ||||
|         auto &elem = sparse[page][fast_mod(pos, traits_type::page_size)]; | ||||
|         ENTT_ASSERT(elem == null, "Slot not available"); | ||||
|         return elem; | ||||
|     } | ||||
| @@ -214,8 +209,8 @@ class basic_sparse_set { | ||||
|  | ||||
|         for(auto &&page: sparse) { | ||||
|             if(page != nullptr) { | ||||
|                 std::destroy(page, page + entity_traits::page_size); | ||||
|                 alloc_traits::deallocate(page_allocator, page, entity_traits::page_size); | ||||
|                 std::destroy(page, page + traits_type::page_size); | ||||
|                 alloc_traits::deallocate(page_allocator, page, traits_type::page_size); | ||||
|                 page = nullptr; | ||||
|             } | ||||
|         } | ||||
| @@ -226,13 +221,28 @@ private: | ||||
|         return nullptr; | ||||
|     } | ||||
|  | ||||
|     virtual void swap_at(const std::size_t, const std::size_t) {} | ||||
|     virtual void move_element(const std::size_t, const std::size_t) {} | ||||
|     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 Swaps two items at specific locations. | ||||
|      * @param lhs A position to move from. | ||||
|      * @param rhs The other position to move from. | ||||
|      */ | ||||
|     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]); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Erases an entity from a sparse set. | ||||
|      * @param it An iterator to the element to pop. | ||||
| @@ -240,8 +250,8 @@ protected: | ||||
|     void swap_and_pop(const basic_iterator it) { | ||||
|         ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched"); | ||||
|         auto &self = sparse_ref(*it); | ||||
|         const auto entt = entity_traits::to_entity(self); | ||||
|         sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back())); | ||||
|         const auto entt = traits_type::to_entity(self); | ||||
|         sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); | ||||
|         packed[static_cast<size_type>(entt)] = packed.back(); | ||||
|         // unnecessary but it helps to detect nasty bugs | ||||
|         ENTT_ASSERT((packed.back() = null, true), ""); | ||||
| @@ -256,8 +266,8 @@ protected: | ||||
|      */ | ||||
|     void in_place_pop(const basic_iterator it) { | ||||
|         ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched"); | ||||
|         const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null)); | ||||
|         packed[static_cast<size_type>(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved)); | ||||
|         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: | ||||
| @@ -278,6 +288,23 @@ protected: | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @brief Erases all entities of a sparse set. */ | ||||
|     virtual void pop_all() { | ||||
|         if(const auto prev = std::exchange(free_list, tombstone); prev == null) { | ||||
|             for(auto first = begin(); !(first.index() < 0); ++first) { | ||||
|                 sparse_ref(*first) = null; | ||||
|             } | ||||
|         } else { | ||||
|             for(auto first = begin(); !(first.index() < 0); ++first) { | ||||
|                 if(*first != tombstone) { | ||||
|                     sparse_ref(*first) = null; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         packed.clear(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns an entity to a sparse set. | ||||
|      * @param entt A valid identifier. | ||||
| @@ -289,25 +316,27 @@ protected: | ||||
|  | ||||
|         if(auto &elem = assure_at_least(entt); free_list == null || force_back) { | ||||
|             packed.push_back(entt); | ||||
|             elem = entity_traits::combine(static_cast<typename entity_traits::entity_type>(packed.size() - 1u), entity_traits::to_integral(entt)); | ||||
|             elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt)); | ||||
|             return begin(); | ||||
|         } else { | ||||
|             const auto pos = static_cast<size_type>(entity_traits::to_entity(free_list)); | ||||
|             elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt)); | ||||
|             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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| 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 entity_traits::value_type; | ||||
|     using entity_type = typename traits_type::value_type; | ||||
|     /*! @brief Underlying version type. */ | ||||
|     using version_type = typename entity_traits::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. */ | ||||
| @@ -317,7 +346,7 @@ public: | ||||
|     /*! @brief Reverse iterator type. */ | ||||
|     using reverse_iterator = std::reverse_iterator<iterator>; | ||||
|     /*! @brief Constant reverse iterator type. */ | ||||
|     using const_reverse_iterator = reverse_iterator; | ||||
|     using const_reverse_iterator = std::reverse_iterator<const_iterator>; | ||||
|  | ||||
|     /*! @brief Default constructor. */ | ||||
|     basic_sparse_set() | ||||
| @@ -341,14 +370,14 @@ public: | ||||
|     /** | ||||
|      * @brief Constructs an empty container with the given value type, policy | ||||
|      * and allocator. | ||||
|      * @param value Returned value type, if any. | ||||
|      * @param elem Returned value type, if any. | ||||
|      * @param pol Type of deletion policy. | ||||
|      * @param allocator The allocator to use (possibly default-constructed). | ||||
|      */ | ||||
|     explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) | ||||
|     explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) | ||||
|         : sparse{allocator}, | ||||
|           packed{allocator}, | ||||
|           info{&value}, | ||||
|           info{&elem}, | ||||
|           free_list{tombstone}, | ||||
|           mode{pol} {} | ||||
|  | ||||
| @@ -465,7 +494,7 @@ public: | ||||
|      * @return Extent of the sparse set. | ||||
|      */ | ||||
|     [[nodiscard]] size_type extent() const noexcept { | ||||
|         return sparse.size() * entity_traits::page_size; | ||||
|         return sparse.size() * traits_type::page_size; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -490,6 +519,14 @@ public: | ||||
|         return packed.empty(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Checks whether a sparse set is fully packed. | ||||
|      * @return True if the sparse set is fully packed, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool contiguous() const noexcept { | ||||
|         return (free_list == null); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Direct access to the internal packed array. | ||||
|      * @return A pointer to the internal packed array. | ||||
| @@ -501,8 +538,7 @@ public: | ||||
|     /** | ||||
|      * @brief Returns an iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the internal packed | ||||
|      * array. If the sparse set is empty, the returned iterator will be equal to | ||||
|      * If the sparse set is empty, the returned iterator will be equal to | ||||
|      * `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the sparse set. | ||||
| @@ -519,11 +555,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last entity in | ||||
|      * a sparse set. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last entity of a sparse | ||||
|      * set. | ||||
|      */ | ||||
| @@ -539,9 +570,8 @@ public: | ||||
|     /** | ||||
|      * @brief Returns a reverse iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the reversed internal | ||||
|      * packed array. If the sparse set is empty, the returned iterator will be | ||||
|      * equal to `rend()`. | ||||
|      * If the sparse set is empty, the returned iterator will be equal to | ||||
|      * `rend()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the reversed internal packed | ||||
|      * array. | ||||
| @@ -557,11 +587,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reverse iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last entity in | ||||
|      * the reversed sparse set. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last entity of the | ||||
|      * reversed sparse set. | ||||
|      */ | ||||
| @@ -581,7 +606,7 @@ public: | ||||
|      * iterator otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] iterator find(const entity_type entt) const noexcept { | ||||
|         return contains(entt) ? --(end() - index(entt)) : end(); | ||||
|         return contains(entt) ? to_iterator(entt) : end(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -591,9 +616,9 @@ public: | ||||
|      */ | ||||
|     [[nodiscard]] bool contains(const entity_type entt) const noexcept { | ||||
|         const auto elem = sparse_ptr(entt); | ||||
|         constexpr auto cap = entity_traits::to_entity(null); | ||||
|         constexpr auto cap = traits_type::to_entity(null); | ||||
|         // testing versions permits to avoid accessing the packed array | ||||
|         return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap); | ||||
|         return elem && (((~cap & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -604,8 +629,8 @@ public: | ||||
|      */ | ||||
|     [[nodiscard]] version_type current(const entity_type entt) const noexcept { | ||||
|         const auto elem = sparse_ptr(entt); | ||||
|         constexpr auto fallback = entity_traits::to_version(tombstone); | ||||
|         return elem ? entity_traits::to_version(*elem) : fallback; | ||||
|         constexpr auto fallback = traits_type::to_version(tombstone); | ||||
|         return elem ? traits_type::to_version(*elem) : fallback; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -620,7 +645,7 @@ public: | ||||
|      */ | ||||
|     [[nodiscard]] size_type index(const entity_type entt) const noexcept { | ||||
|         ENTT_ASSERT(contains(entt), "Set does not contain entity"); | ||||
|         return static_cast<size_type>(entity_traits::to_entity(sparse_ref(entt))); | ||||
|         return static_cast<size_type>(traits_type::to_entity(sparse_ref(entt))); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -652,13 +677,13 @@ public: | ||||
|      * @param entt A valid identifier. | ||||
|      * @return An opaque pointer to the element assigned to the entity, if any. | ||||
|      */ | ||||
|     [[nodiscard]] const void *get(const entity_type entt) const noexcept { | ||||
|     [[nodiscard]] const void *value(const entity_type entt) const noexcept { | ||||
|         return get_at(index(entt)); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc get */ | ||||
|     [[nodiscard]] void *get(const entity_type entt) noexcept { | ||||
|         return const_cast<void *>(std::as_const(*this).get(entt)); | ||||
|     /*! @copydoc value */ | ||||
|     [[nodiscard]] void *value(const entity_type entt) noexcept { | ||||
|         return const_cast<void *>(std::as_const(*this).value(entt)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -669,28 +694,12 @@ public: | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @param entt A valid identifier. | ||||
|      * @param value Optional opaque value to forward to mixins, if any. | ||||
|      * @param elem Optional opaque element to forward to mixins, if any. | ||||
|      * @return Iterator pointing to the emplaced element in case of success, the | ||||
|      * `end()` iterator otherwise. | ||||
|      */ | ||||
|     iterator emplace(const entity_type entt, const void *value = nullptr) { | ||||
|         return try_emplace(entt, false, value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Bump the version number of an entity. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to bump the version of an entity that doesn't belong to the | ||||
|      * sparse set results in undefined behavior. | ||||
|      * | ||||
|      * @param entt A valid identifier. | ||||
|      */ | ||||
|     void bump(const entity_type entt) { | ||||
|         auto &entity = sparse_ref(entt); | ||||
|         ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); | ||||
|         entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt)); | ||||
|         packed[static_cast<size_type>(entity_traits::to_entity(entity))] = entt; | ||||
|     iterator push(const entity_type entt, const void *elem = nullptr) { | ||||
|         return try_emplace(entt, false, elem); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -707,7 +716,7 @@ public: | ||||
|      * success, the `end()` iterator otherwise. | ||||
|      */ | ||||
|     template<typename It> | ||||
|     iterator insert(It first, It last) { | ||||
|     iterator push(It first, It last) { | ||||
|         for(auto it = first; it != last; ++it) { | ||||
|             try_emplace(*it, true); | ||||
|         } | ||||
| @@ -715,6 +724,24 @@ public: | ||||
|         return first == last ? end() : find(*first); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Bump the version number of an entity. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to bump the version of an entity that doesn't belong to the | ||||
|      * sparse set results in undefined behavior. | ||||
|      * | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The version of the given identifier. | ||||
|      */ | ||||
|     version_type bump(const entity_type 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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Erases an entity from a sparse set. | ||||
|      * | ||||
| @@ -725,7 +752,7 @@ public: | ||||
|      * @param entt A valid identifier. | ||||
|      */ | ||||
|     void erase(const entity_type entt) { | ||||
|         const auto it = --(end() - index(entt)); | ||||
|         const auto it = to_iterator(entt); | ||||
|         pop(it, it + 1u); | ||||
|     } | ||||
|  | ||||
| @@ -769,29 +796,45 @@ public: | ||||
|     size_type remove(It first, It last) { | ||||
|         size_type count{}; | ||||
|  | ||||
|         if constexpr(std::is_same_v<It, basic_iterator>) { | ||||
|             while(first != last) { | ||||
|                 while(first != last && !contains(*first)) { | ||||
|                     ++first; | ||||
|                 } | ||||
|  | ||||
|                 const auto it = first; | ||||
|  | ||||
|                 while(first != last && contains(*first)) { | ||||
|                     ++first; | ||||
|                 } | ||||
|  | ||||
|                 count += std::distance(it, first); | ||||
|                 erase(it, first); | ||||
|             } | ||||
|         } else { | ||||
|             for(; first != last; ++first) { | ||||
|                 count += remove(*first); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|     /*! @brief Removes all tombstones from the packed array of a sparse set. */ | ||||
|     /*! @brief Removes all tombstones from a sparse set. */ | ||||
|     void compact() { | ||||
|         size_type from = packed.size(); | ||||
|         for(; from && packed[from - 1u] == tombstone; --from) {} | ||||
|  | ||||
|         for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) { | ||||
|             if(const size_type to = entity_traits::to_entity(*it); to < 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; | ||||
|                 move_element(from, to); | ||||
|                 swap_or_move(from, to); | ||||
|  | ||||
|                 using std::swap; | ||||
|                 swap(packed[from], packed[to]); | ||||
|                 packed[to] = std::exchange(packed[from], tombstone); | ||||
|                 const auto entity = static_cast<typename traits_type::entity_type>(to); | ||||
|                 sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); | ||||
|  | ||||
|                 const auto entity = static_cast<typename entity_traits::entity_type>(to); | ||||
|                 sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to])); | ||||
|                 *it = entity_traits::combine(static_cast<typename entity_traits::entity_type>(from), entity_traits::reserved); | ||||
|                 *it = traits_type::combine(static_cast<typename traits_type::entity_type>(from), tombstone); | ||||
|                 for(; from && packed[from - 1u] == tombstone; --from) {} | ||||
|             } | ||||
|         } | ||||
| @@ -814,21 +857,12 @@ public: | ||||
|      * @param rhs A valid identifier. | ||||
|      */ | ||||
|     void swap_elements(const entity_type lhs, const entity_type rhs) { | ||||
|         ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities"); | ||||
|         const auto from = index(lhs); | ||||
|         const auto to = index(rhs); | ||||
|  | ||||
|         auto &entt = sparse_ref(lhs); | ||||
|         auto &other = sparse_ref(rhs); | ||||
|  | ||||
|         const auto from = entity_traits::to_entity(entt); | ||||
|         const auto to = entity_traits::to_entity(other); | ||||
|  | ||||
|         // basic no-leak guarantee (with invalid state) if swapping throws | ||||
|         swap_at(static_cast<size_type>(from), static_cast<size_type>(to)); | ||||
|         entt = entity_traits::combine(to, entity_traits::to_integral(packed[from])); | ||||
|         other = entity_traits::combine(from, entity_traits::to_integral(packed[to])); | ||||
|  | ||||
|         using std::swap; | ||||
|         swap(packed[from], packed[to]); | ||||
|         // basic no-leak guarantee if swapping throws | ||||
|         swap_or_move(from, to); | ||||
|         swap_at(from, to); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -876,9 +910,9 @@ public: | ||||
|                 const auto idx = index(packed[next]); | ||||
|                 const auto entt = packed[curr]; | ||||
|  | ||||
|                 swap_at(next, idx); | ||||
|                 const auto entity = static_cast<typename entity_traits::entity_type>(curr); | ||||
|                 sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr])); | ||||
|                 swap_or_move(next, idx); | ||||
|                 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); | ||||
|             } | ||||
|         } | ||||
| @@ -906,48 +940,35 @@ public: | ||||
|      * @brief Sort entities according to their order in another sparse set. | ||||
|      * | ||||
|      * Entities that are part of both the sparse sets are ordered internally | ||||
|      * according to the order they have in `other`. All the other entities goes | ||||
|      * to the end of the list and there are no guarantees on their order.<br/> | ||||
|      * In other terms, this function can be used to impose the same order on two | ||||
|      * sets by using one of them as a master and the other one as a slave. | ||||
|      * | ||||
|      * Iterating the sparse set with a couple of iterators returns elements in | ||||
|      * the expected order after a call to `respect`. See `begin` and `end` for | ||||
|      * more details. | ||||
|      * 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. | ||||
|      * | ||||
|      * @param other The sparse sets that imposes the order of the entities. | ||||
|      */ | ||||
|     void respect(const basic_sparse_set &other) { | ||||
|     void sort_as(const basic_sparse_set &other) { | ||||
|         compact(); | ||||
|  | ||||
|         const auto to = other.end(); | ||||
|         auto from = other.begin(); | ||||
|  | ||||
|         for(size_type pos = packed.size() - 1; pos && from != to; ++from) { | ||||
|             if(contains(*from)) { | ||||
|                 if(*from != packed[pos]) { | ||||
|         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(packed[pos], *from); | ||||
|                     swap_elements(entt, curr); | ||||
|                 } | ||||
|  | ||||
|                 --pos; | ||||
|                 ++it; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @brief Clears a sparse set. */ | ||||
|     void clear() { | ||||
|         if(const auto last = end(); free_list == null) { | ||||
|             pop(begin(), last); | ||||
|         } else { | ||||
|             for(auto &&entity: *this) { | ||||
|                 // tombstone filter on itself | ||||
|                 if(const auto it = find(entity); it != last) { | ||||
|                     pop(it, it + 1u); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         pop_all(); | ||||
|         // sanity check to avoid subtle issues due to storage classes | ||||
|         ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); | ||||
|         free_list = tombstone; | ||||
|         packed.clear(); | ||||
|     } | ||||
|   | ||||
							
								
								
									
										607
									
								
								external/entt/entt/src/entt/entity/storage.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										607
									
								
								external/entt/entt/src/entt/entity/storage.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -9,7 +9,6 @@ | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| #include "../config/config.h" | ||||
| #include "../core/compressed_pair.hpp" | ||||
| #include "../core/iterator.hpp" | ||||
| #include "../core/memory.hpp" | ||||
| #include "../core/type_info.hpp" | ||||
| @@ -17,7 +16,6 @@ | ||||
| #include "entity.hpp" | ||||
| #include "fwd.hpp" | ||||
| #include "sparse_set.hpp" | ||||
| #include "storage_mixin.hpp" | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| @@ -28,13 +26,12 @@ namespace entt { | ||||
|  | ||||
| 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>; | ||||
|     using comp_traits = component_traits<std::remove_pointer_t<typename container_type::value_type>>; | ||||
|  | ||||
|     using iterator_traits = std::iterator_traits<std::conditional_t< | ||||
|         std::is_const_v<Container>, | ||||
| @@ -51,12 +48,12 @@ public: | ||||
|     constexpr storage_iterator() noexcept = default; | ||||
|  | ||||
|     constexpr storage_iterator(Container *ref, const difference_type idx) noexcept | ||||
|         : packed{ref}, | ||||
|         : payload{ref}, | ||||
|           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 | ||||
|         : storage_iterator{other.packed, other.offset} {} | ||||
|     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 { | ||||
|         return --offset, *this; | ||||
| @@ -96,12 +93,12 @@ public: | ||||
|  | ||||
|     [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { | ||||
|         const auto pos = index() - value; | ||||
|         return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; | ||||
|         return (*payload)[pos / Size][fast_mod(pos, Size)]; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] constexpr pointer operator->() const noexcept { | ||||
|         const auto pos = index(); | ||||
|         return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); | ||||
|         return (*payload)[pos / Size] + fast_mod(pos, Size); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] constexpr reference operator*() const noexcept { | ||||
| @@ -113,42 +110,42 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     Container *packed; | ||||
|     Container *payload; | ||||
|     difference_type offset; | ||||
| }; | ||||
|  | ||||
| template<typename CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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 CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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 CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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 CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr bool operator<(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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 CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr bool operator>(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &rhs) noexcept { | ||||
|     return lhs.index() < rhs.index(); | ||||
| 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 CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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 CLhs, typename CRhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const storage_iterator<CLhs> &lhs, const storage_iterator<CRhs> &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); | ||||
| } | ||||
|  | ||||
| @@ -158,10 +155,11 @@ class extended_storage_iterator final { | ||||
|     friend class extended_storage_iterator; | ||||
|  | ||||
| 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; | ||||
|  | ||||
|     constexpr extended_storage_iterator() | ||||
| @@ -191,20 +189,24 @@ public: | ||||
|         return {*std::get<It>(it), *std::get<Other>(it)...}; | ||||
|     } | ||||
|  | ||||
|     template<typename... CLhs, typename... CRhs> | ||||
|     friend constexpr bool operator==(const extended_storage_iterator<CLhs...> &, const extended_storage_iterator<CRhs...> &) noexcept; | ||||
|     [[nodiscard]] constexpr iterator_type base() const noexcept { | ||||
|         return std::get<It>(it); | ||||
|     } | ||||
|  | ||||
|     template<typename... Lhs, typename... Rhs> | ||||
|     friend constexpr bool operator==(const extended_storage_iterator<Lhs...> &, const extended_storage_iterator<Rhs...> &) noexcept; | ||||
|  | ||||
| private: | ||||
|     std::tuple<It, Other...> it; | ||||
| }; | ||||
|  | ||||
| template<typename... CLhs, typename... CRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator==(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept { | ||||
|     return std::get<0>(lhs.it) == std::get<0>(rhs.it); | ||||
| } | ||||
|  | ||||
| template<typename... CLhs, typename... CRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<CLhs...> &lhs, const extended_storage_iterator<CRhs...> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const extended_storage_iterator<Lhs...> &lhs, const extended_storage_iterator<Rhs...> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| @@ -227,43 +229,43 @@ template<typename... CLhs, typename... CRhs> | ||||
|  * normally available for non-empty types will not be available for empty ones. | ||||
|  * | ||||
|  * @tparam Type Type of objects assigned to the entities. | ||||
|  * @tparam Entity A valid entity type (see entt_traits for more details). | ||||
|  * @tparam Entity A valid entity type. | ||||
|  * @tparam Allocator Type of allocator used to manage memory and elements. | ||||
|  */ | ||||
| template<typename Type, typename Entity, typename Allocator, typename> | ||||
| class basic_storage: 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 underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; | ||||
|     using container_type = std::vector<typename alloc_traits::pointer, typename alloc_traits::template rebind_alloc<typename alloc_traits::pointer>>; | ||||
|     using comp_traits = component_traits<Type>; | ||||
|     using underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; | ||||
|     using underlying_iterator = typename underlying_type::basic_iterator; | ||||
|  | ||||
|     static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v<Type> && std::is_move_assignable_v<Type>); | ||||
|  | ||||
|     [[nodiscard]] auto &element_at(const std::size_t pos) const { | ||||
|         return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; | ||||
|         return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; | ||||
|     } | ||||
|  | ||||
|     auto assure_at_least(const std::size_t pos) { | ||||
|         auto &&container = packed.first(); | ||||
|         const auto idx = pos / comp_traits::page_size; | ||||
|         const auto idx = pos / traits_type::page_size; | ||||
|  | ||||
|         if(!(idx < container.size())) { | ||||
|             auto curr = container.size(); | ||||
|             container.resize(idx + 1u, nullptr); | ||||
|         if(!(idx < payload.size())) { | ||||
|             auto curr = payload.size(); | ||||
|             allocator_type allocator{get_allocator()}; | ||||
|             payload.resize(idx + 1u, nullptr); | ||||
|  | ||||
|             ENTT_TRY { | ||||
|                 for(const auto last = container.size(); curr < last; ++curr) { | ||||
|                     container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size); | ||||
|                 for(const auto last = payload.size(); curr < last; ++curr) { | ||||
|                     payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); | ||||
|                 } | ||||
|             } | ||||
|             ENTT_CATCH { | ||||
|                 container.resize(curr); | ||||
|                 payload.resize(curr); | ||||
|                 ENTT_THROW; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return container[idx] + fast_mod(pos, comp_traits::page_size); | ||||
|         return payload[idx] + fast_mod(pos, traits_type::page_size); | ||||
|     } | ||||
|  | ||||
|     template<typename... Args> | ||||
| @@ -272,7 +274,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra | ||||
|  | ||||
|         ENTT_TRY { | ||||
|             auto elem = assure_at_least(static_cast<size_type>(it.index())); | ||||
|             entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward<Args>(args)...); | ||||
|             entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...); | ||||
|         } | ||||
|         ENTT_CATCH { | ||||
|             base_type::pop(it, it + 1u); | ||||
| @@ -283,25 +285,24 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra | ||||
|     } | ||||
|  | ||||
|     void shrink_to_size(const std::size_t sz) { | ||||
|         const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; | ||||
|         allocator_type allocator{get_allocator()}; | ||||
|  | ||||
|         for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { | ||||
|             if constexpr(comp_traits::in_place_delete) { | ||||
|             if constexpr(traits_type::in_place_delete) { | ||||
|                 if(base_type::at(pos) != tombstone) { | ||||
|                     std::destroy_at(std::addressof(element_at(pos))); | ||||
|                     alloc_traits::destroy(allocator, std::addressof(element_at(pos))); | ||||
|                 } | ||||
|             } else { | ||||
|                 std::destroy_at(std::addressof(element_at(pos))); | ||||
|                 alloc_traits::destroy(allocator, std::addressof(element_at(pos))); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         auto &&container = packed.first(); | ||||
|         auto page_allocator{packed.second()}; | ||||
|         const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size; | ||||
|  | ||||
|         for(auto pos = from, last = container.size(); pos < last; ++pos) { | ||||
|             alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size); | ||||
|         for(auto pos = from, last = payload.size(); pos < last; ++pos) { | ||||
|             alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); | ||||
|         } | ||||
|  | ||||
|         container.resize(from); | ||||
|         payload.resize(from); | ||||
|     } | ||||
|  | ||||
| private: | ||||
| @@ -309,54 +310,68 @@ private: | ||||
|         return std::addressof(element_at(pos)); | ||||
|     } | ||||
|  | ||||
|     void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final { | ||||
|         // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy | ||||
|         ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type"); | ||||
|  | ||||
|         if constexpr(!is_pinned_type_v) { | ||||
|             using std::swap; | ||||
|             swap(element_at(lhs), element_at(rhs)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final { | ||||
|     void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { | ||||
|         // 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"); | ||||
|  | ||||
|         if constexpr(!is_pinned_type_v) { | ||||
|             auto &elem = element_at(from); | ||||
|             entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem)); | ||||
|             std::destroy_at(std::addressof(elem)); | ||||
|  | ||||
|             if constexpr(traits_type::in_place_delete) { | ||||
|                 if(base_type::operator[](to) == tombstone) { | ||||
|                     allocator_type allocator{get_allocator()}; | ||||
|                     entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); | ||||
|                     alloc_traits::destroy(allocator, std::addressof(elem)); | ||||
|                     return; | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             using std::swap; | ||||
|             swap(elem, element_at(to)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     /*! @brief Random access iterator type. */ | ||||
|     using basic_iterator = typename underlying_type::basic_iterator; | ||||
|  | ||||
|     /** | ||||
|      * @brief Erases entities from a sparse set. | ||||
|      * @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(basic_iterator first, basic_iterator last) override { | ||||
|         for(; first != last; ++first) { | ||||
|     void pop(underlying_iterator first, underlying_iterator last) override { | ||||
|         for(allocator_type allocator{get_allocator()}; first != last; ++first) { | ||||
|             // cannot use first.index() because it would break with cross iterators | ||||
|             auto &elem = element_at(base_type::index(*first)); | ||||
|  | ||||
|             if constexpr(comp_traits::in_place_delete) { | ||||
|             if constexpr(traits_type::in_place_delete) { | ||||
|                 base_type::in_place_pop(first); | ||||
|                 std::destroy_at(std::addressof(elem)); | ||||
|                 alloc_traits::destroy(allocator, std::addressof(elem)); | ||||
|             } else { | ||||
|                 auto &other = element_at(base_type::size() - 1u); | ||||
|                 // destroying on exit allows reentrant destructors | ||||
|                 [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); | ||||
|                 std::destroy_at(std::addressof(other)); | ||||
|                 alloc_traits::destroy(allocator, std::addressof(other)); | ||||
|                 base_type::swap_and_pop(first); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @brief Erases all entities of a storage. */ | ||||
|     void pop_all() override { | ||||
|         allocator_type allocator{get_allocator()}; | ||||
|  | ||||
|         for(auto first = base_type::begin(); !(first.index() < 0); ++first) { | ||||
|             if constexpr(traits_type::in_place_delete) { | ||||
|                 if(*first != tombstone) { | ||||
|                     base_type::in_place_pop(first); | ||||
|                     alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index())))); | ||||
|                 } | ||||
|             } else { | ||||
|                 base_type::swap_and_pop(first); | ||||
|                 alloc_traits::destroy(allocator, std::addressof(element_at(static_cast<size_type>(first.index())))); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns an entity to a storage. | ||||
|      * @param entt A valid identifier. | ||||
| @@ -364,7 +379,7 @@ protected: | ||||
|      * @param force_back Force back insertion. | ||||
|      * @return Iterator pointing to the emplaced element. | ||||
|      */ | ||||
|     basic_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { | ||||
|     underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { | ||||
|         if(value) { | ||||
|             if constexpr(std::is_copy_constructible_v<value_type>) { | ||||
|                 return emplace_element(entt, force_back, *static_cast<const value_type *>(value)); | ||||
| @@ -383,22 +398,24 @@ protected: | ||||
| public: | ||||
|     /*! @brief Base type. */ | ||||
|     using base_type = underlying_type; | ||||
|     /*! @brief Allocator type. */ | ||||
|     using allocator_type = Allocator; | ||||
|     /*! @brief Type of the objects assigned to entities. */ | ||||
|     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. */ | ||||
| @@ -407,6 +424,10 @@ public: | ||||
|     using iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::iterator, iterator>>; | ||||
|     /*! @brief Constant extended iterable storage proxy. */ | ||||
|     using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator, const_iterator>>; | ||||
|     /*! @brief Extended reverse iterable storage proxy. */ | ||||
|     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 Default constructor. */ | ||||
|     basic_storage() | ||||
| @@ -417,8 +438,8 @@ public: | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     explicit basic_storage(const allocator_type &allocator) | ||||
|         : base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator}, | ||||
|           packed{container_type{allocator}, allocator} {} | ||||
|         : base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator}, | ||||
|           payload{allocator} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Move constructor. | ||||
| @@ -426,7 +447,7 @@ public: | ||||
|      */ | ||||
|     basic_storage(basic_storage &&other) noexcept | ||||
|         : base_type{std::move(other)}, | ||||
|           packed{std::move(other.packed)} {} | ||||
|           payload{std::move(other.payload)} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Allocator-extended move constructor. | ||||
| @@ -435,8 +456,8 @@ public: | ||||
|      */ | ||||
|     basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept | ||||
|         : base_type{std::move(other), allocator}, | ||||
|           packed{container_type{std::move(other.packed.first()), allocator}, allocator} { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); | ||||
|           payload{std::move(other.payload), allocator} { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); | ||||
|     } | ||||
|  | ||||
|     /*! @brief Default destructor. */ | ||||
| @@ -450,12 +471,11 @@ public: | ||||
|      * @return This storage. | ||||
|      */ | ||||
|     basic_storage &operator=(basic_storage &&other) noexcept { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "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"); | ||||
|  | ||||
|         shrink_to_size(0u); | ||||
|         base_type::operator=(std::move(other)); | ||||
|         packed.first() = std::move(other.packed.first()); | ||||
|         propagate_on_container_move_assignment(packed.second(), other.packed.second()); | ||||
|         payload = std::move(other.payload); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
| @@ -465,9 +485,8 @@ public: | ||||
|      */ | ||||
|     void swap(basic_storage &other) { | ||||
|         using std::swap; | ||||
|         underlying_type::swap(other); | ||||
|         propagate_on_container_swap(packed.second(), other.packed.second()); | ||||
|         swap(packed.first(), other.packed.first()); | ||||
|         base_type::swap(other); | ||||
|         swap(payload, other.payload); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -475,7 +494,7 @@ public: | ||||
|      * @return The associated allocator. | ||||
|      */ | ||||
|     [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { | ||||
|         return allocator_type{packed.second()}; | ||||
|         return payload.get_allocator(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -499,7 +518,7 @@ public: | ||||
|      * @return Capacity of the storage. | ||||
|      */ | ||||
|     [[nodiscard]] size_type capacity() const noexcept override { | ||||
|         return packed.first().size() * comp_traits::page_size; | ||||
|         return payload.size() * traits_type::page_size; | ||||
|     } | ||||
|  | ||||
|     /*! @brief Requests the removal of unused capacity. */ | ||||
| @@ -513,25 +532,24 @@ public: | ||||
|      * @return A pointer to the array of objects. | ||||
|      */ | ||||
|     [[nodiscard]] const_pointer raw() const noexcept { | ||||
|         return packed.first().data(); | ||||
|         return payload.data(); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc raw */ | ||||
|     [[nodiscard]] pointer raw() noexcept { | ||||
|         return packed.first().data(); | ||||
|         return payload.data(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first instance of the internal array. | ||||
|      * If the storage is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first instance of the internal array. | ||||
|      */ | ||||
|     [[nodiscard]] const_iterator cbegin() const noexcept { | ||||
|         const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); | ||||
|         return const_iterator{&packed.first(), pos}; | ||||
|         return const_iterator{&payload, pos}; | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc cbegin */ | ||||
| @@ -542,21 +560,16 @@ public: | ||||
|     /*! @copydoc begin */ | ||||
|     [[nodiscard]] iterator begin() noexcept { | ||||
|         const auto pos = static_cast<typename iterator::difference_type>(base_type::size()); | ||||
|         return iterator{&packed.first(), pos}; | ||||
|         return iterator{&payload, pos}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last instance | ||||
|      * of the internal array. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last instance of the | ||||
|      * internal array. | ||||
|      */ | ||||
|     [[nodiscard]] const_iterator cend() const noexcept { | ||||
|         return const_iterator{&packed.first(), {}}; | ||||
|         return const_iterator{&payload, {}}; | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc cend */ | ||||
| @@ -566,15 +579,13 @@ public: | ||||
|  | ||||
|     /*! @copydoc end */ | ||||
|     [[nodiscard]] iterator end() noexcept { | ||||
|         return iterator{&packed.first(), {}}; | ||||
|         return iterator{&payload, {}}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reverse iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first instance of the reversed | ||||
|      * internal array. If the storage is empty, the returned iterator will be | ||||
|      * equal to `rend()`. | ||||
|      * If the storage is empty, the returned iterator will be equal to `rend()`. | ||||
|      * | ||||
|      * @return An iterator to the first instance of the reversed internal array. | ||||
|      */ | ||||
| @@ -594,11 +605,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reverse iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last instance | ||||
|      * of the reversed internal array. Attempting to dereference the returned | ||||
|      * iterator results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last instance of the | ||||
|      * reversed internal array. | ||||
|      */ | ||||
| @@ -663,7 +669,7 @@ public: | ||||
|      */ | ||||
|     template<typename... Args> | ||||
|     value_type &emplace(const entity_type entt, Args &&...args) { | ||||
|         if constexpr(std::is_aggregate_v<value_type>) { | ||||
|         if constexpr(std::is_aggregate_v<value_type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<value_type>)) { | ||||
|             const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...}); | ||||
|             return element_at(static_cast<size_type>(it.index())); | ||||
|         } else { | ||||
| @@ -699,12 +705,15 @@ 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 last element inserted, if any. | ||||
|      */ | ||||
|     template<typename It> | ||||
|     void insert(It first, It last, const value_type &value = {}) { | ||||
|     iterator insert(It first, It last, const value_type &value = {}) { | ||||
|         for(; first != last; ++first) { | ||||
|             emplace_element(*first, true, value); | ||||
|         } | ||||
|  | ||||
|         return begin(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -718,12 +727,15 @@ 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 from An iterator to the first element of the range of objects. | ||||
|      * @return Iterator pointing to the first element inserted, if any. | ||||
|      */ | ||||
|     template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>> | ||||
|     void insert(EIt first, EIt last, CIt from) { | ||||
|     iterator insert(EIt first, EIt last, CIt from) { | ||||
|         for(; first != last; ++first, ++from) { | ||||
|             emplace_element(*first, true, *from); | ||||
|         } | ||||
|  | ||||
|         return begin(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -743,34 +755,54 @@ public: | ||||
|         return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reverse iterable object to use to _visit_ a storage. | ||||
|      * | ||||
|      * @sa each | ||||
|      * | ||||
|      * @return A reverse iterable object to use to _visit_ the storage. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterable reach() noexcept { | ||||
|         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 {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}}; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     compressed_pair<container_type, allocator_type> packed; | ||||
|     container_type payload; | ||||
| }; | ||||
|  | ||||
| /*! @copydoc basic_storage */ | ||||
| template<typename Type, typename Entity, typename Allocator> | ||||
| class basic_storage<Type, Entity, Allocator, std::enable_if_t<ignore_as_empty_v<Type>>> | ||||
| class basic_storage<Type, Entity, Allocator, std::enable_if_t<component_traits<Type>::page_size == 0u>> | ||||
|     : 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 underlying_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; | ||||
|     using comp_traits = component_traits<Type>; | ||||
|  | ||||
| public: | ||||
|     /*! @brief Base type. */ | ||||
|     using base_type = underlying_type; | ||||
|     /*! @brief Allocator type. */ | ||||
|     using allocator_type = Allocator; | ||||
|     using base_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; | ||||
|     /*! @brief Type of the objects assigned to entities. */ | ||||
|     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. */ | ||||
|     using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>; | ||||
|     /*! @brief Extended reverse iterable storage proxy. */ | ||||
|     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 Default constructor. */ | ||||
|     basic_storage() | ||||
| @@ -781,7 +813,7 @@ public: | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     explicit basic_storage(const allocator_type &allocator) | ||||
|         : base_type{type_id<value_type>(), deletion_policy{comp_traits::in_place_delete}, allocator} {} | ||||
|         : base_type{type_id<value_type>(), deletion_policy{traits_type::in_place_delete}, allocator} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Move constructor. | ||||
| @@ -896,6 +928,325 @@ public: | ||||
|     [[nodiscard]] const_iterable each() const noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reverse iterable object to use to _visit_ a storage. | ||||
|      * | ||||
|      * @sa each | ||||
|      * | ||||
|      * @return A reverse iterable object to use to _visit_ the storage. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterable reach() noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc reach */ | ||||
|     [[nodiscard]] const_reverse_iterable reach() const noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Swap-only entity storage specialization. | ||||
|  * @tparam Entity A valid entity type. | ||||
|  * @tparam Allocator Type of allocator used to manage memory and elements. | ||||
|  */ | ||||
| template<typename Entity, typename Allocator> | ||||
| 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_type = basic_sparse_set<Entity, typename alloc_traits::template rebind_alloc<Entity>>; | ||||
|     using underlying_iterator = typename underlying_type::basic_iterator; | ||||
|     using local_traits_type = entt_traits<Entity>; | ||||
|  | ||||
|     auto entity_at(const std::size_t pos) const noexcept { | ||||
|         ENTT_ASSERT(pos < local_traits_type::to_entity(null), "Invalid element"); | ||||
|         return local_traits_type::combine(static_cast<typename local_traits_type::entity_type>(pos), {}); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) override { | ||||
|         ENTT_ASSERT(((lhs < length) + (rhs < length)) != 1u, "Cross swapping is not supported"); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     /** | ||||
|      * @brief Erases entities from a storage. | ||||
|      * @param first An iterator to the first element of the range of entities. | ||||
|      * @param last An iterator past the last element of the range of entities. | ||||
|      */ | ||||
|     void pop(underlying_iterator first, underlying_iterator last) override { | ||||
|         for(; first != last; ++first) { | ||||
|             if(const auto pos = base_type::index(*first); pos < length) { | ||||
|                 base_type::bump(local_traits_type::next(*first)); | ||||
|  | ||||
|                 if(pos != --length) { | ||||
|                     base_type::swap_at(pos, length); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @brief Erases all entities of a sparse set. */ | ||||
|     void pop_all() override { | ||||
|         length = 0u; | ||||
|         base_type::pop_all(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns an entity to a storage. | ||||
|      * @param hint A valid identifier. | ||||
|      * @return Iterator pointing to the emplaced element. | ||||
|      */ | ||||
|     underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { | ||||
|         return base_type::find(emplace(hint)); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     /*! @brief Base type. */ | ||||
|     using base_type = basic_sparse_set<Entity, Allocator>; | ||||
|     /*! @brief Type of the objects assigned to entities. */ | ||||
|     using value_type = Entity; | ||||
|     /*! @brief Component traits. */ | ||||
|     using traits_type = component_traits<value_type>; | ||||
|     /*! @brief Underlying entity identifier. */ | ||||
|     using entity_type = Entity; | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     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. */ | ||||
|     using const_iterable = iterable_adaptor<internal::extended_storage_iterator<typename base_type::const_iterator>>; | ||||
|     /*! @brief Extended reverse iterable storage proxy. */ | ||||
|     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 Default constructor. */ | ||||
|     basic_storage() | ||||
|         : basic_storage{allocator_type{}} { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs an empty container with a given allocator. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     explicit basic_storage(const allocator_type &allocator) | ||||
|         : base_type{type_id<value_type>(), deletion_policy::swap_and_pop, allocator}, | ||||
|           length{} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Move constructor. | ||||
|      * @param other The instance to move from. | ||||
|      */ | ||||
|     basic_storage(basic_storage &&other) noexcept | ||||
|         : base_type{std::move(other)}, | ||||
|           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) noexcept | ||||
|         : base_type{std::move(other), allocator}, | ||||
|           length{std::exchange(other.length, size_type{})} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Move assignment operator. | ||||
|      * @param other The instance to move from. | ||||
|      * @return This storage. | ||||
|      */ | ||||
|     basic_storage &operator=(basic_storage &&other) noexcept { | ||||
|         base_type::operator=(std::move(other)); | ||||
|         length = std::exchange(other.length, size_type{}); | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the object assigned to an entity, that is `void`. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to use an entity that doesn't belong to the storage results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @param entt A valid identifier. | ||||
|      */ | ||||
|     void get([[maybe_unused]] const entity_type entt) const noexcept { | ||||
|         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) < 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() { | ||||
|         if(length == base_type::size()) { | ||||
|             return *base_type::try_emplace(entity_at(length++), true); | ||||
|         } | ||||
|  | ||||
|         return base_type::operator[](length++); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Creates a new identifier or recycles a destroyed one. | ||||
|      * | ||||
|      * If the requested identifier isn't in use, the suggested one is used. | ||||
|      * Otherwise, a new identifier is returned. | ||||
|      * | ||||
|      * @param hint Required identifier. | ||||
|      * @return A valid identifier. | ||||
|      */ | ||||
|     entity_type emplace(const entity_type hint) { | ||||
|         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++); | ||||
|         } | ||||
|  | ||||
|         base_type::bump(hint); | ||||
|  | ||||
|         return hint; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Updates a given identifier. | ||||
|      * @tparam Func Types of the function objects to invoke. | ||||
|      * @param entt A valid identifier. | ||||
|      * @param func Valid function objects. | ||||
|      */ | ||||
|     template<typename... Func> | ||||
|     void patch([[maybe_unused]] const entity_type entt, Func &&...func) { | ||||
|         ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); | ||||
|         (std::forward<Func>(func)(), ...); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns each element in a range an identifier. | ||||
|      * @tparam It Type of mutable forward iterator. | ||||
|      * @param first An iterator to the first element of the range to generate. | ||||
|      * @param last An iterator past the last element of the range to generate. | ||||
|      */ | ||||
|     template<typename It> | ||||
|     void insert(It first, It last) { | ||||
|         for(const auto sz = base_type::size(); first != last && length != sz; ++first, ++length) { | ||||
|             *first = base_type::operator[](length); | ||||
|         } | ||||
|  | ||||
|         for(; first != last; ++first) { | ||||
|             *first = *base_type::try_emplace(entity_at(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. | ||||
|      * | ||||
|      * The iterable object returns a tuple that contains the current entity. | ||||
|      * | ||||
|      * @return An iterable object to use to _visit_ the storage. | ||||
|      */ | ||||
|     [[nodiscard]] iterable each() noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::end() - length}, internal::extended_storage_iterator{base_type::end()}}; | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc each */ | ||||
|     [[nodiscard]] const_iterable each() const noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::cend() - length}, internal::extended_storage_iterator{base_type::cend()}}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a reverse iterable object to use to _visit_ a storage. | ||||
|      * | ||||
|      * @sa each | ||||
|      * | ||||
|      * @return A reverse iterable object to use to _visit_ the storage. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterable reach() noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rbegin() + length}}; | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc reach */ | ||||
|     [[nodiscard]] const_reverse_iterable reach() const noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crbegin() + length}}; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     size_type length; | ||||
| }; | ||||
|  | ||||
| } // namespace entt | ||||
|   | ||||
							
								
								
									
										236
									
								
								external/entt/entt/src/entt/entity/storage_mixin.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										236
									
								
								external/entt/entt/src/entt/entity/storage_mixin.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -1,236 +0,0 @@ | ||||
| #ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP | ||||
| #define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP | ||||
|  | ||||
| #include <utility> | ||||
| #include "../config/config.h" | ||||
| #include "../core/any.hpp" | ||||
| #include "../signal/sigh.hpp" | ||||
| #include "fwd.hpp" | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| /** | ||||
|  * @brief Mixin type used to add signal support to storage types. | ||||
|  * | ||||
|  * The function type of a listener is equivalent to: | ||||
|  * | ||||
|  * @code{.cpp} | ||||
|  * void(basic_registry<entity_type> &, entity_type); | ||||
|  * @endcode | ||||
|  * | ||||
|  * This applies to all signals made available. | ||||
|  * | ||||
|  * @tparam Type The type of the underlying storage. | ||||
|  */ | ||||
| template<typename Type> | ||||
| class sigh_storage_mixin final: public Type { | ||||
|     using basic_registry_type = basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>; | ||||
|     using sigh_type = sigh<void(basic_registry_type &, const typename Type::entity_type), typename Type::allocator_type>; | ||||
|     using basic_iterator = typename Type::basic_iterator; | ||||
|  | ||||
|     void pop(basic_iterator first, basic_iterator last) override { | ||||
|         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); | ||||
|  | ||||
|         for(; first != last; ++first) { | ||||
|             const auto entt = *first; | ||||
|             destruction.publish(*owner, entt); | ||||
|             const auto it = Type::find(entt); | ||||
|             Type::pop(it, it + 1u); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final { | ||||
|         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); | ||||
|         Type::try_emplace(entt, force_back, value); | ||||
|         construction.publish(*owner, entt); | ||||
|         return Type::find(entt); | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     /*! @brief Allocator type. */ | ||||
|     using allocator_type = typename Type::allocator_type; | ||||
|     /*! @brief Underlying entity identifier. */ | ||||
|     using entity_type = typename Type::entity_type; | ||||
|     /*! @brief Expected registry type. */ | ||||
|     using registry_type = basic_registry_type; | ||||
|  | ||||
|     /*! @brief Default constructor. */ | ||||
|     sigh_storage_mixin() | ||||
|         : sigh_storage_mixin{allocator_type{}} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs an empty storage with a given allocator. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     explicit sigh_storage_mixin(const allocator_type &allocator) | ||||
|         : Type{allocator}, | ||||
|           owner{}, | ||||
|           construction{allocator}, | ||||
|           destruction{allocator}, | ||||
|           update{allocator} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Move constructor. | ||||
|      * @param other The instance to move from. | ||||
|      */ | ||||
|     sigh_storage_mixin(sigh_storage_mixin &&other) noexcept | ||||
|         : Type{std::move(other)}, | ||||
|           owner{other.owner}, | ||||
|           construction{std::move(other.construction)}, | ||||
|           destruction{std::move(other.destruction)}, | ||||
|           update{std::move(other.update)} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Allocator-extended move constructor. | ||||
|      * @param other The instance to move from. | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept | ||||
|         : 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 Move assignment operator. | ||||
|      * @param other The instance to move from. | ||||
|      * @return This storage. | ||||
|      */ | ||||
|     sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept { | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Exchanges the contents with those of a given storage. | ||||
|      * @param other Storage to exchange the content with. | ||||
|      */ | ||||
|     void swap(sigh_storage_mixin &other) { | ||||
|         using std::swap; | ||||
|         Type::swap(other); | ||||
|         swap(owner, other.owner); | ||||
|         swap(construction, other.construction); | ||||
|         swap(destruction, other.destruction); | ||||
|         swap(update, other.update); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink object. | ||||
|      * | ||||
|      * The sink returned by this function can be used to receive notifications | ||||
|      * whenever a new instance is created and assigned to an entity.<br/> | ||||
|      * Listeners are invoked after the object has been assigned to the entity. | ||||
|      * | ||||
|      * @sa sink | ||||
|      * | ||||
|      * @return A temporary sink object. | ||||
|      */ | ||||
|     [[nodiscard]] auto on_construct() noexcept { | ||||
|         return sink{construction}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink object. | ||||
|      * | ||||
|      * The sink returned by this function can be used to receive notifications | ||||
|      * whenever an instance is explicitly updated.<br/> | ||||
|      * Listeners are invoked after the object has been updated. | ||||
|      * | ||||
|      * @sa sink | ||||
|      * | ||||
|      * @return A temporary sink object. | ||||
|      */ | ||||
|     [[nodiscard]] auto on_update() noexcept { | ||||
|         return sink{update}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink object. | ||||
|      * | ||||
|      * The sink returned by this function can be used to receive notifications | ||||
|      * whenever an instance is removed from an entity and thus destroyed.<br/> | ||||
|      * Listeners are invoked before the object has been removed from the entity. | ||||
|      * | ||||
|      * @sa sink | ||||
|      * | ||||
|      * @return A temporary sink object. | ||||
|      */ | ||||
|     [[nodiscard]] auto on_destroy() noexcept { | ||||
|         return sink{destruction}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns entities to a storage. | ||||
|      * @tparam Args Types of arguments to use to construct the object. | ||||
|      * @param entt A valid identifier. | ||||
|      * @param args Parameters to use to initialize the object. | ||||
|      * @return A reference to the newly created object. | ||||
|      */ | ||||
|     template<typename... Args> | ||||
|     decltype(auto) emplace(const entity_type entt, Args &&...args) { | ||||
|         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); | ||||
|         Type::emplace(entt, std::forward<Args>(args)...); | ||||
|         construction.publish(*owner, entt); | ||||
|         return this->get(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Patches the given instance for an entity. | ||||
|      * @tparam Func Types of the function objects to invoke. | ||||
|      * @param entt A valid identifier. | ||||
|      * @param func Valid function objects. | ||||
|      * @return A reference to the patched instance. | ||||
|      */ | ||||
|     template<typename... Func> | ||||
|     decltype(auto) patch(const entity_type entt, Func &&...func) { | ||||
|         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); | ||||
|         Type::patch(entt, std::forward<Func>(func)...); | ||||
|         update.publish(*owner, entt); | ||||
|         return this->get(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns entities to a storage. | ||||
|      * @tparam It Type of input iterator. | ||||
|      * @tparam Args Types of arguments to use to construct the objects assigned | ||||
|      * to the entities. | ||||
|      * @param first An iterator to the first element of the range of entities. | ||||
|      * @param last An iterator past the last element of the range of entities. | ||||
|      * @param args Parameters to use to initialize the objects assigned to the | ||||
|      * entities. | ||||
|      */ | ||||
|     template<typename It, typename... Args> | ||||
|     void insert(It first, It last, Args &&...args) { | ||||
|         ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); | ||||
|         Type::insert(first, last, std::forward<Args>(args)...); | ||||
|  | ||||
|         for(auto it = construction.empty() ? last : first; it != last; ++it) { | ||||
|             construction.publish(*owner, *it); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Forwards variables to 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; | ||||
|         Type::bind(std::move(value)); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     basic_registry_type *owner; | ||||
|     sigh_type construction; | ||||
|     sigh_type destruction; | ||||
|     sigh_type update; | ||||
| }; | ||||
|  | ||||
| } // namespace entt | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										407
									
								
								external/entt/entt/src/entt/entity/view.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										407
									
								
								external/entt/entt/src/entt/entity/view.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -9,11 +9,8 @@ | ||||
| #include "../config/config.h" | ||||
| #include "../core/iterator.hpp" | ||||
| #include "../core/type_traits.hpp" | ||||
| #include "component.hpp" | ||||
| #include "entity.hpp" | ||||
| #include "fwd.hpp" | ||||
| #include "sparse_set.hpp" | ||||
| #include "storage.hpp" | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| @@ -24,6 +21,24 @@ namespace entt { | ||||
|  | ||||
| namespace internal { | ||||
|  | ||||
| template<typename... Args, typename Type, std::size_t N> | ||||
| [[nodiscard]] auto filter_as_tuple(const std::array<const Type *, N> &filter) noexcept { | ||||
|     return std::apply([](const auto *...curr) { return std::make_tuple(static_cast<Args *>(const_cast<constness_as_t<Type, Args> *>(curr))...); }, filter); | ||||
| } | ||||
|  | ||||
| template<typename Type, std::size_t N> | ||||
| [[nodiscard]] auto none_of(const std::array<const Type *, N> &filter, const typename Type::entity_type entt) noexcept { | ||||
|     return std::apply([entt](const auto *...curr) { return (!(curr && curr->contains(entt)) && ...); }, filter); | ||||
| } | ||||
|  | ||||
| template<typename... Get, typename... Exclude, std::size_t... Index> | ||||
| [[nodiscard]] auto view_pack(const std::tuple<Get *...> value, const std::tuple<Exclude *...> excl, std::index_sequence<Index...>) { | ||||
|     const auto pools = std::tuple_cat(value, excl); | ||||
|     basic_view<get_t<Get...>, exclude_t<Exclude...>> elem{}; | ||||
|     (((std::get<Index>(pools) != nullptr) ? elem.template storage<Index>(*std::get<Index>(pools)) : void()), ...); | ||||
|     return elem; | ||||
| } | ||||
|  | ||||
| template<typename Type, std::size_t Get, std::size_t Exclude> | ||||
| class view_iterator final { | ||||
|     using iterator_type = typename Type::const_iterator; | ||||
| @@ -31,7 +46,7 @@ class view_iterator final { | ||||
|     [[nodiscard]] bool valid() const noexcept { | ||||
|         return ((Get != 0u) || (*it != tombstone)) | ||||
|                && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) | ||||
|                && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); | ||||
|                && none_of(filter, *it); | ||||
|     } | ||||
|  | ||||
| public: | ||||
| @@ -47,11 +62,11 @@ public: | ||||
|           pools{}, | ||||
|           filter{} {} | ||||
|  | ||||
|     view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Get> all_of, std::array<const Type *, Exclude> none_of) noexcept | ||||
|     view_iterator(iterator_type curr, iterator_type to, std::array<const Type *, Get> value, std::array<const Type *, Exclude> excl) noexcept | ||||
|         : it{curr}, | ||||
|           last{to}, | ||||
|           pools{all_of}, | ||||
|           filter{none_of} { | ||||
|           pools{value}, | ||||
|           filter{excl} { | ||||
|         while(it != last && !valid()) { | ||||
|             ++it; | ||||
|         } | ||||
| @@ -97,6 +112,7 @@ template<typename LhsType, auto... LhsArgs, typename RhsType, auto... RhsArgs> | ||||
|  | ||||
| template<typename It, typename... Type> | ||||
| struct extended_view_iterator final { | ||||
|     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<Type>().get_as_tuple({})...)); | ||||
|     using pointer = input_iterator_pointer<value_type>; | ||||
| @@ -107,9 +123,9 @@ struct extended_view_iterator final { | ||||
|         : it{}, | ||||
|           pools{} {} | ||||
|  | ||||
|     extended_view_iterator(It from, std::tuple<Type *...> storage) | ||||
|     extended_view_iterator(It from, std::tuple<Type *...> value) | ||||
|         : it{from}, | ||||
|           pools{storage} {} | ||||
|           pools{value} {} | ||||
|  | ||||
|     extended_view_iterator &operator++() noexcept { | ||||
|         return ++it, *this; | ||||
| @@ -128,6 +144,10 @@ struct extended_view_iterator final { | ||||
|         return operator*(); | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] constexpr iterator_type base() const noexcept { | ||||
|         return it; | ||||
|     } | ||||
|  | ||||
|     template<typename... Lhs, typename... Rhs> | ||||
|     friend bool constexpr operator==(const extended_view_iterator<Lhs...> &, const extended_view_iterator<Rhs...> &) noexcept; | ||||
|  | ||||
| @@ -179,31 +199,33 @@ class basic_view; | ||||
|  *   or removed from it). | ||||
|  * * The entity currently pointed is destroyed. | ||||
|  * | ||||
|  * In all other cases, modifying the pools iterated by the view in any way | ||||
|  * invalidates all the iterators and using them results in undefined behavior. | ||||
|  * In all other cases, modifying the storage iterated by the view in any way | ||||
|  * invalidates all the iterators. | ||||
|  * | ||||
|  * @tparam Get Types of storage iterated by the view. | ||||
|  * @tparam Exclude Types of storage used to filter the view. | ||||
|  */ | ||||
| template<typename... Get, typename... Exclude> | ||||
| class basic_view<get_t<Get...>, exclude_t<Exclude...>> { | ||||
|     using underlying_type = std::common_type_t<typename Get::entity_type..., typename Exclude::entity_type...>; | ||||
|     using basic_common_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>; | ||||
|     static constexpr auto offset = sizeof...(Get); | ||||
|     using base_type = std::common_type_t<typename Get::base_type..., typename Exclude::base_type...>; | ||||
|     using underlying_type = typename base_type::entity_type; | ||||
|  | ||||
|     template<typename, typename, typename> | ||||
|     friend class basic_view; | ||||
|  | ||||
|     template<typename Type> | ||||
|     static constexpr std::size_t index_of = type_list_index_v<std::remove_const_t<Type>, type_list<typename Get::value_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...>>; | ||||
|  | ||||
|     [[nodiscard]] auto opaque_check_set() const noexcept { | ||||
|         std::array<const base_type *, sizeof...(Get) - 1u> other{}; | ||||
|         std::array<const common_type *, sizeof...(Get) - 1u> other{}; | ||||
|         std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); | ||||
|         return other; | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] auto filter_as_array() const noexcept { | ||||
|         return std::apply([](const auto *...curr) { return std::array<const base_type *, sizeof...(Exclude)>{curr...}; }, filter); | ||||
|     void unchecked_refresh() noexcept { | ||||
|         view = std::get<0>(pools); | ||||
|         std::apply([this](auto *, auto *...other) { ((this->view = other->size() < this->view->size() ? other : this->view), ...); }, pools); | ||||
|     } | ||||
|  | ||||
|     template<std::size_t Curr, std::size_t Other, typename... Args> | ||||
| @@ -211,18 +233,14 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>> { | ||||
|         if constexpr(Curr == Other) { | ||||
|             return std::forward_as_tuple(std::get<Args>(curr)...); | ||||
|         } else { | ||||
|             return storage<Other>().get_as_tuple(std::get<0>(curr)); | ||||
|             return std::get<Other>(pools)->get_as_tuple(std::get<0>(curr)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     [[nodiscard]] auto reject(const underlying_type entt) const noexcept { | ||||
|         return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter); | ||||
|     } | ||||
|  | ||||
|     template<std::size_t Curr, typename Func, std::size_t... Index> | ||||
|     void each(Func &func, std::index_sequence<Index...>) const { | ||||
|         for(const auto curr: storage<Curr>().each()) { | ||||
|             if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage<Index>().contains(entt)) && ...) && !reject(entt)) { | ||||
|         for(const auto curr: std::get<Curr>(pools)->each()) { | ||||
|             if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || std::get<Index>(pools)->contains(entt)) && ...) && internal::none_of(filter, entt)) { | ||||
|                 if constexpr(is_applicable_v<Func, decltype(std::tuple_cat(std::tuple<entity_type>{}, std::declval<basic_view>().get({})))>) { | ||||
|                     std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get<Curr, Index>(curr)...)); | ||||
|                 } else { | ||||
| @@ -234,7 +252,7 @@ class basic_view<get_t<Get...>, exclude_t<Exclude...>> { | ||||
|  | ||||
|     template<typename Func, std::size_t... Index> | ||||
|     void pick_and_each(Func &func, std::index_sequence<Index...> seq) const { | ||||
|         ((&storage<Index>() == view ? each<Index>(func, seq) : void()), ...); | ||||
|         ((std::get<Index>(pools) == view ? each<Index>(func, seq) : void()), ...); | ||||
|     } | ||||
|  | ||||
| public: | ||||
| @@ -243,9 +261,9 @@ public: | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using size_type = std::size_t; | ||||
|     /*! @brief Common type among all storage types. */ | ||||
|     using base_type = basic_common_type; | ||||
|     using common_type = base_type; | ||||
|     /*! @brief Bidirectional iterator type. */ | ||||
|     using iterator = internal::view_iterator<base_type, sizeof...(Get) - 1u, sizeof...(Exclude)>; | ||||
|     using iterator = internal::view_iterator<common_type, sizeof...(Get) - 1u, sizeof...(Exclude)>; | ||||
|     /*! @brief Iterable view type. */ | ||||
|     using iterable = iterable_adaptor<internal::extended_view_iterator<iterator, Get...>>; | ||||
|  | ||||
| @@ -258,12 +276,14 @@ public: | ||||
|     /** | ||||
|      * @brief Constructs a multi-type view from a set of storage classes. | ||||
|      * @param value The storage for the types to iterate. | ||||
|      * @param exclude The storage for the types used to filter the view. | ||||
|      * @param excl The storage for the types used to filter the view. | ||||
|      */ | ||||
|     basic_view(Get &...value, Exclude &...exclude) noexcept | ||||
|     basic_view(Get &...value, Exclude &...excl) noexcept | ||||
|         : pools{&value...}, | ||||
|           filter{&exclude...}, | ||||
|           view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {} | ||||
|           filter{&excl...}, | ||||
|           view{} { | ||||
|         unchecked_refresh(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a multi-type view from a set of storage classes. | ||||
| @@ -271,66 +291,91 @@ public: | ||||
|      * @param excl The storage for the types used to filter the view. | ||||
|      */ | ||||
|     basic_view(std::tuple<Get &...> value, std::tuple<Exclude &...> excl = {}) noexcept | ||||
|         : pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)}, | ||||
|           filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, excl)}, | ||||
|           view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {} | ||||
|         : basic_view{std::make_from_tuple<basic_view>(std::tuple_cat(value, excl))} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Creates a new view driven by a given component in its iterations. | ||||
|      * @tparam Type Type of component used to drive the iteration. | ||||
|      * @return A new view driven by the given component in its iterations. | ||||
|      * @brief Forces a view to use a given component to drive iterations | ||||
|      * @tparam Type Type of component to use to drive iterations. | ||||
|      */ | ||||
|     template<typename Type> | ||||
|     [[nodiscard]] basic_view use() const noexcept { | ||||
|         return use<index_of<Type>>(); | ||||
|     void use() noexcept { | ||||
|         use<index_of<Type>>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Creates a new view driven by a given component in its iterations. | ||||
|      * @tparam Index Index of the component used to drive the iteration. | ||||
|      * @return A new view driven by the given component in its iterations. | ||||
|      * @brief Forces a view to use a given component to drive iterations | ||||
|      * @tparam Index Index of the component to use to drive iterations. | ||||
|      */ | ||||
|     template<std::size_t Index> | ||||
|     [[nodiscard]] basic_view use() const noexcept { | ||||
|         basic_view other{*this}; | ||||
|         other.view = &storage<Index>(); | ||||
|         return other; | ||||
|     void use() noexcept { | ||||
|         if(view) { | ||||
|             view = std::get<Index>(pools); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @brief Updates the internal leading view if required. */ | ||||
|     void refresh() noexcept { | ||||
|         if(view || std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools)) { | ||||
|             unchecked_refresh(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Updates the internal leading view if required. | ||||
|      * @return A newly created and internally optimized view. | ||||
|      */ | ||||
|     [[nodiscard]] basic_view refresh() const noexcept { | ||||
|         return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the leading storage of a view. | ||||
|      * @brief Returns the leading storage of a view, if any. | ||||
|      * @return The leading storage of the view. | ||||
|      */ | ||||
|     [[nodiscard]] const base_type &handle() const noexcept { | ||||
|         return *view; | ||||
|     [[nodiscard]] const common_type *handle() const noexcept { | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given component type. | ||||
|      * @tparam Comp Type of component of which to return the storage. | ||||
|      * @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]] decltype(auto) storage() const noexcept { | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         return storage<index_of<Type>>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given index. | ||||
|      * @brief Returns the storage for a given index, if any. | ||||
|      * @tparam Index Index of the storage to return. | ||||
|      * @return The storage for the given index. | ||||
|      */ | ||||
|     template<std::size_t Index> | ||||
|     [[nodiscard]] decltype(auto) storage() const noexcept { | ||||
|         return *std::get<Index>(pools); | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         if constexpr(Index < offset) { | ||||
|             return std::get<Index>(pools); | ||||
|         } else { | ||||
|             return std::get<Index - offset>(internal::filter_as_tuple<Exclude...>(filter)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns a storage to a view. | ||||
|      * @tparam Type Type of storage to assign to the view. | ||||
|      * @param elem A storage to assign to the view. | ||||
|      */ | ||||
|     template<typename Type> | ||||
|     void storage(Type &elem) noexcept { | ||||
|         storage<index_of<typename Type::value_type>>(elem); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns a storage to a view. | ||||
|      * @tparam Index Index of the storage to assign to the view. | ||||
|      * @tparam Type Type of storage to assign to the view. | ||||
|      * @param elem A storage to assign to the view. | ||||
|      */ | ||||
|     template<std::size_t Index, typename Type> | ||||
|     void storage(Type &elem) noexcept { | ||||
|         if constexpr(Index < offset) { | ||||
|             std::get<Index>(pools) = &elem; | ||||
|             refresh(); | ||||
|         } else { | ||||
|             std::get<Index - offset>(filter) = &elem; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -338,32 +383,26 @@ public: | ||||
|      * @return Estimated number of entities iterated by the view. | ||||
|      */ | ||||
|     [[nodiscard]] size_type size_hint() const noexcept { | ||||
|         return view->size(); | ||||
|         return view ? view->size() : size_type{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the view. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the view. If the view | ||||
|      * is empty, the returned iterator will be equal to `end()`. | ||||
|      * If the view is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the view. | ||||
|      */ | ||||
|     [[nodiscard]] iterator begin() const noexcept { | ||||
|         return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; | ||||
|         return view ? iterator{view->begin(), view->end(), opaque_check_set(), filter} : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the view. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the view. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the view. | ||||
|      */ | ||||
|     [[nodiscard]] iterator end() const noexcept { | ||||
|         return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; | ||||
|         return view ? iterator{view->end(), view->end(), opaque_check_set(), filter} : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -382,11 +421,15 @@ public: | ||||
|      * otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] entity_type back() const noexcept { | ||||
|         if(view) { | ||||
|             auto it = view->rbegin(); | ||||
|             for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} | ||||
|             return it == view->rend() ? null : *it; | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Finds an entity. | ||||
|      * @param entt A valid identifier. | ||||
| @@ -394,7 +437,7 @@ public: | ||||
|      * iterator otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] iterator find(const entity_type entt) const noexcept { | ||||
|         return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end(); | ||||
|         return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter} : end(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -407,11 +450,12 @@ public: | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Checks if a view is properly initialized. | ||||
|      * @return True if the view is properly initialized, false otherwise. | ||||
|      * @brief Checks if a view is fully initialized. | ||||
|      * @return True if the view is fully initialized, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] explicit operator bool() const noexcept { | ||||
|         return view != nullptr; | ||||
|         return std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, pools) | ||||
|                && std::apply([](const auto *...curr) { return ((curr != nullptr) && ...); }, filter); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -420,8 +464,7 @@ public: | ||||
|      * @return True if the view contains the given entity, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool contains(const entity_type entt) const noexcept { | ||||
|         return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) | ||||
|                && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); | ||||
|         return view && std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && internal::none_of(filter, entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -431,39 +474,33 @@ public: | ||||
|      * Attempting to use an entity that doesn't belong to the view results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam Type Types of components to get. | ||||
|      * @tparam Type Type of the component to get. | ||||
|      * @tparam Other Other types of components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<typename... Type> | ||||
|     template<typename Type, typename... Other> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         if constexpr(sizeof...(Type) == 0) { | ||||
|         return get<index_of<Type>, index_of<Other>...>(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the components assigned to the given entity. | ||||
|      * | ||||
|      * @sa get | ||||
|      * | ||||
|      * @tparam Index Indexes of the components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<std::size_t... Index> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         if constexpr(sizeof...(Index) == 0) { | ||||
|             return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); | ||||
|         } else if constexpr(sizeof...(Type) == 1) { | ||||
|             return (storage<index_of<Type>>().get(entt), ...); | ||||
|         } else if constexpr(sizeof...(Index) == 1) { | ||||
|             return (std::get<Index>(pools)->get(entt), ...); | ||||
|         } else { | ||||
|             return std::tuple_cat(storage<index_of<Type>>().get_as_tuple(entt)...); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the components assigned to the given entity. | ||||
|      * | ||||
|      * @warning | ||||
|      * Attempting to use an entity that doesn't belong to the view results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam First Index of a component to get. | ||||
|      * @tparam Other Indexes of other components to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The components assigned to the entity. | ||||
|      */ | ||||
|     template<std::size_t First, std::size_t... Other> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         if constexpr(sizeof...(Other) == 0) { | ||||
|             return storage<First>().get(entt); | ||||
|         } else { | ||||
|             return std::tuple_cat(storage<First>().get_as_tuple(entt), storage<Other>().get_as_tuple(entt)...); | ||||
|             return std::tuple_cat(std::get<Index>(pools)->get_as_tuple(entt)...); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -487,7 +524,7 @@ public: | ||||
|      */ | ||||
|     template<typename Func> | ||||
|     void each(Func func) const { | ||||
|         pick_and_each(func, std::index_sequence_for<Get...>{}); | ||||
|         view ? pick_and_each(func, std::index_sequence_for<Get...>{}) : void(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -512,14 +549,16 @@ public: | ||||
|      */ | ||||
|     template<typename... OGet, typename... OExclude> | ||||
|     [[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept { | ||||
|         return std::make_from_tuple<basic_view<get_t<Get..., OGet...>, exclude_t<Exclude..., OExclude...>>>( | ||||
|             std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter))); | ||||
|         return internal::view_pack( | ||||
|             std::tuple_cat(pools, other.pools), | ||||
|             std::tuple_cat(internal::filter_as_tuple<Exclude...>(filter), internal::filter_as_tuple<OExclude...>(other.filter)), | ||||
|             std::index_sequence_for<Get..., OGet..., Exclude..., OExclude...>{}); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::tuple<Get *...> pools; | ||||
|     std::tuple<Exclude *...> filter; | ||||
|     const base_type *view; | ||||
|     std::array<const common_type *, sizeof...(Exclude)> filter; | ||||
|     const common_type *view; | ||||
| }; | ||||
|  | ||||
| /** | ||||
| @@ -538,13 +577,13 @@ private: | ||||
|  *   or removed from it). | ||||
|  * * The entity currently pointed is destroyed. | ||||
|  * | ||||
|  * In all other cases, modifying the pool iterated by the view in any way | ||||
|  * invalidates all the iterators and using them results in undefined behavior. | ||||
|  * In all other cases, modifying the storage iterated by the view in any way | ||||
|  * invalidates all the iterators. | ||||
|  * | ||||
|  * @tparam Get Type of storage iterated by the view. | ||||
|  */ | ||||
| template<typename Get> | ||||
| class basic_view<get_t<Get>, exclude_t<>, std::void_t<std::enable_if_t<!component_traits<typename Get::value_type>::in_place_delete>>> { | ||||
| class basic_view<get_t<Get>, exclude_t<>, std::void_t<std::enable_if_t<!Get::traits_type::in_place_delete>>> { | ||||
|     template<typename, typename, typename> | ||||
|     friend class basic_view; | ||||
|  | ||||
| @@ -554,62 +593,81 @@ public: | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using size_type = std::size_t; | ||||
|     /*! @brief Common type among all storage types. */ | ||||
|     using base_type = typename Get::base_type; | ||||
|     using common_type = typename Get::base_type; | ||||
|     /*! @brief Random access iterator type. */ | ||||
|     using iterator = typename base_type::iterator; | ||||
|     using iterator = typename common_type::iterator; | ||||
|     /*! @brief Reversed iterator type. */ | ||||
|     using reverse_iterator = typename base_type::reverse_iterator; | ||||
|     using reverse_iterator = typename common_type::reverse_iterator; | ||||
|     /*! @brief Iterable view type. */ | ||||
|     using iterable = decltype(std::declval<Get>().each()); | ||||
|  | ||||
|     /*! @brief Default constructor to use to create empty, invalid views. */ | ||||
|     basic_view() noexcept | ||||
|         : pools{}, | ||||
|           filter{} {} | ||||
|           filter{}, | ||||
|           view{} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a single-type view from a storage class. | ||||
|      * @param ref The storage for the type to iterate. | ||||
|      * @param value The storage for the type to iterate. | ||||
|      */ | ||||
|     basic_view(Get &ref) noexcept | ||||
|         : pools{&ref}, | ||||
|           filter{} {} | ||||
|     basic_view(Get &value) noexcept | ||||
|         : pools{&value}, | ||||
|           filter{}, | ||||
|           view{&value} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Constructs a single-type view from a storage class. | ||||
|      * @param ref The storage for the type to iterate. | ||||
|      * @param value The storage for the type to iterate. | ||||
|      */ | ||||
|     basic_view(std::tuple<Get &> ref, std::tuple<> = {}) noexcept | ||||
|         : pools{&std::get<0>(ref)}, | ||||
|           filter{} {} | ||||
|     basic_view(std::tuple<Get &> value, std::tuple<> = {}) noexcept | ||||
|         : basic_view{std::get<0>(value)} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the leading storage of a view. | ||||
|      * @brief Returns the leading storage of a view, if any. | ||||
|      * @return The leading storage of the view. | ||||
|      */ | ||||
|     [[nodiscard]] const base_type &handle() const noexcept { | ||||
|         return storage(); | ||||
|     [[nodiscard]] const common_type *handle() const noexcept { | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given component 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 = typename Get::value_type> | ||||
|     [[nodiscard]] decltype(auto) storage() const noexcept { | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         static_assert(std::is_same_v<std::remove_const_t<Type>, typename Get::value_type>, "Invalid component type"); | ||||
|         return storage<0>(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the storage for a given index. | ||||
|      * @brief Returns the storage for a given index, if any. | ||||
|      * @tparam Index Index of the storage to return. | ||||
|      * @return The storage for the given index. | ||||
|      */ | ||||
|     template<std::size_t Index> | ||||
|     [[nodiscard]] decltype(auto) storage() const noexcept { | ||||
|         return *std::get<Index>(pools); | ||||
|     [[nodiscard]] auto *storage() const noexcept { | ||||
|         return std::get<Index>(pools); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns a storage to a view. | ||||
|      * @param elem A storage to assign to the view. | ||||
|      */ | ||||
|     void storage(Get &elem) noexcept { | ||||
|         storage<0>(elem); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Assigns a storage to a view. | ||||
|      * @tparam Index Index of the storage to assign to the view. | ||||
|      * @param elem A storage to assign to the view. | ||||
|      */ | ||||
|     template<std::size_t Index> | ||||
|     void storage(Get &elem) noexcept { | ||||
|         view = std::get<Index>(pools) = &elem; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -617,7 +675,7 @@ public: | ||||
|      * @return Number of entities that have the given component. | ||||
|      */ | ||||
|     [[nodiscard]] size_type size() const noexcept { | ||||
|         return handle().size(); | ||||
|         return view ? view->size() : size_type{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -625,59 +683,47 @@ public: | ||||
|      * @return True if the view is empty, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool empty() const noexcept { | ||||
|         return handle().empty(); | ||||
|         return !view || view->empty(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the view. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the view. If the view | ||||
|      * is empty, the returned iterator will be equal to `end()`. | ||||
|      * If the view is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the view. | ||||
|      */ | ||||
|     [[nodiscard]] iterator begin() const noexcept { | ||||
|         return handle().begin(); | ||||
|         return view ? view->begin() : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the view. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the view. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the view. | ||||
|      */ | ||||
|     [[nodiscard]] iterator end() const noexcept { | ||||
|         return handle().end(); | ||||
|         return view ? view->end() : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the first entity of the reversed view. | ||||
|      * | ||||
|      * The returned iterator points to the first entity of the reversed view. If | ||||
|      * the view is empty, the returned iterator will be equal to `rend()`. | ||||
|      * If the view is empty, the returned iterator will be equal to `rend()`. | ||||
|      * | ||||
|      * @return An iterator to the first entity of the reversed view. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterator rbegin() const noexcept { | ||||
|         return handle().rbegin(); | ||||
|         return view ? view->rbegin() : reverse_iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator that is past the last entity of the reversed | ||||
|      * view. | ||||
|      * | ||||
|      * The returned iterator points to the entity following the last entity of | ||||
|      * the reversed view. Attempting to dereference the returned iterator | ||||
|      * results in undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the entity following the last entity of the | ||||
|      * reversed view. | ||||
|      */ | ||||
|     [[nodiscard]] reverse_iterator rend() const noexcept { | ||||
|         return handle().rend(); | ||||
|         return view ? view->rend() : reverse_iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -686,7 +732,7 @@ public: | ||||
|      * otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] entity_type front() const noexcept { | ||||
|         return empty() ? null : *begin(); | ||||
|         return (!view || view->empty()) ? null : *view->begin(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -695,7 +741,7 @@ public: | ||||
|      * otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] entity_type back() const noexcept { | ||||
|         return empty() ? null : *rbegin(); | ||||
|         return (!view || view->empty()) ? null : *view->rbegin(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -705,7 +751,7 @@ public: | ||||
|      * iterator otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] iterator find(const entity_type entt) const noexcept { | ||||
|         return contains(entt) ? handle().find(entt) : end(); | ||||
|         return view ? view->find(entt) : iterator{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -723,15 +769,15 @@ public: | ||||
|      * @return The component assigned to the given entity. | ||||
|      */ | ||||
|     [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { | ||||
|         return storage().get(entt); | ||||
|         return std::get<0>(pools)->get(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Checks if a view is properly initialized. | ||||
|      * @return True if the view is properly initialized, false otherwise. | ||||
|      * @brief Checks if a view is fully initialized. | ||||
|      * @return True if the view is fully initialized, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] explicit operator bool() const noexcept { | ||||
|         return std::get<0>(pools) != nullptr; | ||||
|         return (std::get<0>(pools) != nullptr); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -740,7 +786,7 @@ public: | ||||
|      * @return True if the view contains the given entity, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool contains(const entity_type entt) const noexcept { | ||||
|         return handle().contains(entt); | ||||
|         return view && view->contains(entt); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -750,24 +796,24 @@ public: | ||||
|      * Attempting to use an entity that doesn't belong to the view results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @tparam Type Type or index of the component to get. | ||||
|      * @tparam Elem Type or index of the component to get. | ||||
|      * @param entt A valid identifier. | ||||
|      * @return The component assigned to the entity. | ||||
|      */ | ||||
|     template<typename... Type> | ||||
|     template<typename Elem> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         if constexpr(sizeof...(Type) == 0) { | ||||
|             return storage().get_as_tuple(entt); | ||||
|         } else { | ||||
|             static_assert((std::is_same_v<std::remove_const_t<Type>, typename Get::value_type> && ...), "Invalid component type"); | ||||
|             return storage().get(entt); | ||||
|         } | ||||
|         static_assert(std::is_same_v<std::remove_const_t<Elem>, typename Get::value_type>, "Invalid component type"); | ||||
|         return get<0>(entt); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc get */ | ||||
|     template<std::size_t Index> | ||||
|     template<std::size_t... Elem> | ||||
|     [[nodiscard]] decltype(auto) get(const entity_type entt) const { | ||||
|         return storage().get(entt); | ||||
|         if constexpr(sizeof...(Elem) == 0) { | ||||
|             return std::get<0>(pools)->get_as_tuple(entt); | ||||
|         } else { | ||||
|             return std::get<Elem...>(pools)->get(entt); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -794,20 +840,22 @@ public: | ||||
|      */ | ||||
|     template<typename Func> | ||||
|     void each(Func func) const { | ||||
|         if(view) { | ||||
|             if constexpr(is_applicable_v<Func, decltype(*each().begin())>) { | ||||
|                 for(const auto pack: each()) { | ||||
|                     std::apply(func, pack); | ||||
|                 } | ||||
|         } else if constexpr(ignore_as_empty_v<typename Get::value_type>) { | ||||
|             } else if constexpr(Get::traits_type::page_size == 0u) { | ||||
|                 for(size_type pos{}, last = size(); pos < last; ++pos) { | ||||
|                     func(); | ||||
|                 } | ||||
|             } else { | ||||
|             for(auto &&component: storage()) { | ||||
|                 for(auto &&component: *std::get<0>(pools)) { | ||||
|                     func(component); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterable object to use to _visit_ a view. | ||||
| @@ -819,7 +867,7 @@ public: | ||||
|      * @return An iterable object to use to _visit_ the view. | ||||
|      */ | ||||
|     [[nodiscard]] iterable each() const noexcept { | ||||
|         return storage().each(); | ||||
|         return view ? std::get<0>(pools)->each() : iterable{}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -831,13 +879,16 @@ public: | ||||
|      */ | ||||
|     template<typename... OGet, typename... OExclude> | ||||
|     [[nodiscard]] auto operator|(const basic_view<get_t<OGet...>, exclude_t<OExclude...>> &other) const noexcept { | ||||
|         return std::make_from_tuple<basic_view<get_t<Get, OGet...>, exclude_t<OExclude...>>>( | ||||
|             std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter))); | ||||
|         return internal::view_pack( | ||||
|             std::tuple_cat(pools, other.pools), | ||||
|             internal::filter_as_tuple<OExclude...>(other.filter), | ||||
|             std::index_sequence_for<Get, OGet..., OExclude...>{}); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::tuple<Get *> pools; | ||||
|     std::tuple<> filter; | ||||
|     std::array<const common_type *, 0u> filter; | ||||
|     const common_type *view; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|   | ||||
							
								
								
									
										2
									
								
								external/entt/entt/src/entt/entt.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								external/entt/entt/src/entt/entt.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -24,6 +24,7 @@ | ||||
| #include "entity/group.hpp" | ||||
| #include "entity/handle.hpp" | ||||
| #include "entity/helper.hpp" | ||||
| #include "entity/mixin.hpp" | ||||
| #include "entity/observer.hpp" | ||||
| #include "entity/organizer.hpp" | ||||
| #include "entity/registry.hpp" | ||||
| @@ -31,7 +32,6 @@ | ||||
| #include "entity/snapshot.hpp" | ||||
| #include "entity/sparse_set.hpp" | ||||
| #include "entity/storage.hpp" | ||||
| #include "entity/storage_mixin.hpp" | ||||
| #include "entity/view.hpp" | ||||
| #include "graph/adjacency_matrix.hpp" | ||||
| #include "graph/dot.hpp" | ||||
|   | ||||
| @@ -258,7 +258,7 @@ public: | ||||
|     [[nodiscard]] iterable_adaptor<out_edge_iterator> out_edges(const vertex_type vertex) const noexcept { | ||||
|         const auto it = matrix.cbegin(); | ||||
|         const auto from = vertex * vert; | ||||
|         const auto to = vertex * vert + vert; | ||||
|         const auto to = from + vert; | ||||
|         return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; | ||||
|     } | ||||
|  | ||||
| @@ -270,7 +270,7 @@ public: | ||||
|     [[nodiscard]] iterable_adaptor<in_edge_iterator> in_edges(const vertex_type vertex) const noexcept { | ||||
|         const auto it = matrix.cbegin(); | ||||
|         const auto from = vertex; | ||||
|         const auto to = vert * (vert - 1u) + vertex; | ||||
|         const auto to = vert * vert + from; | ||||
|         return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										146
									
								
								external/entt/entt/src/entt/graph/flow.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										146
									
								
								external/entt/entt/src/entt/graph/flow.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -32,6 +32,7 @@ class basic_flow { | ||||
|     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<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) { | ||||
|         ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); | ||||
| @@ -43,6 +44,76 @@ class basic_flow { | ||||
|         deps[res].emplace_back(index.first(), is_rw); | ||||
|     } | ||||
|  | ||||
|     void setup_graph(adjacency_matrix_type &matrix) const { | ||||
|         for(const auto &elem: deps) { | ||||
|             const auto last = elem.second.cend(); | ||||
|             auto it = elem.second.cbegin(); | ||||
|  | ||||
|             while(it != last) { | ||||
|                 if(it->second) { | ||||
|                     // rw item | ||||
|                     if(auto curr = it++; it != last) { | ||||
|                         if(it->second) { | ||||
|                             matrix.insert(curr->first, it->first); | ||||
|                         } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { | ||||
|                             for(; it != next; ++it) { | ||||
|                                 matrix.insert(curr->first, it->first); | ||||
|                                 matrix.insert(it->first, next->first); | ||||
|                             } | ||||
|                         } else { | ||||
|                             for(; it != next; ++it) { | ||||
|                                 matrix.insert(curr->first, it->first); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     // ro item (first iteration only) | ||||
|                     if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { | ||||
|                         for(; it != next; ++it) { | ||||
|                             matrix.insert(it->first, next->first); | ||||
|                         } | ||||
|                     } else { | ||||
|                         it = last; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void transitive_closure(adjacency_matrix_type &matrix) const { | ||||
|         const auto length = matrix.size(); | ||||
|  | ||||
|         for(std::size_t vk{}; vk < length; ++vk) { | ||||
|             for(std::size_t vi{}; vi < length; ++vi) { | ||||
|                 for(std::size_t vj{}; vj < length; ++vj) { | ||||
|                     if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { | ||||
|                         matrix.insert(vi, vj); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void transitive_reduction(adjacency_matrix_type &matrix) const { | ||||
|         const auto length = matrix.size(); | ||||
|  | ||||
|         for(std::size_t vert{}; vert < length; ++vert) { | ||||
|             matrix.erase(vert, vert); | ||||
|         } | ||||
|  | ||||
|         for(std::size_t vj{}; vj < length; ++vj) { | ||||
|             for(std::size_t vi{}; vi < length; ++vi) { | ||||
|                 if(matrix.contains(vi, vj)) { | ||||
|                     for(std::size_t vk{}; vk < length; ++vk) { | ||||
|                         if(matrix.contains(vj, vk)) { | ||||
|                             matrix.erase(vi, vk); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     /*! @brief Allocator type. */ | ||||
|     using allocator_type = Allocator; | ||||
| @@ -50,6 +121,8 @@ public: | ||||
|     using size_type = std::size_t; | ||||
|     /*! @brief Iterable task list. */ | ||||
|     using iterable = iterable_adaptor<typename task_container_type::const_iterator>; | ||||
|     /*! @brief Adjacency matrix type. */ | ||||
|     using graph_type = adjacency_matrix_type; | ||||
|  | ||||
|     /*! @brief Default constructor. */ | ||||
|     basic_flow() | ||||
| @@ -124,9 +197,10 @@ public: | ||||
|  | ||||
|     /*! @brief Clears the flow builder. */ | ||||
|     void clear() noexcept { | ||||
|         index.first() = sync_on = {}; | ||||
|         index.first() = {}; | ||||
|         vertices.clear(); | ||||
|         deps.clear(); | ||||
|         sync_on = {}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -245,72 +319,12 @@ public: | ||||
|      * @brief Generates a task graph for the current content. | ||||
|      * @return The adjacency matrix of the task graph. | ||||
|      */ | ||||
|     [[nodiscard]] adjacency_matrix<directed_tag> graph() const { | ||||
|         const auto length = vertices.size(); | ||||
|         adjacency_matrix<directed_tag> matrix{length}; | ||||
|     [[nodiscard]] graph_type graph() const { | ||||
|         graph_type matrix{vertices.size(), get_allocator()}; | ||||
|  | ||||
|         // creates the adjacency matrix | ||||
|         for(const auto &elem: deps) { | ||||
|             const auto last = elem.second.cend(); | ||||
|             auto it = elem.second.cbegin(); | ||||
|  | ||||
|             while(it != last) { | ||||
|                 if(it->second) { | ||||
|                     // rw item | ||||
|                     if(auto curr = it++; it != last) { | ||||
|                         if(it->second) { | ||||
|                             matrix.insert(curr->first, it->first); | ||||
|                         } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { | ||||
|                             for(; it != next; ++it) { | ||||
|                                 matrix.insert(curr->first, it->first); | ||||
|                                 matrix.insert(it->first, next->first); | ||||
|                             } | ||||
|                         } else { | ||||
|                             for(; it != next; ++it) { | ||||
|                                 matrix.insert(curr->first, it->first); | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } else { | ||||
|                     // ro item (first iteration only) | ||||
|                     if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { | ||||
|                         for(; it != next; ++it) { | ||||
|                             matrix.insert(it->first, next->first); | ||||
|                         } | ||||
|                     } else { | ||||
|                         it = last; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // computes the transitive closure | ||||
|         for(std::size_t vk{}; vk < length; ++vk) { | ||||
|             for(std::size_t vi{}; vi < length; ++vi) { | ||||
|                 for(std::size_t vj{}; vj < length; ++vj) { | ||||
|                     if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { | ||||
|                         matrix.insert(vi, vj); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // applies the transitive reduction | ||||
|         for(std::size_t vert{}; vert < length; ++vert) { | ||||
|             matrix.erase(vert, vert); | ||||
|         } | ||||
|  | ||||
|         for(std::size_t vj{}; vj < length; ++vj) { | ||||
|             for(std::size_t vi{}; vi < length; ++vi) { | ||||
|                 if(matrix.contains(vi, vj)) { | ||||
|                     for(std::size_t vk{}; vk < length; ++vk) { | ||||
|                         if(matrix.contains(vj, vk)) { | ||||
|                             matrix.erase(vi, vk); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         setup_graph(matrix); | ||||
|         transitive_closure(matrix); | ||||
|         transitive_reduction(matrix); | ||||
|  | ||||
|         return matrix; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										32
									
								
								external/entt/entt/src/entt/locator/locator.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										32
									
								
								external/entt/entt/src/entt/locator/locator.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -70,40 +70,40 @@ public: | ||||
|      * cases, they are discarded. | ||||
|      * | ||||
|      * @tparam Args Types of arguments to use to construct the fallback service. | ||||
|      * @tparam Impl Fallback service type. | ||||
|      * @tparam Type Fallback service type. | ||||
|      * @param args Parameters to use to construct the fallback service. | ||||
|      * @return A reference to a valid service. | ||||
|      */ | ||||
|     template<typename Impl = Service, typename... Args> | ||||
|     template<typename Type = Service, typename... Args> | ||||
|     [[nodiscard]] static Service &value_or(Args &&...args) { | ||||
|         return service ? *service : emplace<Impl>(std::forward<Args>(args)...); | ||||
|         return service ? *service : emplace<Type>(std::forward<Args>(args)...); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sets or replaces a service. | ||||
|      * @tparam Impl Service type. | ||||
|      * @tparam Type Service type. | ||||
|      * @tparam Args Types of arguments to use to construct the service. | ||||
|      * @param args Parameters to use to construct the service. | ||||
|      * @return A reference to a valid service. | ||||
|      */ | ||||
|     template<typename Impl = Service, typename... Args> | ||||
|     template<typename Type = Service, typename... Args> | ||||
|     static Service &emplace(Args &&...args) { | ||||
|         service = std::make_shared<Impl>(std::forward<Args>(args)...); | ||||
|         service = std::make_shared<Type>(std::forward<Args>(args)...); | ||||
|         return *service; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Sets or replaces a service using a given allocator. | ||||
|      * @tparam Impl Service type. | ||||
|      * @tparam Type Service type. | ||||
|      * @tparam Allocator Type of allocator used to manage memory and elements. | ||||
|      * @tparam Args Types of arguments to use to construct the service. | ||||
|      * @param alloc The allocator to use. | ||||
|      * @param args Parameters to use to construct the service. | ||||
|      * @return A reference to a valid service. | ||||
|      */ | ||||
|     template<typename Impl = Service, typename Allocator, typename... Args> | ||||
|     static Service &allocate_emplace(Allocator alloc, Args &&...args) { | ||||
|         service = std::allocate_shared<Impl>(alloc, std::forward<Args>(args)...); | ||||
|     template<typename Type = Service, typename Allocator, typename... Args> | ||||
|     static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { | ||||
|         service = std::allocate_shared<Type>(alloc, std::forward<Args>(args)...); | ||||
|         return *service; | ||||
|     } | ||||
|  | ||||
| @@ -125,6 +125,18 @@ public: | ||||
|         service = other.value; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Resets or replaces a service. | ||||
|      * @tparam Type Service type. | ||||
|      * @tparam Deleter Deleter type. | ||||
|      * @param elem A pointer to a service to manage. | ||||
|      * @param deleter A deleter to use to destroy the service. | ||||
|      */ | ||||
|     template<typename Type, typename Deleter = std::default_delete<Type>> | ||||
|     static void reset(Type *elem, Deleter deleter = {}) { | ||||
|         service = std::shared_ptr<Service>{elem, std::move(deleter)}; | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     // std::shared_ptr because of its type erased allocator which is useful here | ||||
|     inline static std::shared_ptr<Service> service{}; | ||||
|   | ||||
							
								
								
									
										8
									
								
								external/entt/entt/src/entt/meta/context.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								external/entt/entt/src/entt/meta/context.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -21,8 +21,8 @@ struct meta_type_node; | ||||
| struct meta_context { | ||||
|     dense_map<id_type, meta_type_node, identity> value{}; | ||||
|  | ||||
|     static inline meta_context &from(meta_ctx &ctx); | ||||
|     static inline 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 | ||||
| @@ -49,11 +49,11 @@ class meta_ctx: private internal::meta_context { | ||||
|  * Internal details not to be documented. | ||||
|  */ | ||||
|  | ||||
| inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { | ||||
| [[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { | ||||
|     return ctx; | ||||
| } | ||||
|  | ||||
| inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { | ||||
| [[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { | ||||
|     return ctx; | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										119
									
								
								external/entt/entt/src/entt/meta/factory.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										119
									
								
								external/entt/entt/src/entt/meta/factory.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -29,28 +29,12 @@ namespace entt { | ||||
|  | ||||
| namespace internal { | ||||
|  | ||||
| inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) { | ||||
| [[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()]; | ||||
| } | ||||
|  | ||||
| inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) { | ||||
|     return parent.details->base.insert_or_assign(id, std::move(node)).first->second; | ||||
| } | ||||
|  | ||||
| inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) { | ||||
|     return parent.details->conv.insert_or_assign(id, std::move(node)).first->second; | ||||
| } | ||||
|  | ||||
| inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) { | ||||
|     return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second; | ||||
| } | ||||
|  | ||||
| inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) { | ||||
|     return (parent.dtor = std::move(node)); | ||||
| } | ||||
|  | ||||
| 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; | ||||
| } | ||||
| @@ -72,10 +56,6 @@ inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_ty | ||||
|     return parent.details->func.insert_or_assign(id, std::move(node)).first->second; | ||||
| } | ||||
|  | ||||
| inline meta_prop_node &meta_extend(dense_map<id_type, meta_prop_node, identity> &prop, const id_type id, meta_prop_node node) { | ||||
|     return (prop[id] = std::move(node)); | ||||
| } | ||||
|  | ||||
| } // namespace internal | ||||
|  | ||||
| /** | ||||
| @@ -156,16 +136,8 @@ public: | ||||
|     template<typename Base> | ||||
|     auto base() noexcept { | ||||
|         static_assert(!std::is_same_v<Type, Base> && std::is_base_of_v<Base, Type>, "Invalid base type"); | ||||
|  | ||||
|         internal::meta_extend( | ||||
|             internal::owner(*ctx, *info), | ||||
|             type_id<Base>().hash(), | ||||
|             internal::meta_base_node{ | ||||
|                 &internal::resolve<Base>, | ||||
|                 +[](const void *instance) noexcept { | ||||
|                     return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); | ||||
|                 }}); | ||||
|  | ||||
|         auto *const op = +[](const void *instance) noexcept { return static_cast<const void *>(static_cast<const Base *>(static_cast<const Type *>(instance))); }; | ||||
|         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; | ||||
|     } | ||||
| @@ -185,15 +157,8 @@ public: | ||||
|     template<auto Candidate> | ||||
|     auto conv() noexcept { | ||||
|         using conv_type = std::remove_cv_t<std::remove_reference_t<std::invoke_result_t<decltype(Candidate), Type &>>>; | ||||
|  | ||||
|         internal::meta_extend( | ||||
|             internal::owner(*ctx, *info), | ||||
|             type_id<conv_type>().hash(), | ||||
|             internal::meta_conv_node{ | ||||
|                 +[](const meta_ctx &area, const void *instance) { | ||||
|                     return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); | ||||
|                 }}); | ||||
|  | ||||
|         auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast<const Type *>(instance))); }; | ||||
|         internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op}); | ||||
|         bucket = nullptr; | ||||
|         return *this; | ||||
|     } | ||||
| @@ -210,15 +175,8 @@ public: | ||||
|     template<typename To> | ||||
|     auto conv() noexcept { | ||||
|         using conv_type = std::remove_cv_t<std::remove_reference_t<To>>; | ||||
|  | ||||
|         internal::meta_extend( | ||||
|             internal::owner(*ctx, *info), | ||||
|             type_id<conv_type>().hash(), | ||||
|             internal::meta_conv_node{ | ||||
|                 +[](const meta_ctx &area, const void *instance) { | ||||
|                     return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); | ||||
|                 }}); | ||||
|  | ||||
|         auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast<To>(*static_cast<const Type *>(instance))); }; | ||||
|         internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id<conv_type>().hash(), internal::meta_conv_node{op}); | ||||
|         bucket = nullptr; | ||||
|         return *this; | ||||
|     } | ||||
| @@ -241,15 +199,7 @@ public: | ||||
|         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"); | ||||
|  | ||||
|         internal::meta_extend( | ||||
|             internal::owner(*ctx, *info), | ||||
|             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>}); | ||||
|  | ||||
|         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; | ||||
|     } | ||||
| @@ -269,14 +219,7 @@ public: | ||||
|         // default constructor is already implicitly generated, no need for redundancy | ||||
|         if constexpr(sizeof...(Args) != 0u) { | ||||
|             using descriptor = meta_function_helper_t<Type, Type (*)(Args...)>; | ||||
|  | ||||
|             internal::meta_extend( | ||||
|                 internal::owner(*ctx, *info), | ||||
|                 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...>}); | ||||
|             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; | ||||
| @@ -304,12 +247,8 @@ public: | ||||
|     template<auto Func> | ||||
|     auto dtor() noexcept { | ||||
|         static_assert(std::is_invocable_v<decltype(Func), Type &>, "The function doesn't accept an object of the type provided"); | ||||
|  | ||||
|         internal::meta_extend( | ||||
|             internal::owner(*ctx, *info), | ||||
|             internal::meta_dtor_node{ | ||||
|                 +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }}); | ||||
|  | ||||
|         auto *const op = +[](void *instance) { std::invoke(Func, *static_cast<Type *>(instance)); }; | ||||
|         internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op}; | ||||
|         bucket = nullptr; | ||||
|         return *this; | ||||
|     } | ||||
| @@ -330,32 +269,39 @@ public: | ||||
|     template<auto Data, typename Policy = as_is_t> | ||||
|     auto data(const id_type id) noexcept { | ||||
|         if constexpr(std::is_member_object_pointer_v<decltype(Data)>) { | ||||
|             using data_type = std::remove_reference_t<std::invoke_result_t<decltype(Data), Type &>>; | ||||
|             using data_type = std::invoke_result_t<decltype(Data), Type &>; | ||||
|             static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); | ||||
|  | ||||
|             auto &&elem = internal::meta_extend( | ||||
|                 internal::owner(*ctx, *info), | ||||
|                 id, | ||||
|                 internal::meta_data_node{ | ||||
|                     /* this is never static */ | ||||
|                     std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none, | ||||
|                     std::is_const_v<std::remove_reference_t<data_type>> ? internal::meta_traits::is_const : internal::meta_traits::is_none, | ||||
|                     1u, | ||||
|                     &internal::resolve<std::remove_cv_t<data_type>>, | ||||
|                     &meta_arg<type_list<std::remove_cv_t<data_type>>>, | ||||
|                     &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; | ||||
|         } else { | ||||
|             using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>; | ||||
|             using data_type = std::remove_pointer_t<decltype(Data)>; | ||||
|  | ||||
|             if constexpr(std::is_pointer_v<decltype(Data)>) { | ||||
|                 static_assert(Policy::template value<decltype(*Data)>, "Invalid return type for the given policy"); | ||||
|             } else { | ||||
|                 static_assert(Policy::template value<data_type>, "Invalid return type for the given policy"); | ||||
|             } | ||||
|  | ||||
|             auto &&elem = internal::meta_extend( | ||||
|                 internal::owner(*ctx, *info), | ||||
|                 id, | ||||
|                 internal::meta_data_node{ | ||||
|                     ((std::is_same_v<Type, std::remove_cv_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, | ||||
|                     ((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<data_type>>, | ||||
|                     &meta_arg<type_list<std::remove_cv_t<data_type>>>, | ||||
|                     &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>}); | ||||
|  | ||||
| @@ -495,18 +441,11 @@ public: | ||||
|         ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties"); | ||||
|  | ||||
|         if constexpr(sizeof...(Value) == 0u) { | ||||
|             internal::meta_extend( | ||||
|                 *bucket, | ||||
|                 id, | ||||
|                 internal::meta_prop_node{ | ||||
|                     &internal::resolve<void>}); | ||||
|             (*bucket)[id] = internal::meta_prop_node{&internal::resolve<void>}; | ||||
|         } else { | ||||
|             internal::meta_extend( | ||||
|                 *bucket, | ||||
|                 id, | ||||
|                 internal::meta_prop_node{ | ||||
|             (*bucket)[id] = internal::meta_prop_node{ | ||||
|                 &internal::resolve<std::decay_t<Value>>..., | ||||
|                     std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...}); | ||||
|                 std::make_shared<std::decay_t<Value>>(std::forward<Value>(value))...}; | ||||
|         } | ||||
|  | ||||
|         return *this; | ||||
|   | ||||
							
								
								
									
										113
									
								
								external/entt/entt/src/entt/meta/meta.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										113
									
								
								external/entt/entt/src/entt/meta/meta.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -613,7 +613,7 @@ private: | ||||
|  * @return A properly initialized and not necessarily owning wrapper. | ||||
|  */ | ||||
| template<typename Type> | ||||
| meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { | ||||
| [[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { | ||||
|     return meta_any{ctx, std::in_place_type<Type &&>, std::forward<Type>(value)}; | ||||
| } | ||||
|  | ||||
| @@ -624,7 +624,7 @@ meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { | ||||
|  * @return A properly initialized and not necessarily owning wrapper. | ||||
|  */ | ||||
| template<typename Type> | ||||
| meta_any forward_as_meta(Type &&value) { | ||||
| [[nodiscard]] meta_any forward_as_meta(Type &&value) { | ||||
|     return forward_as_meta(locator<meta_ctx>::value_or(), std::forward<Type>(value)); | ||||
| } | ||||
|  | ||||
| @@ -722,6 +722,16 @@ struct meta_handle { | ||||
|         return static_cast<bool>(any); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc meta_any::operator== */ | ||||
|     [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { | ||||
|         return (any == other.any); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc meta_any::operator!= */ | ||||
|     [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { | ||||
|         return !(*this == other); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Access operator for accessing the contained opaque object. | ||||
|      * @return A wrapper that shares a reference to an unmanaged object. | ||||
| @@ -756,13 +766,21 @@ struct meta_prop { | ||||
|           ctx{&area} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the stored value by copy. | ||||
|      * @brief Returns the stored value by const reference. | ||||
|      * @return A wrapper containing the value stored with the property. | ||||
|      */ | ||||
|     [[nodiscard]] meta_any value() const { | ||||
|         return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the stored value by reference. | ||||
|      * @return A wrapper containing the value stored with the property. | ||||
|      */ | ||||
|     [[nodiscard]] meta_any value() { | ||||
|         return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns true if an object is valid, false otherwise. | ||||
|      * @return True if the object is valid, false otherwise. | ||||
| @@ -771,11 +789,30 @@ struct meta_prop { | ||||
|         return (node != nullptr); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Checks if two objects refer to the same type. | ||||
|      * @param other The object with which to compare. | ||||
|      * @return True if the objects refer to the same type, false otherwise. | ||||
|      */ | ||||
|     [[nodiscard]] bool operator==(const meta_prop &other) const noexcept { | ||||
|         return (ctx == other.ctx && node == other.node); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     const internal::meta_prop_node *node; | ||||
|     const meta_ctx *ctx; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Checks if two objects refer to the same type. | ||||
|  * @param lhs An object, either valid or not. | ||||
|  * @param rhs An object, either valid or not. | ||||
|  * @return False if the objects refer to the same node, true otherwise. | ||||
|  */ | ||||
| [[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| /*! @brief Opaque wrapper for data members. */ | ||||
| struct meta_data { | ||||
|     /*! @brief Unsigned integer type. */ | ||||
| @@ -876,11 +913,26 @@ struct meta_data { | ||||
|         return (node != nullptr); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc meta_prop::operator== */ | ||||
|     [[nodiscard]] bool operator==(const meta_data &other) const noexcept { | ||||
|         return (ctx == other.ctx && node == other.node); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     const internal::meta_data_node *node; | ||||
|     const meta_ctx *ctx; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Checks if two objects refer to the same type. | ||||
|  * @param lhs An object, either valid or not. | ||||
|  * @param rhs An object, either valid or not. | ||||
|  * @return False if the objects refer to the same node, true otherwise. | ||||
|  */ | ||||
| [[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| /*! @brief Opaque wrapper for member functions. */ | ||||
| struct meta_func { | ||||
|     /*! @brief Unsigned integer type. */ | ||||
| @@ -962,8 +1014,12 @@ struct meta_func { | ||||
|      */ | ||||
|     template<typename... Args> | ||||
|     meta_any invoke(meta_handle instance, Args &&...args) const { | ||||
|         meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward<Args>(args)}...}; | ||||
|         return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); | ||||
|         if constexpr(sizeof...(Args) == 0u) { | ||||
|             return invoke(std::move(instance), static_cast<meta_any *>(nullptr), size_type{}); | ||||
|         } else { | ||||
|             meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...}; | ||||
|             return invoke(std::move(instance), arguments, sizeof...(Args)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc meta_data::prop */ | ||||
| @@ -997,11 +1053,26 @@ struct meta_func { | ||||
|         return (node != nullptr); | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc meta_prop::operator== */ | ||||
|     [[nodiscard]] bool operator==(const meta_func &other) const noexcept { | ||||
|         return (ctx == other.ctx && node == other.node); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     const internal::meta_func_node *node; | ||||
|     const meta_ctx *ctx; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * @brief Checks if two objects refer to the same type. | ||||
|  * @param lhs An object, either valid or not. | ||||
|  * @param rhs An object, either valid or not. | ||||
|  * @return False if the objects refer to the same node, true otherwise. | ||||
|  */ | ||||
| [[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| /*! @brief Opaque wrapper for types. */ | ||||
| class meta_type { | ||||
|     template<typename Func> | ||||
| @@ -1339,21 +1410,25 @@ public: | ||||
|      */ | ||||
|     template<typename... Args> | ||||
|     [[nodiscard]] meta_any construct(Args &&...args) const { | ||||
|         meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward<Args>(args)}...}; | ||||
|         if constexpr(sizeof...(Args) == 0u) { | ||||
|             return construct(static_cast<meta_any *>(nullptr), size_type{}); | ||||
|         } else { | ||||
|             meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...}; | ||||
|             return construct(arguments, sizeof...(Args)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Wraps an opaque element of the underlying type. | ||||
|      * @param element A valid pointer to an element of the underlying type. | ||||
|      * @return A wrapper that references the given instance. | ||||
|      */ | ||||
|     meta_any from_void(void *element) const { | ||||
|     [[nodiscard]] meta_any from_void(void *element) const { | ||||
|         return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; | ||||
|     } | ||||
|  | ||||
|     /*! @copydoc from_void */ | ||||
|     meta_any from_void(const void *element) const { | ||||
|     [[nodiscard]] meta_any from_void(const void *element) const { | ||||
|         return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; | ||||
|     } | ||||
|  | ||||
| @@ -1373,7 +1448,7 @@ public: | ||||
|     meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { | ||||
|         if(node.details) { | ||||
|             if(auto it = node.details->func.find(id); it != node.details->func.cend()) { | ||||
|                 if(const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { | ||||
|                 if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { | ||||
|                     return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); | ||||
|                 } | ||||
|             } | ||||
| @@ -1399,8 +1474,12 @@ public: | ||||
|      */ | ||||
|     template<typename... Args> | ||||
|     meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { | ||||
|         meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward<Args>(args)}...}; | ||||
|         return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); | ||||
|         if constexpr(sizeof...(Args) == 0u) { | ||||
|             return invoke(id, std::move(instance), static_cast<meta_any *>(nullptr), size_type{}); | ||||
|         } else { | ||||
|             meta_any arguments[sizeof...(Args)]{{*ctx, std::forward<Args>(args)}...}; | ||||
|             return invoke(id, std::move(instance), arguments, sizeof...(Args)); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -1466,11 +1545,7 @@ public: | ||||
|         return !(ctx == nullptr); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Checks if two objects refer to the same type. | ||||
|      * @param other The object with which to compare. | ||||
|      * @return True if the objects refer to the same type, false otherwise. | ||||
|      */ | ||||
|     /*! @copydoc meta_prop::operator== */ | ||||
|     [[nodiscard]] bool operator==(const meta_type &other) const noexcept { | ||||
|         return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); | ||||
|     } | ||||
| @@ -1622,7 +1697,7 @@ public: | ||||
|     explicit meta_iterator(const meta_ctx &area, It iter) noexcept | ||||
|         : ctx{&area}, | ||||
|           vtable{&basic_vtable<It>}, | ||||
|           handle{std::move(iter)} {} | ||||
|           handle{iter} {} | ||||
|  | ||||
|     meta_iterator &operator++() noexcept { | ||||
|         vtable(operation::incr, handle, 1, nullptr); | ||||
| @@ -1716,7 +1791,7 @@ public: | ||||
|     meta_iterator(const meta_ctx &area, std::integral_constant<bool, KeyOnly>, It iter) noexcept | ||||
|         : ctx{&area}, | ||||
|           vtable{&basic_vtable<KeyOnly, It>}, | ||||
|           handle{std::move(iter)} {} | ||||
|           handle{iter} {} | ||||
|  | ||||
|     meta_iterator &operator++() noexcept { | ||||
|         vtable(operation::incr, handle, nullptr); | ||||
| @@ -1826,7 +1901,7 @@ inline meta_sequence_container::iterator meta_sequence_container::insert(iterato | ||||
|  * @return A possibly invalid iterator following the last removed element. | ||||
|  */ | ||||
| inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { | ||||
|     return insert(std::move(it), {}); | ||||
|     return insert(it, {}); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   | ||||
							
								
								
									
										6
									
								
								external/entt/entt/src/entt/meta/policy.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								external/entt/entt/src/entt/meta/policy.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -68,11 +68,7 @@ struct as_void_t final { | ||||
|  */ | ||||
| template<typename Type> | ||||
| struct is_meta_policy | ||||
|     : std::disjunction< | ||||
|           std::is_same<Type, as_ref_t>, | ||||
|           std::is_same<Type, as_cref_t>, | ||||
|           std::is_same<Type, as_is_t>, | ||||
|           std::is_same<Type, as_void_t>> {}; | ||||
|     : std::bool_constant<std::is_same_v<Type, as_ref_t> || std::is_same_v<Type, as_cref_t> || std::is_same_v<Type, as_is_t> || std::is_same_v<Type, as_void_t>> {}; | ||||
|  | ||||
| /** | ||||
|  * @brief Helper variable template. | ||||
|   | ||||
							
								
								
									
										8
									
								
								external/entt/entt/src/entt/meta/range.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								external/entt/entt/src/entt/meta/range.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -25,19 +25,19 @@ struct meta_range_iterator final { | ||||
|     using reference = value_type; | ||||
|     using iterator_category = std::input_iterator_tag; | ||||
|  | ||||
|     meta_range_iterator() noexcept | ||||
|     constexpr meta_range_iterator() noexcept | ||||
|         : it{}, | ||||
|           ctx{} {} | ||||
|  | ||||
|     meta_range_iterator(const meta_ctx &area, const It iter) noexcept | ||||
|     constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept | ||||
|         : it{iter}, | ||||
|           ctx{&area} {} | ||||
|  | ||||
|     meta_range_iterator &operator++() noexcept { | ||||
|     constexpr meta_range_iterator &operator++() noexcept { | ||||
|         return ++it, *this; | ||||
|     } | ||||
|  | ||||
|     meta_range_iterator operator++(int) noexcept { | ||||
|     constexpr meta_range_iterator operator++(int) noexcept { | ||||
|         meta_range_iterator orig = *this; | ||||
|         return ++(*this), orig; | ||||
|     } | ||||
|   | ||||
| @@ -30,7 +30,6 @@ struct meta_associative_container_traits; | ||||
| /** | ||||
|  * @brief Provides the member constant `value` to true if a given type is a | ||||
|  * pointer-like type from the point of view of the meta system, false otherwise. | ||||
|  * @tparam Type Potentially pointer-like type. | ||||
|  */ | ||||
| template<typename> | ||||
| struct is_meta_pointer_like: std::false_type {}; | ||||
|   | ||||
							
								
								
									
										13
									
								
								external/entt/entt/src/entt/meta/utility.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										13
									
								
								external/entt/entt/src/entt/meta/utility.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -92,9 +92,12 @@ template<typename Type, typename Ret, typename MaybeType, typename... Args> | ||||
| struct meta_function_descriptor<Type, Ret (*)(MaybeType, Args...)> | ||||
|     : meta_function_descriptor_traits< | ||||
|           Ret, | ||||
|           std::conditional_t<std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, type_list<Args...>, type_list<MaybeType, Args...>>, | ||||
|           !std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, | ||||
|           std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> && std::is_const_v<std::remove_reference_t<MaybeType>>> {}; | ||||
|           std::conditional_t< | ||||
|               std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>, | ||||
|               type_list<Args...>, | ||||
|               type_list<MaybeType, Args...>>, | ||||
|           !(std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>), | ||||
|           std::is_const_v<std::remove_reference_t<MaybeType>> && (std::is_same_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type> || std::is_base_of_v<std::remove_cv_t<std::remove_reference_t<MaybeType>>, Type>)> {}; | ||||
|  | ||||
| /** | ||||
|  * @brief Meta function descriptor. | ||||
| @@ -162,7 +165,7 @@ using meta_function_helper_t = typename meta_function_helper<Type, Candidate>::t | ||||
|  * @return A meta any containing the returned value, if any. | ||||
|  */ | ||||
| template<typename Policy = as_is_t, typename Type> | ||||
| std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { | ||||
| [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { | ||||
|     if constexpr(std::is_same_v<Policy, as_void_t>) { | ||||
|         return meta_any{ctx, std::in_place_type<void>}; | ||||
|     } else if constexpr(std::is_same_v<Policy, as_ref_t>) { | ||||
| @@ -183,7 +186,7 @@ std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(const meta_ct | ||||
|  * @return A meta any containing the returned value, if any. | ||||
|  */ | ||||
| template<typename Policy = as_is_t, typename Type> | ||||
| std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) { | ||||
| [[nodiscard]] std::enable_if_t<is_meta_policy_v<Policy>, meta_any> meta_dispatch(Type &&value) { | ||||
|     return meta_dispatch<Policy, Type>(locator<meta_ctx>::value_or(), std::forward<Type>(value)); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										2
									
								
								external/entt/entt/src/entt/poly/poly.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								external/entt/entt/src/entt/poly/poly.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -19,7 +19,7 @@ struct poly_inspector { | ||||
|      * @brief Generic conversion operator (definition only). | ||||
|      * @tparam Type Type to which conversion is requested. | ||||
|      */ | ||||
|     template<class Type> | ||||
|     template<typename Type> | ||||
|     operator Type &&() const; | ||||
|  | ||||
|     /** | ||||
|   | ||||
							
								
								
									
										9
									
								
								external/entt/entt/src/entt/process/fwd.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								external/entt/entt/src/entt/process/fwd.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -1,13 +1,18 @@ | ||||
| #ifndef ENTT_PROCESS_FWD_HPP | ||||
| #define ENTT_PROCESS_FWD_HPP | ||||
|  | ||||
| #include <cstdint> | ||||
|  | ||||
| namespace entt { | ||||
|  | ||||
| template<typename, typename> | ||||
| class process; | ||||
|  | ||||
| template<typename> | ||||
| class scheduler; | ||||
| template<typename = std::uint32_t> | ||||
| class basic_scheduler; | ||||
|  | ||||
| /*! @brief Alias declaration for the most common use case. */ | ||||
| using scheduler = basic_scheduler<>; | ||||
|  | ||||
| } // namespace entt | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| #include "fwd.hpp" | ||||
| #include "process.hpp" | ||||
|  | ||||
| namespace entt { | ||||
| @@ -38,11 +39,11 @@ namespace entt { | ||||
|  * @tparam Delta Type to use to provide elapsed time. | ||||
|  */ | ||||
| template<typename Delta> | ||||
| class scheduler { | ||||
| class basic_scheduler { | ||||
|     struct process_handler { | ||||
|         using instance_type = std::unique_ptr<void, void (*)(void *)>; | ||||
|         using update_fn_type = bool(scheduler &, std::size_t, Delta, void *); | ||||
|         using abort_fn_type = void(scheduler &, std::size_t, bool); | ||||
|         using update_fn_type = bool(basic_scheduler &, std::size_t, Delta, void *); | ||||
|         using abort_fn_type = void(basic_scheduler &, std::size_t, bool); | ||||
|         using next_type = std::unique_ptr<process_handler>; | ||||
|  | ||||
|         instance_type instance; | ||||
| @@ -58,8 +59,8 @@ class scheduler { | ||||
|         template<typename Proc, typename... Args> | ||||
|         continuation then(Args &&...args) { | ||||
|             static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type"); | ||||
|             auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>}; | ||||
|             handler->next.reset(new process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr}); | ||||
|             auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &basic_scheduler::deleter<Proc>}; | ||||
|             handler->next.reset(new process_handler{std::move(proc), &basic_scheduler::update<Proc>, &basic_scheduler::abort<Proc>, nullptr}); | ||||
|             handler = handler->next.get(); | ||||
|             return *this; | ||||
|         } | ||||
| @@ -74,7 +75,7 @@ class scheduler { | ||||
|     }; | ||||
|  | ||||
|     template<typename Proc> | ||||
|     [[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) { | ||||
|     [[nodiscard]] static bool update(basic_scheduler &owner, std::size_t pos, const Delta delta, void *data) { | ||||
|         auto *process = static_cast<Proc *>(owner.handlers[pos].instance.get()); | ||||
|         process->tick(delta, data); | ||||
|  | ||||
| @@ -94,7 +95,7 @@ class scheduler { | ||||
|     } | ||||
|  | ||||
|     template<typename Proc> | ||||
|     static void abort(scheduler &owner, std::size_t pos, const bool immediately) { | ||||
|     static void abort(basic_scheduler &owner, std::size_t pos, const bool immediately) { | ||||
|         static_cast<Proc *>(owner.handlers[pos].instance.get())->abort(immediately); | ||||
|     } | ||||
|  | ||||
| @@ -104,18 +105,20 @@ class scheduler { | ||||
|     } | ||||
|  | ||||
| public: | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using delta_type = Delta; | ||||
|     /*! @brief Unsigned integer type. */ | ||||
|     using size_type = std::size_t; | ||||
|  | ||||
|     /*! @brief Default constructor. */ | ||||
|     scheduler() | ||||
|     basic_scheduler() | ||||
|         : handlers{} {} | ||||
|  | ||||
|     /*! @brief Default move constructor. */ | ||||
|     scheduler(scheduler &&) = default; | ||||
|     basic_scheduler(basic_scheduler &&) = default; | ||||
|  | ||||
|     /*! @brief Default move assignment operator. @return This scheduler. */ | ||||
|     scheduler &operator=(scheduler &&) = default; | ||||
|     basic_scheduler &operator=(basic_scheduler &&) = default; | ||||
|  | ||||
|     /** | ||||
|      * @brief Number of processes currently scheduled. | ||||
| @@ -171,8 +174,8 @@ public: | ||||
|     template<typename Proc, typename... Args> | ||||
|     auto attach(Args &&...args) { | ||||
|         static_assert(std::is_base_of_v<process<Proc, Delta>, Proc>, "Invalid process type"); | ||||
|         auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &scheduler::deleter<Proc>}; | ||||
|         auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update<Proc>, &scheduler::abort<Proc>, nullptr}); | ||||
|         auto proc = typename process_handler::instance_type{new Proc{std::forward<Args>(args)...}, &basic_scheduler::deleter<Proc>}; | ||||
|         auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &basic_scheduler::update<Proc>, &basic_scheduler::abort<Proc>, nullptr}); | ||||
|         // forces the process to exit the uninitialized state | ||||
|         ref.update(*this, handlers.size() - 1u, {}, nullptr); | ||||
|         return continuation{&handlers.back()}; | ||||
| @@ -246,7 +249,7 @@ public: | ||||
|      * @param delta Elapsed time. | ||||
|      * @param data Optional data. | ||||
|      */ | ||||
|     void update(const Delta delta, void *data = nullptr) { | ||||
|     void update(const delta_type delta, void *data = nullptr) { | ||||
|         for(auto pos = handlers.size(); pos; --pos) { | ||||
|             const auto curr = pos - 1u; | ||||
|  | ||||
|   | ||||
							
								
								
									
										50
									
								
								external/entt/entt/src/entt/resource/cache.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										50
									
								
								external/entt/entt/src/entt/resource/cache.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -95,51 +95,51 @@ public: | ||||
|         return operator*(); | ||||
|     } | ||||
|  | ||||
|     template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
|     friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept; | ||||
|     template<typename... Lhs, typename... Rhs> | ||||
|     friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept; | ||||
|  | ||||
|     template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
|     friend constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept; | ||||
|     template<typename... Lhs, typename... Rhs> | ||||
|     friend constexpr bool operator==(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept; | ||||
|  | ||||
|     template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
|     friend constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &, const resource_cache_iterator<TRhs, IRhs> &) noexcept; | ||||
|     template<typename... Lhs, typename... Rhs> | ||||
|     friend constexpr bool operator<(const resource_cache_iterator<Lhs...> &, const resource_cache_iterator<Rhs...> &) noexcept; | ||||
|  | ||||
| private: | ||||
|     It it; | ||||
| }; | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return lhs.it - rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator==(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator==(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return lhs.it == rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator!=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator<(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator<(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return lhs.it < rhs.it; | ||||
| } | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator>(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator>(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return rhs < lhs; | ||||
| } | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator<=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return !(lhs > rhs); | ||||
| } | ||||
|  | ||||
| template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<TLhs, ILhs> &lhs, const resource_cache_iterator<TRhs, IRhs> &rhs) noexcept { | ||||
| template<typename... Lhs, typename... Rhs> | ||||
| [[nodiscard]] constexpr bool operator>=(const resource_cache_iterator<Lhs...> &lhs, const resource_cache_iterator<Rhs...> &rhs) noexcept { | ||||
|     return !(lhs < rhs); | ||||
| } | ||||
|  | ||||
| @@ -158,7 +158,7 @@ template<typename TLhs, typename ILhs, typename TRhs, typename IRhs> | ||||
|  */ | ||||
| template<typename Type, typename Loader, typename Allocator> | ||||
| class resource_cache { | ||||
|     using alloc_traits = typename std::allocator_traits<Allocator>; | ||||
|     using alloc_traits = std::allocator_traits<Allocator>; | ||||
|     static_assert(std::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type"); | ||||
|     using container_allocator = typename alloc_traits::template rebind_alloc<std::pair<const id_type, typename Loader::result_type>>; | ||||
|     using container_type = dense_map<id_type, typename Loader::result_type, identity, std::equal_to<id_type>, container_allocator>; | ||||
| @@ -241,8 +241,7 @@ public: | ||||
|     /** | ||||
|      * @brief Returns an iterator to the beginning. | ||||
|      * | ||||
|      * The returned iterator points to the first instance of the cache. If the | ||||
|      * cache is empty, the returned iterator will be equal to `end()`. | ||||
|      * If the cache is empty, the returned iterator will be equal to `end()`. | ||||
|      * | ||||
|      * @return An iterator to the first instance of the internal cache. | ||||
|      */ | ||||
| @@ -262,11 +261,6 @@ public: | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns an iterator to the end. | ||||
|      * | ||||
|      * The returned iterator points to the element following the last instance | ||||
|      * of the cache. Attempting to dereference the returned iterator results in | ||||
|      * undefined behavior. | ||||
|      * | ||||
|      * @return An iterator to the element following the last instance of the | ||||
|      * internal cache. | ||||
|      */ | ||||
|   | ||||
| @@ -163,81 +163,81 @@ private: | ||||
|  | ||||
| /** | ||||
|  * @brief Compares two handles. | ||||
|  * @tparam Res Type of resource managed by the first handle. | ||||
|  * @tparam Other Type of resource managed by the second handle. | ||||
|  * @tparam Lhs Type of resource managed by the first handle. | ||||
|  * @tparam Rhs Type of resource managed by the second handle. | ||||
|  * @param lhs A valid handle. | ||||
|  * @param rhs A valid handle. | ||||
|  * @return True if both handles refer to the same resource, false otherwise. | ||||
|  */ | ||||
| template<typename Res, typename Other> | ||||
| [[nodiscard]] bool operator==(const resource<Res> &lhs, const resource<Other> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] bool operator==(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { | ||||
|     return (std::addressof(*lhs) == std::addressof(*rhs)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Compares two handles. | ||||
|  * @tparam Res Type of resource managed by the first handle. | ||||
|  * @tparam Other Type of resource managed by the second handle. | ||||
|  * @tparam Lhs Type of resource managed by the first handle. | ||||
|  * @tparam Rhs Type of resource managed by the second handle. | ||||
|  * @param lhs A valid handle. | ||||
|  * @param rhs A valid handle. | ||||
|  * @return False if both handles refer to the same registry, true otherwise. | ||||
|  * @return False if both handles refer to the same resource, true otherwise. | ||||
|  */ | ||||
| template<typename Res, typename Other> | ||||
| [[nodiscard]] bool operator!=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] bool operator!=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { | ||||
|     return !(lhs == rhs); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Compares two handles. | ||||
|  * @tparam Res Type of resource managed by the first handle. | ||||
|  * @tparam Other Type of resource managed by the second handle. | ||||
|  * @tparam Lhs Type of resource managed by the first handle. | ||||
|  * @tparam Rhs Type of resource managed by the second handle. | ||||
|  * @param lhs A valid handle. | ||||
|  * @param rhs A valid handle. | ||||
|  * @return True if the first handle is less than the second, false otherwise. | ||||
|  */ | ||||
| template<typename Res, typename Other> | ||||
| [[nodiscard]] bool operator<(const resource<Res> &lhs, const resource<Other> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] bool operator<(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { | ||||
|     return (std::addressof(*lhs) < std::addressof(*rhs)); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Compares two handles. | ||||
|  * @tparam Res Type of resource managed by the first handle. | ||||
|  * @tparam Other Type of resource managed by the second handle. | ||||
|  * @tparam Lhs Type of resource managed by the first handle. | ||||
|  * @tparam Rhs Type of resource managed by the second handle. | ||||
|  * @param lhs A valid handle. | ||||
|  * @param rhs A valid handle. | ||||
|  * @return True if the first handle is greater than the second, false otherwise. | ||||
|  */ | ||||
| template<typename Res, typename Other> | ||||
| [[nodiscard]] bool operator>(const resource<Res> &lhs, const resource<Other> &rhs) noexcept { | ||||
|     return (std::addressof(*lhs) > std::addressof(*rhs)); | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] bool operator>(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { | ||||
|     return rhs < lhs; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Compares two handles. | ||||
|  * @tparam Res Type of resource managed by the first handle. | ||||
|  * @tparam Other Type of resource managed by the second handle. | ||||
|  * @tparam Lhs Type of resource managed by the first handle. | ||||
|  * @tparam Rhs Type of resource managed by the second handle. | ||||
|  * @param lhs A valid handle. | ||||
|  * @param rhs A valid handle. | ||||
|  * @return True if the first handle is less than or equal to the second, false | ||||
|  * otherwise. | ||||
|  */ | ||||
| template<typename Res, typename Other> | ||||
| [[nodiscard]] bool operator<=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] bool operator<=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { | ||||
|     return !(lhs > rhs); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @brief Compares two handles. | ||||
|  * @tparam Res Type of resource managed by the first handle. | ||||
|  * @tparam Other Type of resource managed by the second handle. | ||||
|  * @tparam Lhs Type of resource managed by the first handle. | ||||
|  * @tparam Rhs Type of resource managed by the second handle. | ||||
|  * @param lhs A valid handle. | ||||
|  * @param rhs A valid handle. | ||||
|  * @return True if the first handle is greater than or equal to the second, | ||||
|  * false otherwise. | ||||
|  */ | ||||
| template<typename Res, typename Other> | ||||
| [[nodiscard]] bool operator>=(const resource<Res> &lhs, const resource<Other> &rhs) noexcept { | ||||
| template<typename Lhs, typename Rhs> | ||||
| [[nodiscard]] bool operator>=(const resource<Lhs> &lhs, const resource<Rhs> &rhs) noexcept { | ||||
|     return !(lhs < rhs); | ||||
| } | ||||
|  | ||||
|   | ||||
							
								
								
									
										26
									
								
								external/entt/entt/src/entt/signal/delegate.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								external/entt/entt/src/entt/signal/delegate.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -76,7 +76,13 @@ class delegate<Ret(Args...)> { | ||||
|     [[nodiscard]] auto wrap(std::index_sequence<Index...>) noexcept { | ||||
|         return [](const void *, Args... args) -> Ret { | ||||
|             [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); | ||||
|  | ||||
|             if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), type_list_element_t<Index, type_list<Args...>>...>) { | ||||
|                 return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); | ||||
|             } else { | ||||
|                 constexpr auto offset = sizeof...(Args) - sizeof...(Index); | ||||
|                 return static_cast<Ret>(std::invoke(Candidate, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...)); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| @@ -85,7 +91,13 @@ class delegate<Ret(Args...)> { | ||||
|         return [](const void *payload, Args... args) -> Ret { | ||||
|             [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); | ||||
|             Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); | ||||
|  | ||||
|             if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type &, type_list_element_t<Index, type_list<Args...>>...>) { | ||||
|                 return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); | ||||
|             } else { | ||||
|                 constexpr auto offset = sizeof...(Args) - sizeof...(Index); | ||||
|                 return static_cast<Ret>(std::invoke(Candidate, *curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...)); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| @@ -94,7 +106,13 @@ class delegate<Ret(Args...)> { | ||||
|         return [](const void *payload, Args... args) -> Ret { | ||||
|             [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward<Args>(args)...); | ||||
|             Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(payload)); | ||||
|  | ||||
|             if constexpr(std::is_invocable_r_v<Ret, decltype(Candidate), Type *, type_list_element_t<Index, type_list<Args...>>...>) { | ||||
|                 return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index, type_list<Args...>>>(std::get<Index>(arguments))...)); | ||||
|             } else { | ||||
|                 constexpr auto offset = sizeof...(Args) - sizeof...(Index); | ||||
|                 return static_cast<Ret>(std::invoke(Candidate, curr, std::forward<type_list_element_t<Index + offset, type_list<Args...>>>(std::get<Index + offset>(arguments))...)); | ||||
|             } | ||||
|         }; | ||||
|     } | ||||
|  | ||||
| @@ -233,6 +251,14 @@ public: | ||||
|         fn = nullptr; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a pointer to the stored callable function target, if any. | ||||
|      * @return An opaque pointer to the stored callable function target. | ||||
|      */ | ||||
|     [[nodiscard]] function_type *target() const noexcept { | ||||
|         return fn; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns the instance or the payload linked to a delegate, if any. | ||||
|      * @return An opaque pointer to the underlying data. | ||||
|   | ||||
| @@ -75,7 +75,7 @@ public: | ||||
|  | ||||
|     template<typename... Args> | ||||
|     void enqueue(Args &&...args) { | ||||
|         if constexpr(std::is_aggregate_v<Type>) { | ||||
|         if constexpr(std::is_aggregate_v<Type> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<Type>)) { | ||||
|             events.push_back(Type{std::forward<Args>(args)...}); | ||||
|         } else { | ||||
|             events.emplace_back(std::forward<Args>(args)...); | ||||
| @@ -179,7 +179,9 @@ public: | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept | ||||
|         : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {} | ||||
|         : pools{container_type{std::move(other.pools.first()), allocator}, allocator} { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Move assignment operator. | ||||
| @@ -187,6 +189,8 @@ public: | ||||
|      * @return This dispatcher. | ||||
|      */ | ||||
|     basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); | ||||
|  | ||||
|         pools = std::move(other.pools); | ||||
|         return *this; | ||||
|     } | ||||
|   | ||||
| @@ -76,7 +76,9 @@ public: | ||||
|      * @param allocator The allocator to use. | ||||
|      */ | ||||
|     emitter(emitter &&other, const allocator_type &allocator) noexcept | ||||
|         : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {} | ||||
|         : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Move assignment operator. | ||||
| @@ -84,6 +86,8 @@ public: | ||||
|      * @return This dispatcher. | ||||
|      */ | ||||
|     emitter &operator=(emitter &&other) noexcept { | ||||
|         ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); | ||||
|  | ||||
|         handlers = std::move(other.handlers); | ||||
|         return *this; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										147
									
								
								external/entt/entt/src/entt/signal/sigh.hpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										147
									
								
								external/entt/entt/src/entt/signal/sigh.hpp
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +1,8 @@ | ||||
| #ifndef ENTT_SIGNAL_SIGH_HPP | ||||
| #define ENTT_SIGNAL_SIGH_HPP | ||||
|  | ||||
| #include <algorithm> | ||||
| #include <functional> | ||||
| #include <cstddef> | ||||
| #include <memory> | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| @@ -56,7 +56,8 @@ class sigh<Ret(Args...), Allocator> { | ||||
|     friend class sink<sigh<Ret(Args...), Allocator>>; | ||||
|  | ||||
|     using alloc_traits = std::allocator_traits<Allocator>; | ||||
|     using container_type = std::vector<delegate<Ret(Args...)>, typename alloc_traits::template rebind_alloc<delegate<Ret(Args...)>>>; | ||||
|     using delegate_type = delegate<Ret(Args...)>; | ||||
|     using container_type = std::vector<delegate_type, typename alloc_traits::template rebind_alloc<delegate_type>>; | ||||
|  | ||||
| public: | ||||
|     /*! @brief Allocator type. */ | ||||
| @@ -168,8 +169,8 @@ public: | ||||
|      * @param args Arguments to use to invoke listeners. | ||||
|      */ | ||||
|     void publish(Args... args) const { | ||||
|         for(auto &&call: std::as_const(calls)) { | ||||
|             call(args...); | ||||
|         for(auto pos = calls.size(); pos; --pos) { | ||||
|             calls[pos - 1u](args...); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -189,24 +190,24 @@ public: | ||||
|      */ | ||||
|     template<typename Func> | ||||
|     void collect(Func func, Args... args) const { | ||||
|         for(auto &&call: calls) { | ||||
|             if constexpr(std::is_void_v<Ret>) { | ||||
|         for(auto pos = calls.size(); pos; --pos) { | ||||
|             if constexpr(std::is_void_v<Ret> || !std::is_invocable_v<Func, Ret>) { | ||||
|                 calls[pos - 1u](args...); | ||||
|  | ||||
|                 if constexpr(std::is_invocable_r_v<bool, Func>) { | ||||
|                     call(args...); | ||||
|                     if(func()) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } else { | ||||
|                     call(args...); | ||||
|                     func(); | ||||
|                 } | ||||
|             } else { | ||||
|                 if constexpr(std::is_invocable_r_v<bool, Func, Ret>) { | ||||
|                     if(func(call(args...))) { | ||||
|                     if(func(calls[pos - 1u](args...))) { | ||||
|                         break; | ||||
|                     } | ||||
|                 } else { | ||||
|                     func(call(args...)); | ||||
|                     func(calls[pos - 1u](args...)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -358,6 +359,7 @@ private: | ||||
| template<typename Ret, typename... Args, typename Allocator> | ||||
| class sink<sigh<Ret(Args...), Allocator>> { | ||||
|     using signal_type = sigh<Ret(Args...), Allocator>; | ||||
|     using delegate_type = typename signal_type::delegate_type; | ||||
|     using difference_type = typename signal_type::container_type::difference_type; | ||||
|  | ||||
|     template<auto Candidate, typename Type> | ||||
| @@ -370,13 +372,14 @@ class sink<sigh<Ret(Args...), Allocator>> { | ||||
|         sink{*static_cast<signal_type *>(signal)}.disconnect<Candidate>(); | ||||
|     } | ||||
|  | ||||
|     auto before(delegate<Ret(Args...)> call) { | ||||
|         const auto &calls = signal->calls; | ||||
|         const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); | ||||
|  | ||||
|         sink other{*this}; | ||||
|         other.offset = calls.cend() - it; | ||||
|         return other; | ||||
|     template<typename Func> | ||||
|     void disconnect_if(Func callback) { | ||||
|         for(auto pos = signal->calls.size(); pos; --pos) { | ||||
|             if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { | ||||
|                 elem = std::move(signal->calls.back()); | ||||
|                 signal->calls.pop_back(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
| public: | ||||
| @@ -385,8 +388,7 @@ public: | ||||
|      * @param ref A valid reference to a signal object. | ||||
|      */ | ||||
|     sink(sigh<Ret(Args...), Allocator> &ref) noexcept | ||||
|         : offset{}, | ||||
|           signal{&ref} {} | ||||
|         : signal{&ref} {} | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns false if at least a listener is connected to the sink. | ||||
| @@ -396,89 +398,9 @@ public: | ||||
|         return signal->calls.empty(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink that connects before a given free function or an | ||||
|      * unbound member. | ||||
|      * @tparam Function A valid free function pointer. | ||||
|      * @return A properly initialized sink object. | ||||
|      */ | ||||
|     template<auto Function> | ||||
|     [[nodiscard]] sink before() { | ||||
|         delegate<Ret(Args...)> call{}; | ||||
|         call.template connect<Function>(); | ||||
|         return before(std::move(call)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink that connects before a free function with payload | ||||
|      * or a bound member. | ||||
|      * @tparam Candidate Member or free function to look for. | ||||
|      * @tparam Type Type of class or type of payload. | ||||
|      * @param value_or_instance A valid object that fits the purpose. | ||||
|      * @return A properly initialized sink object. | ||||
|      */ | ||||
|     template<auto Candidate, typename Type> | ||||
|     [[nodiscard]] sink before(Type &&value_or_instance) { | ||||
|         delegate<Ret(Args...)> call{}; | ||||
|         call.template connect<Candidate>(value_or_instance); | ||||
|         return before(std::move(call)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink that connects before a given instance or specific | ||||
|      * payload. | ||||
|      * @tparam Type Type of class or type of payload. | ||||
|      * @param value_or_instance A valid object that fits the purpose. | ||||
|      * @return A properly initialized sink object. | ||||
|      */ | ||||
|     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>, sink>> | ||||
|     [[nodiscard]] sink before(Type &value_or_instance) { | ||||
|         return before(&value_or_instance); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink that connects before a given instance or specific | ||||
|      * payload. | ||||
|      * @param value_or_instance A valid pointer that fits the purpose. | ||||
|      * @return A properly initialized sink object. | ||||
|      */ | ||||
|     [[nodiscard]] sink before(const void *value_or_instance) { | ||||
|         sink other{*this}; | ||||
|  | ||||
|         if(value_or_instance) { | ||||
|             const auto &calls = signal->calls; | ||||
|             const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { | ||||
|                 return delegate.data() == value_or_instance; | ||||
|             }); | ||||
|  | ||||
|             other.offset = calls.cend() - it; | ||||
|         } | ||||
|  | ||||
|         return other; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Returns a sink that connects before anything else. | ||||
|      * @return A properly initialized sink object. | ||||
|      */ | ||||
|     [[nodiscard]] sink before() { | ||||
|         sink other{*this}; | ||||
|         other.offset = signal->calls.size(); | ||||
|         return other; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Connects a free function (with or without payload), a bound or an | ||||
|      * unbound member to a signal. | ||||
|      * | ||||
|      * The signal isn't responsible for the connected object or the payload, if | ||||
|      * any. Users must guarantee that the lifetime of the instance overcomes the | ||||
|      * one of the signal. On the other side, the signal handler performs | ||||
|      * checks to avoid multiple connections for the same function.<br/> | ||||
|      * When used to connect a free function with payload, its signature must be | ||||
|      * such that the instance is the first argument before the ones used to | ||||
|      * define the signal itself. | ||||
|      * | ||||
|      * @tparam Candidate Function or member to connect to the signal. | ||||
|      * @tparam Type Type of class or type of payload, if any. | ||||
|      * @param value_or_instance A valid object that fits the purpose, if any. | ||||
| @@ -488,9 +410,9 @@ public: | ||||
|     connection connect(Type &&...value_or_instance) { | ||||
|         disconnect<Candidate>(value_or_instance...); | ||||
|  | ||||
|         delegate<Ret(Args...)> call{}; | ||||
|         delegate_type call{}; | ||||
|         call.template connect<Candidate>(value_or_instance...); | ||||
|         signal->calls.insert(signal->calls.end() - offset, std::move(call)); | ||||
|         signal->calls.push_back(std::move(call)); | ||||
|  | ||||
|         delegate<void(void *)> conn{}; | ||||
|         conn.template connect<&release<Candidate, Type...>>(value_or_instance...); | ||||
| @@ -506,21 +428,9 @@ public: | ||||
|      */ | ||||
|     template<auto Candidate, typename... Type> | ||||
|     void disconnect(Type &&...value_or_instance) { | ||||
|         auto &calls = signal->calls; | ||||
|         delegate<Ret(Args...)> call{}; | ||||
|         delegate_type call{}; | ||||
|         call.template connect<Candidate>(value_or_instance...); | ||||
|         calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @brief Disconnects free functions with payload or bound members from a | ||||
|      * signal. | ||||
|      * @tparam Type Type of class or type of payload. | ||||
|      * @param value_or_instance A valid object that fits the purpose. | ||||
|      */ | ||||
|     template<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<std::remove_pointer_t<Type>>, void>>> | ||||
|     void disconnect(Type &value_or_instance) { | ||||
|         disconnect(&value_or_instance); | ||||
|         disconnect_if([&call](const auto &elem) { return elem == call; }); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -530,9 +440,7 @@ public: | ||||
|      */ | ||||
|     void disconnect(const void *value_or_instance) { | ||||
|         if(value_or_instance) { | ||||
|             auto &calls = signal->calls; | ||||
|             auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; | ||||
|             calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); | ||||
|             disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -542,7 +450,6 @@ public: | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     difference_type offset; | ||||
|     signal_type *signal; | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										70
									
								
								external/entt/entt/test/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										70
									
								
								external/entt/entt/test/CMakeLists.txt
									
									
									
									
										vendored
									
									
								
							| @@ -51,7 +51,20 @@ function(SETUP_TARGET TARGET_NAME) | ||||
|         target_compile_options( | ||||
|             ${TARGET_NAME} | ||||
|             PRIVATE | ||||
|                 /EHsc /W1 /wd4996 /w14800 | ||||
|                 # vs2017 emits too many false positives for my tastes | ||||
|                 $<IF:$<EQUAL:${MSVC_TOOLSET_VERSION},141>, /W1, /W4> | ||||
|                 # clang-cl goes a little wrong with some warnings instead | ||||
|                 $<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">: | ||||
|                     -Wno-deprecated-declarations | ||||
|                     -Wno-ignored-qualifiers | ||||
|                     -Wno-unknown-warning-option | ||||
|                     -Wno-exceptions | ||||
|                     -Wno-unused-local-typedef | ||||
|                     -Wno-unused-private-field | ||||
|                 > | ||||
|                 # documentation diagnostic turned on for clang-cl only | ||||
|                 $<$<STREQUAL:"${CMAKE_CXX_COMPILER_ID}","Clang">:-Wdocumentation> | ||||
|                 /EHsc /wd4324 /wd4996 | ||||
|                 $<$<CONFIG:Debug>:/Od> | ||||
|                 $<$<CONFIG:Release>:/O2> | ||||
|         ) | ||||
| @@ -94,21 +107,23 @@ function(SETUP_BASIC_TEST TEST_NAME TEST_SOURCES) | ||||
|     add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) | ||||
| endfunction() | ||||
|  | ||||
| function(SETUP_LIB_TEST TEST_NAME) | ||||
|     add_library(_${TEST_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/lib.cpp) | ||||
|     SETUP_TARGET(_${TEST_NAME} ENTT_API_EXPORT) | ||||
|     SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp ENTT_API_IMPORT) | ||||
|     target_link_libraries(lib_${TEST_NAME} PRIVATE _${TEST_NAME}) | ||||
| function(SETUP_LIB_SHARED_TEST TEST_NAME SUB_PATH) | ||||
|     set(TARGET_NAME ${TEST_NAME}_${SUB_PATH}) | ||||
|     add_library(_${TARGET_NAME} SHARED $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/lib.cpp) | ||||
|     SETUP_TARGET(_${TARGET_NAME} ENTT_API_EXPORT) | ||||
|     SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp ENTT_API_IMPORT) | ||||
|     target_link_libraries(lib_${TARGET_NAME} PRIVATE _${TARGET_NAME}) | ||||
| endfunction() | ||||
|  | ||||
| function(SETUP_PLUGIN_TEST TEST_NAME) | ||||
|     add_library(_${TEST_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/plugin.cpp) | ||||
|     SETUP_TARGET(_${TEST_NAME} ${ARGVN}) | ||||
|     SETUP_BASIC_TEST(lib_${TEST_NAME} lib/${TEST_NAME}/main.cpp PLUGIN="$<TARGET_FILE:_${TEST_NAME}>" ${ARGVN}) | ||||
|     target_include_directories(_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR}) | ||||
|     target_include_directories(lib_${TEST_NAME} PRIVATE ${cr_INCLUDE_DIR}) | ||||
|     target_link_libraries(lib_${TEST_NAME} PRIVATE ${CMAKE_DL_LIBS}) | ||||
|     add_dependencies(lib_${TEST_NAME} _${TEST_NAME}) | ||||
| function(SETUP_LIB_PLUGIN_TEST TEST_NAME SUB_PATH) | ||||
|     set(TARGET_NAME ${TEST_NAME}_${SUB_PATH}) | ||||
|     add_library(_${TARGET_NAME} MODULE $<TARGET_OBJECTS:odr> lib/${TEST_NAME}/${SUB_PATH}/plugin.cpp) | ||||
|     SETUP_TARGET(_${TARGET_NAME} ${ARGVN}) | ||||
|     SETUP_BASIC_TEST(lib_${TARGET_NAME} lib/${TEST_NAME}/${SUB_PATH}/main.cpp PLUGIN="$<TARGET_FILE:_${TARGET_NAME}>" ${ARGVN}) | ||||
|     target_include_directories(_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR}) | ||||
|     target_include_directories(lib_${TARGET_NAME} PRIVATE ${cr_INCLUDE_DIR}) | ||||
|     target_link_libraries(lib_${TARGET_NAME} PRIVATE ${CMAKE_DL_LIBS}) | ||||
|     add_dependencies(lib_${TARGET_NAME} _${TARGET_NAME}) | ||||
| endfunction() | ||||
|  | ||||
| # Test benchmark | ||||
| @@ -142,19 +157,21 @@ if(ENTT_BUILD_LIB) | ||||
|         set(cr_INCLUDE_DIR ${cr_SOURCE_DIR}) | ||||
|     endif() | ||||
|  | ||||
|     SETUP_LIB_TEST(dispatcher) | ||||
|     SETUP_LIB_TEST(emitter) | ||||
|     SETUP_LIB_TEST(locator) | ||||
|     SETUP_LIB_TEST(meta) | ||||
|     SETUP_LIB_TEST(registry) | ||||
|     SETUP_LIB_SHARED_TEST(dispatcher shared) | ||||
|     SETUP_LIB_PLUGIN_TEST(dispatcher plugin) | ||||
|  | ||||
|     SETUP_PLUGIN_TEST(dispatcher_plugin) | ||||
|     SETUP_PLUGIN_TEST(emitter_plugin) | ||||
|     SETUP_PLUGIN_TEST(locator_plugin) | ||||
|     SETUP_PLUGIN_TEST(meta_plugin) | ||||
|     SETUP_PLUGIN_TEST(registry_plugin) | ||||
|     SETUP_LIB_SHARED_TEST(emitter shared) | ||||
|     SETUP_LIB_PLUGIN_TEST(emitter plugin) | ||||
|  | ||||
|     SETUP_PLUGIN_TEST(meta_plugin_std ENTT_STANDARD_CPP) | ||||
|     SETUP_LIB_SHARED_TEST(locator shared) | ||||
|     SETUP_LIB_PLUGIN_TEST(locator plugin) | ||||
|  | ||||
|     SETUP_LIB_SHARED_TEST(meta shared) | ||||
|     SETUP_LIB_PLUGIN_TEST(meta plugin) | ||||
|     SETUP_LIB_PLUGIN_TEST(meta plugin_std ENTT_STANDARD_CPP) | ||||
|  | ||||
|     SETUP_LIB_SHARED_TEST(registry shared) | ||||
|     SETUP_LIB_PLUGIN_TEST(registry plugin) | ||||
| endif() | ||||
|  | ||||
| # Test snapshot | ||||
| @@ -215,10 +232,11 @@ SETUP_BASIC_TEST(observer entt/entity/observer.cpp) | ||||
| SETUP_BASIC_TEST(organizer entt/entity/organizer.cpp) | ||||
| SETUP_BASIC_TEST(registry entt/entity/registry.cpp) | ||||
| SETUP_BASIC_TEST(runtime_view entt/entity/runtime_view.cpp) | ||||
| SETUP_BASIC_TEST(sigh_mixin entt/entity/sigh_mixin.cpp) | ||||
| SETUP_BASIC_TEST(snapshot entt/entity/snapshot.cpp) | ||||
| SETUP_BASIC_TEST(sparse_set entt/entity/sparse_set.cpp) | ||||
| SETUP_BASIC_TEST(storage entt/entity/storage.cpp) | ||||
| SETUP_BASIC_TEST(storage_mixin entt/entity/storage_mixin.cpp) | ||||
| SETUP_BASIC_TEST(storage_entity entt/entity/storage_entity.cpp) | ||||
| SETUP_BASIC_TEST(view entt/entity/view.cpp) | ||||
|  | ||||
| # Test graph | ||||
|   | ||||
							
								
								
									
										393
									
								
								external/entt/entt/test/benchmark/benchmark.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										393
									
								
								external/entt/entt/test/benchmark/benchmark.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -19,7 +19,9 @@ struct stable_position: position { | ||||
| }; | ||||
|  | ||||
| template<auto> | ||||
| struct comp { int x; }; | ||||
| struct comp { | ||||
|     int x; | ||||
| }; | ||||
|  | ||||
| struct timer final { | ||||
|     timer() | ||||
| @@ -34,16 +36,24 @@ private: | ||||
|     std::chrono::time_point<std::chrono::system_clock> start; | ||||
| }; | ||||
|  | ||||
| template<typename Func, typename... Args> | ||||
| void generic_with(Func func) { | ||||
|     timer timer; | ||||
|     func(); | ||||
|     timer.elapsed(); | ||||
| } | ||||
|  | ||||
| template<typename Iterable, typename Func> | ||||
| void generic(Iterable &&iterable, Func func) { | ||||
| void iterate_with(Iterable &&iterable, Func func) { | ||||
|     timer timer; | ||||
|     std::forward<Iterable>(iterable).each(func); | ||||
|     timer.elapsed(); | ||||
| } | ||||
|  | ||||
| template<typename Func> | ||||
| void pathological(Func func) { | ||||
| void pathological_with(Func func) { | ||||
|     entt::registry registry; | ||||
|     auto view = func(registry); | ||||
|  | ||||
|     for(std::uint64_t i = 0; i < 500000L; i++) { | ||||
|         const auto entity = registry.create(); | ||||
| @@ -54,10 +64,21 @@ void pathological(Func func) { | ||||
|  | ||||
|     for(auto i = 0; i < 10; ++i) { | ||||
|         registry.each([i = 0, ®istry](const auto entity) mutable { | ||||
|             if(!(++i % 7)) { registry.remove<position>(entity); } | ||||
|             if(!(++i % 11)) { registry.remove<velocity>(entity); } | ||||
|             if(!(++i % 13)) { registry.remove<comp<0>>(entity); } | ||||
|             if(!(++i % 17)) { registry.destroy(entity); } | ||||
|             if(!(++i % 7)) { | ||||
|                 registry.remove<position>(entity); | ||||
|             } | ||||
|  | ||||
|             if(!(++i % 11)) { | ||||
|                 registry.remove<velocity>(entity); | ||||
|             } | ||||
|  | ||||
|             if(!(++i % 13)) { | ||||
|                 registry.remove<comp<0>>(entity); | ||||
|             } | ||||
|  | ||||
|             if(!(++i % 17)) { | ||||
|                 registry.destroy(entity); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         for(std::uint64_t j = 0; j < 50000L; j++) { | ||||
| @@ -69,7 +90,7 @@ void pathological(Func func) { | ||||
|     } | ||||
|  | ||||
|     timer timer; | ||||
|     func(registry).each([](auto &...comp) { ((comp.x = {}), ...); }); | ||||
|     view.each([](auto &...comp) { ((comp.x = {}), ...); }); | ||||
|     timer.elapsed(); | ||||
| } | ||||
|  | ||||
| @@ -78,13 +99,11 @@ TEST(Benchmark, Create) { | ||||
|  | ||||
|     std::cout << "Creating 1000000 entities" << std::endl; | ||||
|  | ||||
|     timer timer; | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(std::uint64_t i = 0; i < 1000000L; i++) { | ||||
|             static_cast<void>(registry.create()); | ||||
|         } | ||||
|  | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, CreateMany) { | ||||
| @@ -93,9 +112,9 @@ TEST(Benchmark, CreateMany) { | ||||
|  | ||||
|     std::cout << "Creating 1000000 entities at once" << std::endl; | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.create(entities.begin(), entities.end()); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, CreateManyAndEmplaceComponents) { | ||||
| @@ -104,15 +123,14 @@ TEST(Benchmark, CreateManyAndEmplaceComponents) { | ||||
|  | ||||
|     std::cout << "Creating 1000000 entities at once and emplace components" << std::endl; | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.create(entities.begin(), entities.end()); | ||||
|  | ||||
|         for(const auto entity: entities) { | ||||
|             registry.emplace<position>(entity); | ||||
|             registry.emplace<velocity>(entity); | ||||
|         } | ||||
|  | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, CreateManyWithComponents) { | ||||
| @@ -121,79 +139,107 @@ TEST(Benchmark, CreateManyWithComponents) { | ||||
|  | ||||
|     std::cout << "Creating 1000000 entities at once with components" << std::endl; | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.create(entities.begin(), entities.end()); | ||||
|         registry.insert<position>(entities.begin(), entities.end()); | ||||
|         registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, Erase) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<int>(); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Erasing 1000000 components from their entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: view) { | ||||
|         registry.erase<int>(entity); | ||||
|             registry.erase<position>(entity); | ||||
|         } | ||||
|  | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, EraseMany) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<int>(); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Erasing 1000000 components from their entities at once" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.erase<int>(view.begin(), view.end()); | ||||
|     timer.elapsed(); | ||||
|     generic_with([&]() { | ||||
|         registry.erase<position>(view.begin(), view.end()); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, EraseManyMulti) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Erasing 1000000 components per type from their entities at once" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|     registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         registry.erase<position, velocity>(view.begin(), view.end()); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, Remove) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<int>(); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Removing 1000000 components from their entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: view) { | ||||
|         registry.remove<int>(entity); | ||||
|             registry.remove<position>(entity); | ||||
|         } | ||||
|  | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, RemoveMany) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<int>(); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Removing 1000000 components from their entities at once" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.remove<int>(view.begin(), view.end()); | ||||
|     timer.elapsed(); | ||||
|     generic_with([&]() { | ||||
|         registry.remove<position>(view.begin(), view.end()); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, RemoveManyMulti) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Removing 1000000 components per type from their entities at once" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|     registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         registry.remove<position, velocity>(view.begin(), view.end()); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, Clear) { | ||||
| @@ -203,11 +249,40 @@ TEST(Benchmark, Clear) { | ||||
|     std::cout << "Clearing 1000000 components from their entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.clear<int>(); | ||||
|     timer.elapsed(); | ||||
|     generic_with([&]() { | ||||
|         registry.clear<position>(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, ClearMulti) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|  | ||||
|     std::cout << "Clearing 1000000 components per type from their entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|     registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         registry.clear<position, velocity>(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, ClearStable) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|  | ||||
|     std::cout << "Clearing 1000000 stable components from their entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<stable_position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         registry.clear<stable_position>(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, Recycle) { | ||||
| @@ -217,18 +292,13 @@ TEST(Benchmark, Recycle) { | ||||
|     std::cout << "Recycling 1000000 entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.destroy(entities.begin(), entities.end()); | ||||
|  | ||||
|     registry.each([®istry](auto entity) { | ||||
|         registry.destroy(entity); | ||||
|     }); | ||||
|  | ||||
|     timer timer; | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto next = entities.size(); next; --next) { | ||||
|         static_cast<void>(registry.create()); | ||||
|             entities[next] = registry.create(); | ||||
|         } | ||||
|  | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, RecycleMany) { | ||||
| @@ -238,62 +308,121 @@ TEST(Benchmark, RecycleMany) { | ||||
|     std::cout << "Recycling 1000000 entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.destroy(entities.begin(), entities.end()); | ||||
|  | ||||
|     registry.each([®istry](auto entity) { | ||||
|         registry.destroy(entity); | ||||
|     }); | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.create(entities.begin(), entities.end()); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, Destroy) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<int>(); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Destroying 1000000 entities" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: view) { | ||||
|             registry.destroy(entity); | ||||
|         } | ||||
|  | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, DestroyMany) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<int>(); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Destroying 1000000 entities at once" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.destroy(view.begin(), view.end()); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, DestroyManyFastPath) { | ||||
| TEST(Benchmark, DestroyManyMulti) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     std::cout << "Destroying 1000000 entities at once, multiple components" << std::endl; | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|     registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         registry.destroy(view.begin(), view.end()); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, GetFromRegistry) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|  | ||||
|     std::cout << "Destroying 1000000 entities at once, fast path" << std::endl; | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: entities) { | ||||
|             registry.get<position>(entity).x = 0u; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, GetFromRegistryMulti) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<int>(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|     registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|  | ||||
|     timer timer; | ||||
|     registry.destroy(entities.begin(), entities.end()); | ||||
|     timer.elapsed(); | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: entities) { | ||||
|             registry.get<position>(entity).x = 0u; | ||||
|             registry.get<velocity>(entity).y = 0u; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, GetFromView) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<position>(); | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: entities) { | ||||
|             view.get<position>(entity).x = 0u; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, GetFromViewMulti) { | ||||
|     entt::registry registry; | ||||
|     std::vector<entt::entity> entities(1000000); | ||||
|     auto view = registry.view<position, velocity>(); | ||||
|  | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|     registry.insert<position>(entities.begin(), entities.end()); | ||||
|     registry.insert<velocity>(entities.begin(), entities.end()); | ||||
|  | ||||
|     generic_with([&]() { | ||||
|         for(auto entity: entities) { | ||||
|             view.get<position>(entity).x = 0u; | ||||
|             view.get<velocity>(entity).y = 0u; | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IterateSingleComponent1M) { | ||||
| @@ -306,22 +435,22 @@ TEST(Benchmark, IterateSingleComponent1M) { | ||||
|         registry.emplace<position>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IterateSingleComponentTombstonePolicy1M) { | ||||
| TEST(Benchmark, IterateSingleStableComponent1M) { | ||||
|     entt::registry registry; | ||||
|  | ||||
|     std::cout << "Iterating over 1000000 entities, one component, tombstone policy" << std::endl; | ||||
|     std::cout << "Iterating over 1000000 entities, one stable component" << std::endl; | ||||
|  | ||||
|     for(std::uint64_t i = 0; i < 1000000L; i++) { | ||||
|         const auto entity = registry.create(); | ||||
|         registry.emplace<stable_position>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<stable_position>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<stable_position>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -339,7 +468,7 @@ TEST(Benchmark, IterateSingleComponentRuntime1M) { | ||||
|     entt::runtime_view view{}; | ||||
|     view.iterate(registry.storage<position>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|     }); | ||||
| } | ||||
| @@ -355,15 +484,15 @@ TEST(Benchmark, IterateTwoComponents1M) { | ||||
|         registry.emplace<velocity>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IterateTombstonePolicyTwoComponentsTombstonePolicy1M) { | ||||
| TEST(Benchmark, IterateTwoStableComponents1M) { | ||||
|     entt::registry registry; | ||||
|  | ||||
|     std::cout << "Iterating over 1000000 entities, two components, tombstone policy" << std::endl; | ||||
|     std::cout << "Iterating over 1000000 entities, two stable components" << std::endl; | ||||
|  | ||||
|     for(std::uint64_t i = 0; i < 1000000L; i++) { | ||||
|         const auto entity = registry.create(); | ||||
| @@ -371,7 +500,7 @@ TEST(Benchmark, IterateTombstonePolicyTwoComponentsTombstonePolicy1M) { | ||||
|         registry.emplace<velocity>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<stable_position, velocity>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<stable_position, velocity>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -390,7 +519,7 @@ TEST(Benchmark, IterateTwoComponents1MHalf) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -409,7 +538,7 @@ TEST(Benchmark, IterateTwoComponents1MOne) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -425,7 +554,7 @@ TEST(Benchmark, IterateTwoComponentsNonOwningGroup1M) { | ||||
|         registry.emplace<velocity>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<>(entt::get<position, velocity>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<>(entt::get<position, velocity>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -441,7 +570,7 @@ TEST(Benchmark, IterateTwoComponentsFullOwningGroup1M) { | ||||
|         registry.emplace<velocity>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position, velocity>(), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position, velocity>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -457,7 +586,7 @@ TEST(Benchmark, IterateTwoComponentsPartialOwningGroup1M) { | ||||
|         registry.emplace<velocity>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position>(entt::get<velocity>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position>(entt::get<velocity>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -477,7 +606,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1M) { | ||||
|     view.iterate(registry.storage<position>()) | ||||
|         .iterate(registry.storage<velocity>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|     }); | ||||
| @@ -501,7 +630,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MHalf) { | ||||
|     view.iterate(registry.storage<position>()) | ||||
|         .iterate(registry.storage<velocity>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|     }); | ||||
| @@ -525,7 +654,7 @@ TEST(Benchmark, IterateTwoComponentsRuntime1MOne) { | ||||
|     view.iterate(registry.storage<position>()) | ||||
|         .iterate(registry.storage<velocity>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|     }); | ||||
| @@ -543,15 +672,15 @@ TEST(Benchmark, IterateThreeComponents1M) { | ||||
|         registry.emplace<comp<0>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IterateThreeComponentsTombstonePolicy1M) { | ||||
| TEST(Benchmark, IterateThreeStableComponents1M) { | ||||
|     entt::registry registry; | ||||
|  | ||||
|     std::cout << "Iterating over 1000000 entities, three components, tombstone policy" << std::endl; | ||||
|     std::cout << "Iterating over 1000000 entities, three stable components" << std::endl; | ||||
|  | ||||
|     for(std::uint64_t i = 0; i < 1000000L; i++) { | ||||
|         const auto entity = registry.create(); | ||||
| @@ -560,7 +689,7 @@ TEST(Benchmark, IterateThreeComponentsTombstonePolicy1M) { | ||||
|         registry.emplace<comp<0>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<stable_position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<stable_position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -580,7 +709,7 @@ TEST(Benchmark, IterateThreeComponents1MHalf) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -600,7 +729,7 @@ TEST(Benchmark, IterateThreeComponents1MOne) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -617,7 +746,7 @@ TEST(Benchmark, IterateThreeComponentsNonOwningGroup1M) { | ||||
|         registry.emplace<comp<0>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<>(entt::get<position, velocity, comp<0>>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<>(entt::get<position, velocity, comp<0>>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -634,7 +763,7 @@ TEST(Benchmark, IterateThreeComponentsFullOwningGroup1M) { | ||||
|         registry.emplace<comp<0>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position, velocity, comp<0>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -651,7 +780,7 @@ TEST(Benchmark, IterateThreeComponentsPartialOwningGroup1M) { | ||||
|         registry.emplace<comp<0>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position, velocity>(entt::get<comp<0>>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position, velocity>(entt::get<comp<0>>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -673,7 +802,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1M) { | ||||
|         .iterate(registry.storage<velocity>()) | ||||
|         .iterate(registry.storage<comp<0>>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|         registry.get<comp<0>>(entity).x = {}; | ||||
| @@ -700,7 +829,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MHalf) { | ||||
|         .iterate(registry.storage<velocity>()) | ||||
|         .iterate(registry.storage<comp<0>>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|         registry.get<comp<0>>(entity).x = {}; | ||||
| @@ -727,7 +856,7 @@ TEST(Benchmark, IterateThreeComponentsRuntime1MOne) { | ||||
|         .iterate(registry.storage<velocity>()) | ||||
|         .iterate(registry.storage<comp<0>>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|         registry.get<comp<0>>(entity).x = {}; | ||||
| @@ -748,15 +877,15 @@ TEST(Benchmark, IterateFiveComponents1M) { | ||||
|         registry.emplace<comp<2>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IterateFiveComponentsTombstonePolicy1M) { | ||||
| TEST(Benchmark, IterateFiveStableComponents1M) { | ||||
|     entt::registry registry; | ||||
|  | ||||
|     std::cout << "Iterating over 1000000 entities, five components, tombstone policy" << std::endl; | ||||
|     std::cout << "Iterating over 1000000 entities, five stable components" << std::endl; | ||||
|  | ||||
|     for(std::uint64_t i = 0; i < 1000000L; i++) { | ||||
|         const auto entity = registry.create(); | ||||
| @@ -767,7 +896,7 @@ TEST(Benchmark, IterateFiveComponentsTombstonePolicy1M) { | ||||
|         registry.emplace<comp<2>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<stable_position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<stable_position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -789,7 +918,7 @@ TEST(Benchmark, IterateFiveComponents1MHalf) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -811,7 +940,7 @@ TEST(Benchmark, IterateFiveComponents1MOne) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     generic(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.view<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -830,7 +959,7 @@ TEST(Benchmark, IterateFiveComponentsNonOwningGroup1M) { | ||||
|         registry.emplace<comp<2>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<>(entt::get<position, velocity, comp<0>, comp<1>, comp<2>>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -849,7 +978,7 @@ TEST(Benchmark, IterateFiveComponentsFullOwningGroup1M) { | ||||
|         registry.emplace<comp<2>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position, velocity, comp<0>, comp<1>, comp<2>>(), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -868,7 +997,7 @@ TEST(Benchmark, IterateFiveComponentsPartialFourOfFiveOwningGroup1M) { | ||||
|         registry.emplace<comp<2>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position, velocity, comp<0>, comp<1>>(entt::get<comp<2>>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -887,7 +1016,7 @@ TEST(Benchmark, IterateFiveComponentsPartialThreeOfFiveOwningGroup1M) { | ||||
|         registry.emplace<comp<2>>(entity); | ||||
|     } | ||||
|  | ||||
|     generic(registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>), [](auto &...comp) { | ||||
|     iterate_with(registry.group<position, velocity, comp<0>>(entt::get<comp<1>, comp<2>>), [](auto &...comp) { | ||||
|         ((comp.x = {}), ...); | ||||
|     }); | ||||
| } | ||||
| @@ -913,7 +1042,7 @@ TEST(Benchmark, IterateFiveComponentsRuntime1M) { | ||||
|         .iterate(registry.storage<comp<1>>()) | ||||
|         .iterate(registry.storage<comp<2>>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|         registry.get<comp<0>>(entity).x = {}; | ||||
| @@ -946,7 +1075,7 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MHalf) { | ||||
|         .iterate(registry.storage<comp<1>>()) | ||||
|         .iterate(registry.storage<comp<2>>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|         registry.get<comp<0>>(entity).x = {}; | ||||
| @@ -979,7 +1108,7 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) { | ||||
|         .iterate(registry.storage<comp<1>>()) | ||||
|         .iterate(registry.storage<comp<2>>()); | ||||
|  | ||||
|     generic(view, [®istry](auto entity) { | ||||
|     iterate_with(view, [&](auto entity) { | ||||
|         registry.get<position>(entity).x = {}; | ||||
|         registry.get<velocity>(entity).x = {}; | ||||
|         registry.get<comp<0>>(entity).x = {}; | ||||
| @@ -990,22 +1119,22 @@ TEST(Benchmark, IterateFiveComponentsRuntime1MOne) { | ||||
|  | ||||
| TEST(Benchmark, IteratePathological) { | ||||
|     std::cout << "Pathological case" << std::endl; | ||||
|     pathological([](auto ®istry) { return registry.template view<position, velocity, comp<0>>(); }); | ||||
|     pathological_with([](auto ®istry) { return registry.template view<position, velocity, comp<0>>(); }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IteratePathologicalNonOwningGroup) { | ||||
|     std::cout << "Pathological case (non-owning group)" << std::endl; | ||||
|     pathological([](auto ®istry) { return registry.template group<>(entt::get<position, velocity, comp<0>>); }); | ||||
|     pathological_with([](auto ®istry) { return registry.template group<>(entt::get<position, velocity, comp<0>>); }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IteratePathologicalFullOwningGroup) { | ||||
|     std::cout << "Pathological case (full-owning group)" << std::endl; | ||||
|     pathological([](auto ®istry) { return registry.template group<position, velocity, comp<0>>(); }); | ||||
|     pathological_with([](auto ®istry) { return registry.template group<position, velocity, comp<0>>(); }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, IteratePathologicalPartialOwningGroup) { | ||||
|     std::cout << "Pathological case (partial-owning group)" << std::endl; | ||||
|     pathological([](auto ®istry) { return registry.template group<position, velocity>(entt::get<comp<0>>); }); | ||||
|     pathological_with([](auto ®istry) { return registry.template group<position, velocity>(entt::get<comp<0>>); }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, SortSingle) { | ||||
| @@ -1018,9 +1147,9 @@ TEST(Benchmark, SortSingle) { | ||||
|         registry.emplace<position>(entity, i, i); | ||||
|     } | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x < rhs.x && lhs.y < rhs.y; }); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, SortMulti) { | ||||
| @@ -1036,9 +1165,9 @@ TEST(Benchmark, SortMulti) { | ||||
|  | ||||
|     registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x < rhs.x && lhs.y < rhs.y; }); | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.sort<velocity, position>(); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, AlmostSortedStdSort) { | ||||
| @@ -1062,9 +1191,9 @@ TEST(Benchmark, AlmostSortedStdSort) { | ||||
|         registry.emplace<position>(entity, 50000 * i, 50000 * i); | ||||
|     } | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x > rhs.x && lhs.y > rhs.y; }); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Benchmark, AlmostSortedInsertionSort) { | ||||
| @@ -1088,7 +1217,7 @@ TEST(Benchmark, AlmostSortedInsertionSort) { | ||||
|         registry.emplace<position>(entity, 50000 * i, 50000 * i); | ||||
|     } | ||||
|  | ||||
|     timer timer; | ||||
|     generic_with([&]() { | ||||
|         registry.sort<position>([](const auto &lhs, const auto &rhs) { return lhs.x > rhs.x && lhs.y > rhs.y; }, entt::insertion_sort{}); | ||||
|     timer.elapsed(); | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,7 @@ struct basic_test_allocator: std::allocator<Type> { | ||||
|         return *this; | ||||
|     } | ||||
|  | ||||
|     bool operator==(const basic_test_allocator &other) { | ||||
|     bool operator==(const basic_test_allocator &other) const { | ||||
|         return (this == &other); | ||||
|     } | ||||
| }; | ||||
|   | ||||
| @@ -25,14 +25,14 @@ public: | ||||
|     using propagate_on_container_swap = std::true_type; | ||||
|     using exception_type = test_exception; | ||||
|  | ||||
|     template<class Other> | ||||
|     template<typename Other> | ||||
|     struct rebind { | ||||
|         using other = throwing_allocator<Other>; | ||||
|     }; | ||||
|  | ||||
|     throwing_allocator() = default; | ||||
|  | ||||
|     template<class Other> | ||||
|     template<typename Other> | ||||
|     throwing_allocator(const throwing_allocator<Other> &other) | ||||
|         : base{other} {} | ||||
|  | ||||
|   | ||||
| @@ -100,7 +100,7 @@ TEST(DenseMap, Functionalities) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, Constructors) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<int, int> map; | ||||
|  | ||||
|     ASSERT_EQ(map.bucket_count(), minimum_bucket_count); | ||||
| @@ -395,7 +395,7 @@ TEST(DenseMap, Insert) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, InsertRehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     ASSERT_EQ(map.size(), 0u); | ||||
| @@ -429,7 +429,7 @@ TEST(DenseMap, InsertRehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, InsertSameBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     for(std::size_t next{}; next < minimum_bucket_count; ++next) { | ||||
| @@ -598,7 +598,7 @@ TEST(DenseMap, Emplace) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, EmplaceRehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     ASSERT_EQ(map.size(), 0u); | ||||
| @@ -633,7 +633,7 @@ TEST(DenseMap, EmplaceRehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, EmplaceSameBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     for(std::size_t next{}; next < minimum_bucket_count; ++next) { | ||||
| @@ -681,7 +681,7 @@ TEST(DenseMap, TryEmplace) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, TryEmplaceRehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     ASSERT_EQ(map.size(), 0u); | ||||
| @@ -715,7 +715,7 @@ TEST(DenseMap, TryEmplaceRehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, TryEmplaceSameBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     for(std::size_t next{}; next < minimum_bucket_count; ++next) { | ||||
| @@ -749,7 +749,7 @@ TEST(DenseMap, TryEmplaceMovableType) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, Erase) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) { | ||||
| @@ -799,7 +799,7 @@ TEST(DenseMap, Erase) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, EraseWithMovableKeyValue) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::string, std::size_t> map; | ||||
|  | ||||
|     map.emplace("0", 0u); | ||||
| @@ -817,7 +817,7 @@ TEST(DenseMap, EraseWithMovableKeyValue) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, EraseFromBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|  | ||||
|     ASSERT_EQ(map.bucket_count(), minimum_bucket_count); | ||||
| @@ -995,7 +995,7 @@ TEST(DenseMap, LocalIterator) { | ||||
|     static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<const std::size_t &, std::size_t &>>>); | ||||
|     static_assert(std::is_same_v<iterator::reference, std::pair<const std::size_t &, std::size_t &>>); | ||||
|  | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|     map.emplace(3u, 42u); | ||||
|     map.emplace(3u + minimum_bucket_count, 99u); | ||||
| @@ -1023,7 +1023,7 @@ TEST(DenseMap, ConstLocalIterator) { | ||||
|     static_assert(std::is_same_v<iterator::pointer, entt::input_iterator_pointer<std::pair<const std::size_t &, const std::size_t &>>>); | ||||
|     static_assert(std::is_same_v<iterator::reference, std::pair<const std::size_t &, const std::size_t &>>); | ||||
|  | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|     map.emplace(3u, 42u); | ||||
|     map.emplace(3u + minimum_bucket_count, 99u); | ||||
| @@ -1064,7 +1064,7 @@ TEST(DenseMap, LocalIteratorConversion) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, Rehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, entt::identity> map; | ||||
|     map[32u] = 99u; | ||||
|  | ||||
| @@ -1147,7 +1147,7 @@ TEST(DenseMap, Rehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, Reserve) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<int, int> map; | ||||
|  | ||||
|     ASSERT_EQ(map.bucket_count(), minimum_bucket_count); | ||||
| @@ -1159,7 +1159,7 @@ TEST(DenseMap, Reserve) { | ||||
|     map.reserve(minimum_bucket_count); | ||||
|  | ||||
|     ASSERT_EQ(map.bucket_count(), 2 * minimum_bucket_count); | ||||
|     ASSERT_EQ(map.bucket_count(), entt::next_power_of_two(std::ceil(minimum_bucket_count / map.max_load_factor()))); | ||||
|     ASSERT_EQ(map.bucket_count(), entt::next_power_of_two(static_cast<std::size_t>(std::ceil(minimum_bucket_count / map.max_load_factor())))); | ||||
| } | ||||
|  | ||||
| TEST(DenseMap, ThrowingAllocator) { | ||||
| @@ -1167,7 +1167,7 @@ TEST(DenseMap, ThrowingAllocator) { | ||||
|     using packed_allocator = test::throwing_allocator<entt::internal::dense_map_node<std::size_t, std::size_t>>; | ||||
|     using packed_exception = typename packed_allocator::exception_type; | ||||
|  | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_map<std::size_t, std::size_t, std::hash<std::size_t>, std::equal_to<std::size_t>, allocator> map{}; | ||||
|  | ||||
|     packed_allocator::trigger_on_allocate = true; | ||||
|   | ||||
| @@ -98,7 +98,7 @@ TEST(DenseSet, Functionalities) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, Constructors) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<int> set; | ||||
|  | ||||
|     ASSERT_EQ(set.bucket_count(), minimum_bucket_count); | ||||
| @@ -357,7 +357,7 @@ TEST(DenseSet, Insert) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, InsertRehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|  | ||||
|     ASSERT_EQ(set.size(), 0u); | ||||
| @@ -388,7 +388,7 @@ TEST(DenseSet, InsertRehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, InsertSameBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|  | ||||
|     for(std::size_t next{}; next < minimum_bucket_count; ++next) { | ||||
| @@ -451,7 +451,7 @@ TEST(DenseSet, Emplace) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, EmplaceRehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|  | ||||
|     ASSERT_EQ(set.size(), 0u); | ||||
| @@ -483,7 +483,7 @@ TEST(DenseSet, EmplaceRehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, EmplaceSameBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|  | ||||
|     for(std::size_t next{}; next < minimum_bucket_count; ++next) { | ||||
| @@ -503,7 +503,7 @@ TEST(DenseSet, EmplaceSameBucket) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, Erase) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|  | ||||
|     for(std::size_t next{}, last = minimum_bucket_count + 1u; next < last; ++next) { | ||||
| @@ -553,7 +553,7 @@ TEST(DenseSet, Erase) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, EraseWithMovableKeyValue) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::string> set; | ||||
|  | ||||
|     set.emplace("0"); | ||||
| @@ -570,7 +570,7 @@ TEST(DenseSet, EraseWithMovableKeyValue) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, EraseFromBucket) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|  | ||||
|     ASSERT_EQ(set.bucket_count(), minimum_bucket_count); | ||||
| @@ -721,7 +721,7 @@ TEST(DenseSet, LocalIterator) { | ||||
|     static_assert(std::is_same_v<iterator::pointer, const std::size_t *>); | ||||
|     static_assert(std::is_same_v<iterator::reference, const std::size_t &>); | ||||
|  | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|     set.emplace(3u); | ||||
|     set.emplace(3u + minimum_bucket_count); | ||||
| @@ -749,7 +749,7 @@ TEST(DenseSet, ConstLocalIterator) { | ||||
|     static_assert(std::is_same_v<iterator::pointer, const std::size_t *>); | ||||
|     static_assert(std::is_same_v<iterator::reference, const std::size_t &>); | ||||
|  | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|     set.emplace(3u); | ||||
|     set.emplace(3u + minimum_bucket_count); | ||||
| @@ -790,7 +790,7 @@ TEST(DenseSet, LocalIteratorConversion) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, Rehash) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, entt::identity> set; | ||||
|     set.emplace(32u); | ||||
|  | ||||
| @@ -865,7 +865,7 @@ TEST(DenseSet, Rehash) { | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, Reserve) { | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<int> set; | ||||
|  | ||||
|     ASSERT_EQ(set.bucket_count(), minimum_bucket_count); | ||||
| @@ -877,7 +877,7 @@ TEST(DenseSet, Reserve) { | ||||
|     set.reserve(minimum_bucket_count); | ||||
|  | ||||
|     ASSERT_EQ(set.bucket_count(), 2 * minimum_bucket_count); | ||||
|     ASSERT_EQ(set.bucket_count(), entt::next_power_of_two(std::ceil(minimum_bucket_count / set.max_load_factor()))); | ||||
|     ASSERT_EQ(set.bucket_count(), entt::next_power_of_two(static_cast<std::size_t>(std::ceil(minimum_bucket_count / set.max_load_factor())))); | ||||
| } | ||||
|  | ||||
| TEST(DenseSet, ThrowingAllocator) { | ||||
| @@ -885,7 +885,7 @@ TEST(DenseSet, ThrowingAllocator) { | ||||
|     using packed_allocator = test::throwing_allocator<std::pair<std::size_t, std::size_t>>; | ||||
|     using packed_exception = typename packed_allocator::exception_type; | ||||
|  | ||||
|     static constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     constexpr std::size_t minimum_bucket_count = 8u; | ||||
|     entt::dense_set<std::size_t, std::hash<std::size_t>, std::equal_to<std::size_t>, allocator> set{}; | ||||
|  | ||||
|     packed_allocator::trigger_on_allocate = true; | ||||
|   | ||||
							
								
								
									
										47
									
								
								external/entt/entt/test/entt/core/any.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										47
									
								
								external/entt/entt/test/entt/core/any.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -2,6 +2,7 @@ | ||||
| #include <cstdint> | ||||
| #include <cstring> | ||||
| #include <iterator> | ||||
| #include <memory> | ||||
| #include <type_traits> | ||||
| #include <unordered_map> | ||||
| #include <utility> | ||||
| @@ -39,21 +40,9 @@ struct not_comparable { | ||||
|     bool operator==(const not_comparable &) const = delete; | ||||
| }; | ||||
|  | ||||
| struct not_copyable { | ||||
|     not_copyable() | ||||
|         : payload{} {} | ||||
|  | ||||
|     not_copyable(const not_copyable &) = delete; | ||||
|     not_copyable(not_copyable &&) = default; | ||||
|  | ||||
|     not_copyable &operator=(const not_copyable &) = delete; | ||||
|     not_copyable &operator=(not_copyable &&) = default; | ||||
|  | ||||
|     double payload; | ||||
| }; | ||||
|  | ||||
| struct not_movable { | ||||
|     not_movable() = default; | ||||
|  | ||||
|     not_movable(const not_movable &) = default; | ||||
|     not_movable(not_movable &&) = delete; | ||||
|  | ||||
| @@ -1183,13 +1172,12 @@ TEST_F(Any, AnyCast) { | ||||
|     ASSERT_EQ(entt::any_cast<int &>(any), 42); | ||||
|     ASSERT_EQ(entt::any_cast<const int &>(cany), 42); | ||||
|  | ||||
|     not_copyable instance{}; | ||||
|     instance.payload = 42.; | ||||
|     auto instance = std::make_unique<double>(42.); | ||||
|     entt::any ref{entt::forward_as_any(instance)}; | ||||
|     entt::any cref{entt::forward_as_any(std::as_const(instance).payload)}; | ||||
|     entt::any cref{entt::forward_as_any(std::as_const(*instance))}; | ||||
|  | ||||
|     ASSERT_EQ(entt::any_cast<not_copyable>(std::move(ref)).payload, 42.); | ||||
|     ASSERT_EQ(entt::any_cast<double>(std::move(cref)), 42.); | ||||
|     ASSERT_EQ(*entt::any_cast<std::unique_ptr<double>>(std::move(ref)), 42.); | ||||
|     ASSERT_EQ(entt::any_cast<int>(entt::any{42}), 42); | ||||
| } | ||||
|  | ||||
| @@ -1197,16 +1185,15 @@ ENTT_DEBUG_TEST_F(AnyDeathTest, AnyCast) { | ||||
|     entt::any any{42}; | ||||
|     const auto &cany = any; | ||||
|  | ||||
|     ASSERT_DEATH(entt::any_cast<double &>(any), ""); | ||||
|     ASSERT_DEATH(entt::any_cast<const double &>(cany), ""); | ||||
|     ASSERT_DEATH([[maybe_unused]] auto &elem = entt::any_cast<double &>(any), ""); | ||||
|     ASSERT_DEATH([[maybe_unused]] const auto &elem = entt::any_cast<const double &>(cany), ""); | ||||
|  | ||||
|     not_copyable instance{}; | ||||
|     instance.payload = 42.; | ||||
|     auto instance = std::make_unique<double>(42.); | ||||
|     entt::any ref{entt::forward_as_any(instance)}; | ||||
|     entt::any cref{entt::forward_as_any(std::as_const(instance).payload)}; | ||||
|     entt::any cref{entt::forward_as_any(std::as_const(*instance))}; | ||||
|  | ||||
|     ASSERT_DEATH(entt::any_cast<not_copyable>(std::as_const(ref).as_ref()), ""); | ||||
|     ASSERT_DEATH(entt::any_cast<double>(entt::any{42}), ""); | ||||
|     ASSERT_DEATH([[maybe_unused]] auto elem = entt::any_cast<std::unique_ptr<double>>(std::as_const(ref).as_ref()), ""); | ||||
|     ASSERT_DEATH([[maybe_unused]] auto elem = entt::any_cast<double>(entt::any{42}), ""); | ||||
| } | ||||
|  | ||||
| TEST_F(Any, MakeAny) { | ||||
| @@ -1263,8 +1250,8 @@ TEST_F(Any, ForwardAsAny) { | ||||
| } | ||||
|  | ||||
| TEST_F(Any, NotCopyableType) { | ||||
|     const not_copyable value{}; | ||||
|     entt::any any{std::in_place_type<not_copyable>}; | ||||
|     const std::unique_ptr<int> value{}; | ||||
|     entt::any any{std::in_place_type<std::unique_ptr<int>>}; | ||||
|     entt::any other = entt::forward_as_any(value); | ||||
|  | ||||
|     ASSERT_TRUE(any); | ||||
| @@ -1296,7 +1283,7 @@ TEST_F(Any, NotCopyableType) { | ||||
|  | ||||
| TEST_F(Any, NotCopyableValueType) { | ||||
|     std::vector<entt::any> vec{}; | ||||
|     vec.emplace_back(std::in_place_type<not_copyable>); | ||||
|     vec.emplace_back(std::in_place_type<std::unique_ptr<int>>); | ||||
|     vec.shrink_to_fit(); | ||||
|  | ||||
|     ASSERT_EQ(vec.size(), 1u); | ||||
| @@ -1304,7 +1291,7 @@ TEST_F(Any, NotCopyableValueType) { | ||||
|     ASSERT_TRUE(vec[0u]); | ||||
|  | ||||
|     // strong exception guarantee due to noexcept move ctor | ||||
|     vec.emplace_back(std::in_place_type<not_copyable>); | ||||
|     vec.emplace_back(std::in_place_type<std::unique_ptr<int>>); | ||||
|  | ||||
|     ASSERT_EQ(vec.size(), 2u); | ||||
|     ASSERT_TRUE(vec[0u]); | ||||
| @@ -1411,7 +1398,7 @@ TEST_F(Any, SBOVsZeroedSBOSize) { | ||||
| } | ||||
|  | ||||
| TEST_F(Any, SboAlignment) { | ||||
|     static constexpr auto alignment = alignof(over_aligned); | ||||
|     constexpr auto alignment = alignof(over_aligned); | ||||
|     entt::basic_any<alignment, alignment> sbo[2] = {over_aligned{}, over_aligned{}}; | ||||
|     const auto *data = sbo[0].data(); | ||||
|  | ||||
| @@ -1427,7 +1414,7 @@ TEST_F(Any, SboAlignment) { | ||||
| } | ||||
|  | ||||
| TEST_F(Any, NoSboAlignment) { | ||||
|     static constexpr auto alignment = alignof(over_aligned); | ||||
|     constexpr auto alignment = alignof(over_aligned); | ||||
|     entt::basic_any<alignment> nosbo[2] = {over_aligned{}, over_aligned{}}; | ||||
|     const auto *data = nosbo[0].data(); | ||||
|  | ||||
|   | ||||
| @@ -6,19 +6,19 @@ | ||||
| #include <entt/core/hashed_string.hpp> | ||||
|  | ||||
| template<typename> | ||||
| struct foobar_t; | ||||
| struct expected; | ||||
|  | ||||
| template<> | ||||
| struct foobar_t<std::uint32_t> { | ||||
| struct expected<std::uint32_t> { | ||||
|     static constexpr auto value = 0xbf9cf968; | ||||
| }; | ||||
|  | ||||
| template<> | ||||
| struct foobar_t<std::uint64_t> { | ||||
| struct expected<std::uint64_t> { | ||||
|     static constexpr auto value = 0x85944171f73967e8; | ||||
| }; | ||||
|  | ||||
| inline constexpr auto foobar_v = foobar_t<entt::id_type>::value; | ||||
| inline constexpr auto expected_v = expected<entt::id_type>::value; | ||||
|  | ||||
| TEST(BasicHashedString, DeductionGuide) { | ||||
|     static_assert(std::is_same_v<decltype(entt::basic_hashed_string{"foo"}), entt::hashed_string>); | ||||
| @@ -47,8 +47,8 @@ TEST(HashedString, Functionalities) { | ||||
|  | ||||
|     entt::hashed_string hs{"foobar"}; | ||||
|  | ||||
|     ASSERT_EQ(static_cast<hash_type>(hs), foobar_v); | ||||
|     ASSERT_EQ(hs.value(), foobar_v); | ||||
|     ASSERT_EQ(static_cast<hash_type>(hs), expected_v); | ||||
|     ASSERT_EQ(hs.value(), expected_v); | ||||
|  | ||||
|     ASSERT_EQ(foo_hs, "foo"_hs); | ||||
|     ASSERT_NE(bar_hs, "foo"_hs); | ||||
| @@ -78,13 +78,13 @@ TEST(HashedString, Correctness) { | ||||
|     const char *foobar = "foobar"; | ||||
|     std::string_view view{"foobar__", 6}; | ||||
|  | ||||
|     ASSERT_EQ(entt::hashed_string{foobar}, foobar_v); | ||||
|     ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_string{"foobar"}, foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_string{foobar}, expected_v); | ||||
|     ASSERT_EQ((entt::hashed_string{view.data(), view.size()}), expected_v); | ||||
|     ASSERT_EQ(entt::hashed_string{"foobar"}, expected_v); | ||||
|  | ||||
|     ASSERT_EQ(entt::hashed_string::value(foobar), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_string::value("foobar"), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_string::value(foobar), expected_v); | ||||
|     ASSERT_EQ(entt::hashed_string::value(view.data(), view.size()), expected_v); | ||||
|     ASSERT_EQ(entt::hashed_string::value("foobar"), expected_v); | ||||
|  | ||||
|     ASSERT_EQ(entt::hashed_string{foobar}.size(), 6u); | ||||
|     ASSERT_EQ((entt::hashed_string{view.data(), view.size()}).size(), 6u); | ||||
| @@ -111,16 +111,16 @@ TEST(HashedString, Constexprness) { | ||||
|     constexpr std::string_view view{"foobar__", 6}; | ||||
|  | ||||
|     static_assert(entt::hashed_string{"quux"} == "quux"_hs); | ||||
|     static_assert(entt::hashed_string{"foobar"} == foobar_v); | ||||
|     static_assert(entt::hashed_string{"foobar"} == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_string::value("quux") == "quux"_hs); | ||||
|     static_assert(entt::hashed_string::value("foobar") == foobar_v); | ||||
|     static_assert(entt::hashed_string::value("foobar") == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_string{"quux", 4} == "quux"_hs); | ||||
|     static_assert(entt::hashed_string{view.data(), view.size()} == foobar_v); | ||||
|     static_assert(entt::hashed_string{view.data(), view.size()} == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_string::value("quux", 4) == "quux"_hs); | ||||
|     static_assert(entt::hashed_string::value(view.data(), view.size()) == foobar_v); | ||||
|     static_assert(entt::hashed_string::value(view.data(), view.size()) == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_string{"bar"} < "foo"_hs); | ||||
|     static_assert(entt::hashed_string{"bar"} <= "bar"_hs); | ||||
| @@ -151,8 +151,8 @@ TEST(HashedWString, Functionalities) { | ||||
|  | ||||
|     entt::hashed_wstring hws{L"foobar"}; | ||||
|  | ||||
|     ASSERT_EQ(static_cast<hash_type>(hws), foobar_v); | ||||
|     ASSERT_EQ(hws.value(), foobar_v); | ||||
|     ASSERT_EQ(static_cast<hash_type>(hws), expected_v); | ||||
|     ASSERT_EQ(hws.value(), expected_v); | ||||
|  | ||||
|     ASSERT_EQ(foo_hws, L"foo"_hws); | ||||
|     ASSERT_NE(bar_hws, L"foo"_hws); | ||||
| @@ -172,13 +172,13 @@ TEST(HashedWString, Correctness) { | ||||
|     const wchar_t *foobar = L"foobar"; | ||||
|     std::wstring_view view{L"foobar__", 6}; | ||||
|  | ||||
|     ASSERT_EQ(entt::hashed_wstring{foobar}, foobar_v); | ||||
|     ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring{L"foobar"}, foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring{foobar}, expected_v); | ||||
|     ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}), expected_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring{L"foobar"}, expected_v); | ||||
|  | ||||
|     ASSERT_EQ(entt::hashed_wstring::value(foobar), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), foobar_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring::value(foobar), expected_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring::value(view.data(), view.size()), expected_v); | ||||
|     ASSERT_EQ(entt::hashed_wstring::value(L"foobar"), expected_v); | ||||
|  | ||||
|     ASSERT_EQ(entt::hashed_wstring{foobar}.size(), 6u); | ||||
|     ASSERT_EQ((entt::hashed_wstring{view.data(), view.size()}).size(), 6u); | ||||
| @@ -205,16 +205,16 @@ TEST(HashedWString, Constexprness) { | ||||
|     constexpr std::wstring_view view{L"foobar__", 6}; | ||||
|  | ||||
|     static_assert(entt::hashed_wstring{L"quux"} == L"quux"_hws); | ||||
|     static_assert(entt::hashed_wstring{L"foobar"} == foobar_v); | ||||
|     static_assert(entt::hashed_wstring{L"foobar"} == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_wstring::value(L"quux") == L"quux"_hws); | ||||
|     static_assert(entt::hashed_wstring::value(L"foobar") == foobar_v); | ||||
|     static_assert(entt::hashed_wstring::value(L"foobar") == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_wstring{L"quux", 4} == L"quux"_hws); | ||||
|     static_assert(entt::hashed_wstring{view.data(), view.size()} == foobar_v); | ||||
|     static_assert(entt::hashed_wstring{view.data(), view.size()} == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_wstring::value(L"quux", 4) == L"quux"_hws); | ||||
|     static_assert(entt::hashed_wstring::value(view.data(), view.size()) == foobar_v); | ||||
|     static_assert(entt::hashed_wstring::value(view.data(), view.size()) == expected_v); | ||||
|  | ||||
|     static_assert(entt::hashed_wstring{L"bar"} < L"foo"_hws); | ||||
|     static_assert(entt::hashed_wstring{L"bar"} <= L"bar"_hws); | ||||
|   | ||||
							
								
								
									
										14
									
								
								external/entt/entt/test/entt/core/memory.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										14
									
								
								external/entt/entt/test/entt/core/memory.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -24,6 +24,12 @@ TEST(ToAddress, Functionalities) { | ||||
|  | ||||
| TEST(PoccaPocmaAndPocs, Functionalities) { | ||||
|     test::basic_test_allocator<int> lhs, rhs; | ||||
|     test::basic_test_allocator<int, std::false_type> no_pocs; | ||||
|  | ||||
|     // code coverage purposes | ||||
|     ASSERT_FALSE(lhs == rhs); | ||||
|     ASSERT_NO_FATAL_FAILURE(entt::propagate_on_container_swap(no_pocs, no_pocs)); | ||||
|  | ||||
|     // honestly, I don't even know how one is supposed to test such a thing :) | ||||
|     entt::propagate_on_container_copy_assignment(lhs, rhs); | ||||
|     entt::propagate_on_container_move_assignment(lhs, rhs); | ||||
| @@ -31,8 +37,8 @@ TEST(PoccaPocmaAndPocs, Functionalities) { | ||||
| } | ||||
|  | ||||
| ENTT_DEBUG_TEST(PoccaPocmaAndPocsDeathTest, Functionalities) { | ||||
|     using pocs = std::false_type; | ||||
|     test::basic_test_allocator<int, pocs> lhs, rhs; | ||||
|     test::basic_test_allocator<int, std::false_type> lhs, rhs; | ||||
|  | ||||
|     ASSERT_DEATH(entt::propagate_on_container_swap(lhs, rhs), ""); | ||||
| } | ||||
|  | ||||
| @@ -60,8 +66,8 @@ TEST(NextPowerOfTwo, Functionalities) { | ||||
|     ASSERT_EQ(entt::next_power_of_two(17u), 32u); | ||||
|     ASSERT_EQ(entt::next_power_of_two(32u), 32u); | ||||
|     ASSERT_EQ(entt::next_power_of_two(33u), 64u); | ||||
|     ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16)), std::pow(2, 16)); | ||||
|     ASSERT_EQ(entt::next_power_of_two(std::pow(2, 16) + 1u), std::pow(2, 17)); | ||||
|     ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16))), static_cast<std::size_t>(std::pow(2, 16))); | ||||
|     ASSERT_EQ(entt::next_power_of_two(static_cast<std::size_t>(std::pow(2, 16) + 1u)), static_cast<std::size_t>(std::pow(2, 17))); | ||||
| } | ||||
|  | ||||
| ENTT_DEBUG_TEST(NextPowerOfTwoDeathTest, Functionalities) { | ||||
|   | ||||
							
								
								
									
										2
									
								
								external/entt/entt/test/entt/core/tuple.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								external/entt/entt/test/entt/core/tuple.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -34,5 +34,5 @@ TEST(Tuple, UnwrapTuple) { | ||||
| TEST(Tuple, ForwardApply) { | ||||
|     ASSERT_EQ(entt::forward_apply{[](auto &&...args) { return sizeof...(args); }}(std::make_tuple()), 0u); | ||||
|     ASSERT_EQ(entt::forward_apply{[](int i) { return i; }}(std::make_tuple(42)), 42); | ||||
|     ASSERT_EQ(entt::forward_apply{[](auto... args) { return (args + ...); }}(std::make_tuple('a', 1u)), 'b'); | ||||
|     ASSERT_EQ(entt::forward_apply{[](auto... args) { return (args + ...); }}(std::make_tuple('a', 1)), 'b'); | ||||
| } | ||||
|   | ||||
| @@ -89,7 +89,7 @@ TEST(TypeList, Functionalities) { | ||||
|     static_assert(std::is_same_v<entt::type_list_cat_t<type, other, type, other>, entt::type_list<int, char, double, int, char, double>>); | ||||
|     static_assert(std::is_same_v<entt::type_list_cat_t<type, other>, entt::type_list<int, char, double>>); | ||||
|     static_assert(std::is_same_v<entt::type_list_cat_t<type, type>, entt::type_list<int, char, int, char>>); | ||||
|     static_assert(std::is_same_v<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, entt::type_list<int, char>>); | ||||
|     static_assert(std::is_same_v<entt::type_list_unique_t<entt::type_list_cat_t<type, type>>, type>); | ||||
|  | ||||
|     static_assert(entt::type_list_contains_v<type, int>); | ||||
|     static_assert(entt::type_list_contains_v<type, char>); | ||||
| @@ -112,6 +112,14 @@ TEST(TypeList, Functionalities) { | ||||
|     static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, entt::type_identity>, entt::type_list<int, char>>); | ||||
|     static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, std::add_const>, entt::type_list<const int, const char>>); | ||||
|     static_assert(std::is_same_v<entt::type_list_transform_t<entt::type_list<int, char>, multi_argument_operation>, entt::type_list<void, void>>); | ||||
|  | ||||
|     static_assert(std::tuple_size_v<entt::type_list<>> == 0u); | ||||
|     static_assert(std::tuple_size_v<entt::type_list<int>> == 1u); | ||||
|     static_assert(std::tuple_size_v<entt::type_list<int, float>> == 2u); | ||||
|  | ||||
|     static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::type_list<int>>>); | ||||
|     static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::type_list<int, float>>>); | ||||
|     static_assert(std::is_same_v<float, std::tuple_element_t<1, entt::type_list<int, float>>>); | ||||
| } | ||||
|  | ||||
| TEST(ValueList, Functionalities) { | ||||
| @@ -125,10 +133,33 @@ TEST(ValueList, Functionalities) { | ||||
|     static_assert(std::is_same_v<entt::value_list_cat_t<value, other, value, other>, entt::value_list<0, 2, 1, 0, 2, 1>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_cat_t<value, other>, entt::value_list<0, 2, 1>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_cat_t<value, value>, entt::value_list<0, 2, 0, 2>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_unique_t<entt::value_list_cat_t<value, value>>, value>); | ||||
|  | ||||
|     static_assert(entt::value_list_contains_v<value, 0>); | ||||
|     static_assert(entt::value_list_contains_v<value, 2>); | ||||
|     static_assert(!entt::value_list_contains_v<value, 1>); | ||||
|  | ||||
|     static_assert(entt::value_list_element_v<0u, value> == 0); | ||||
|     static_assert(entt::value_list_element_v<1u, value> == 2); | ||||
|     static_assert(entt::value_list_element_v<0u, other> == 1); | ||||
|  | ||||
|     static_assert(entt::value_list_index_v<0, value> == 0u); | ||||
|     static_assert(entt::value_list_index_v<2, value> == 1u); | ||||
|     static_assert(entt::value_list_index_v<1, other> == 0u); | ||||
|  | ||||
|     static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<3, 4>>, entt::value_list<0, 1, 2>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<0, 1, 2>>, entt::value_list<>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<0, 1>>, entt::value_list<2>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<1, 2>>, entt::value_list<0>>); | ||||
|     static_assert(std::is_same_v<entt::value_list_diff_t<entt::value_list<0, 1, 2>, entt::value_list<1>>, entt::value_list<0, 2>>); | ||||
|  | ||||
|     static_assert(std::tuple_size_v<entt::value_list<>> == 0u); | ||||
|     static_assert(std::tuple_size_v<entt::value_list<42>> == 1u); | ||||
|     static_assert(std::tuple_size_v<entt::value_list<42, 'a'>> == 2u); | ||||
|  | ||||
|     static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::value_list<42>>>); | ||||
|     static_assert(std::is_same_v<int, std::tuple_element_t<0, entt::value_list<42, 'a'>>>); | ||||
|     static_assert(std::is_same_v<char, std::tuple_element_t<1, entt::value_list<42, 'a'>>>); | ||||
| } | ||||
|  | ||||
| TEST(IsApplicable, Functionalities) { | ||||
| @@ -183,6 +214,7 @@ TEST(IsEqualityComparable, Functionalities) { | ||||
|     static_assert(entt::is_equality_comparable_v<std::vector<not_comparable>::iterator>); | ||||
|     static_assert(entt::is_equality_comparable_v<nlohmann_json_like>); | ||||
|  | ||||
|     static_assert(!entt::is_equality_comparable_v<int[3u]>); | ||||
|     static_assert(!entt::is_equality_comparable_v<not_comparable>); | ||||
|     static_assert(!entt::is_equality_comparable_v<const not_comparable>); | ||||
|     static_assert(!entt::is_equality_comparable_v<std::vector<not_comparable>>); | ||||
|   | ||||
| @@ -30,50 +30,43 @@ struct entt::component_traits<traits_based> { | ||||
| }; | ||||
|  | ||||
| TEST(Component, VoidType) { | ||||
|     using traits = entt::component_traits<void>; | ||||
|     using traits_type = entt::component_traits<void>; | ||||
|  | ||||
|     static_assert(traits::in_place_delete); | ||||
|     static_assert(entt::ignore_as_empty_v<typename traits::type>); | ||||
|     // we don't really care about this thanks to ignore_as_empty_v | ||||
|     static_assert(traits::page_size != 0u); | ||||
|     static_assert(!traits_type::in_place_delete); | ||||
|     static_assert(traits_type::page_size == 0u); | ||||
| } | ||||
|  | ||||
| TEST(Component, Empty) { | ||||
|     using traits = entt::component_traits<empty>; | ||||
|     using traits_type = entt::component_traits<empty>; | ||||
|  | ||||
|     static_assert(!traits::in_place_delete); | ||||
|     static_assert(entt::ignore_as_empty_v<typename traits::type>); | ||||
|     static_assert(traits::page_size == 0u); | ||||
|     static_assert(!traits_type::in_place_delete); | ||||
|     static_assert(traits_type::page_size == 0u); | ||||
| } | ||||
|  | ||||
| TEST(Component, NonEmpty) { | ||||
|     using traits = entt::component_traits<non_empty>; | ||||
|     using traits_type = entt::component_traits<non_empty>; | ||||
|  | ||||
|     static_assert(!traits::in_place_delete); | ||||
|     static_assert(!entt::ignore_as_empty_v<typename traits::type>); | ||||
|     static_assert(traits::page_size == ENTT_PACKED_PAGE); | ||||
|     static_assert(!traits_type::in_place_delete); | ||||
|     static_assert(traits_type::page_size == ENTT_PACKED_PAGE); | ||||
| } | ||||
|  | ||||
| TEST(Component, NonMovable) { | ||||
|     using traits = entt::component_traits<non_movable>; | ||||
|     using traits_type = entt::component_traits<non_movable>; | ||||
|  | ||||
|     static_assert(traits::in_place_delete); | ||||
|     static_assert(!entt::ignore_as_empty_v<typename traits::type>); | ||||
|     static_assert(traits::page_size == ENTT_PACKED_PAGE); | ||||
|     static_assert(traits_type::in_place_delete); | ||||
|     static_assert(traits_type::page_size == ENTT_PACKED_PAGE); | ||||
| } | ||||
|  | ||||
| TEST(Component, SelfContained) { | ||||
|     using traits = entt::component_traits<self_contained>; | ||||
|     using traits_type = entt::component_traits<self_contained>; | ||||
|  | ||||
|     static_assert(traits::in_place_delete); | ||||
|     static_assert(!entt::ignore_as_empty_v<typename traits::type>); | ||||
|     static_assert(traits::page_size == 4u); | ||||
|     static_assert(traits_type::in_place_delete); | ||||
|     static_assert(traits_type::page_size == 4u); | ||||
| } | ||||
|  | ||||
| TEST(Component, TraitsBased) { | ||||
|     using traits = entt::component_traits<traits_based>; | ||||
|     using traits_type = entt::component_traits<traits_based>; | ||||
|  | ||||
|     static_assert(!traits::in_place_delete); | ||||
|     static_assert(!entt::ignore_as_empty_v<typename traits::type>); | ||||
|     static_assert(traits::page_size == 8u); | ||||
|     static_assert(!traits_type::in_place_delete); | ||||
|     static_assert(traits_type::page_size == 8u); | ||||
| } | ||||
|   | ||||
| @@ -29,6 +29,12 @@ TEST(Entity, Traits) { | ||||
|  | ||||
|     ASSERT_EQ(traits_type::combine(entt::tombstone, entt::null), tombstone); | ||||
|     ASSERT_EQ(traits_type::combine(entt::null, entt::tombstone), null); | ||||
|  | ||||
|     ASSERT_EQ(traits_type::next(entity), traits_type::construct(entt::to_integral(entity), entt::to_version(entity) + 1u)); | ||||
|     ASSERT_EQ(traits_type::next(other), traits_type::construct(entt::to_integral(other), entt::to_version(other) + 1u)); | ||||
|  | ||||
|     ASSERT_EQ(traits_type::next(entt::tombstone), traits_type::construct(entt::null, {})); | ||||
|     ASSERT_EQ(traits_type::next(entt::null), traits_type::construct(entt::null, {})); | ||||
| } | ||||
|  | ||||
| TEST(Entity, Null) { | ||||
|   | ||||
							
								
								
									
										406
									
								
								external/entt/entt/test/entt/entity/group.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										406
									
								
								external/entt/entt/test/entt/entity/group.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -8,6 +8,7 @@ | ||||
| #include <gtest/gtest.h> | ||||
| #include <entt/entity/group.hpp> | ||||
| #include <entt/entity/registry.hpp> | ||||
| #include "../common/config.h" | ||||
|  | ||||
| struct empty_type {}; | ||||
|  | ||||
| @@ -56,7 +57,7 @@ TEST(NonOwningGroup, Functionalities) { | ||||
|     for(auto entity: group) { | ||||
|         ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42); | ||||
|         ASSERT_EQ(std::get<1>(group.get<int, char>(entity)), '2'); | ||||
|         ASSERT_EQ(cgroup.get<const char>(entity), '2'); | ||||
|         ASSERT_EQ(cgroup.get<1>(entity), '2'); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(group.handle().data()[0u], e1); | ||||
| @@ -93,6 +94,7 @@ TEST(NonOwningGroup, Handle) { | ||||
|     ASSERT_TRUE(handle.empty()); | ||||
|     ASSERT_FALSE(handle.contains(entity)); | ||||
|     ASSERT_EQ(&handle, &group.handle()); | ||||
|     ASSERT_NE(&handle, group.storage<int>()); | ||||
|  | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.emplace<char>(entity); | ||||
| @@ -187,10 +189,10 @@ TEST(NonOwningGroup, Each) { | ||||
|     auto cgroup = std::as_const(registry).group_if_exists(entt::get<const int, const char>); | ||||
|  | ||||
|     registry.emplace<int>(entity[0u], 0); | ||||
|     registry.emplace<char>(entity[0u], 0); | ||||
|     registry.emplace<char>(entity[0u], static_cast<char>(0)); | ||||
|  | ||||
|     registry.emplace<int>(entity[1u], 1); | ||||
|     registry.emplace<char>(entity[1u], 1); | ||||
|     registry.emplace<char>(entity[1u], static_cast<char>(1)); | ||||
|  | ||||
|     auto iterable = group.each(); | ||||
|     auto citerable = cgroup.each(); | ||||
| @@ -201,16 +203,18 @@ TEST(NonOwningGroup, Each) { | ||||
|  | ||||
|     auto it = iterable.begin(); | ||||
|  | ||||
|     ASSERT_EQ(it.base(), group.begin()); | ||||
|     ASSERT_EQ((it++, ++it), iterable.end()); | ||||
|     ASSERT_EQ(it.base(), group.end()); | ||||
|  | ||||
|     group.each([expected = 1u](auto entt, int &ivalue, char &cvalue) mutable { | ||||
|         ASSERT_EQ(entt::to_integral(entt), expected); | ||||
|     group.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable { | ||||
|         ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), expected); | ||||
|         ASSERT_EQ(ivalue, expected); | ||||
|         ASSERT_EQ(cvalue, expected); | ||||
|         --expected; | ||||
|     }); | ||||
|  | ||||
|     cgroup.each([expected = 1u](const int &ivalue, const char &cvalue) mutable { | ||||
|     cgroup.each([expected = 1](const int &ivalue, const char &cvalue) mutable { | ||||
|         ASSERT_EQ(ivalue, expected); | ||||
|         ASSERT_EQ(cvalue, expected); | ||||
|         --expected; | ||||
| @@ -224,8 +228,8 @@ TEST(NonOwningGroup, Each) { | ||||
|  | ||||
|     // do not use iterable, make sure an iterable group works when created from a temporary | ||||
|     for(auto [entt, ivalue, cvalue]: registry.group(entt::get<int, char>).each()) { | ||||
|         ASSERT_EQ(entt::to_integral(entt), ivalue); | ||||
|         ASSERT_EQ(entt::to_integral(entt), cvalue); | ||||
|         ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), ivalue); | ||||
|         ASSERT_EQ(static_cast<char>(entt::to_integral(entt)), cvalue); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -273,9 +277,9 @@ TEST(NonOwningGroup, Sort) { | ||||
|     ASSERT_EQ(group.handle().data()[1u], e1); | ||||
|     ASSERT_EQ(group.handle().data()[2u], e2); | ||||
|  | ||||
|     ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u))); | ||||
|     ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u))); | ||||
|     ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u))); | ||||
|     ASSERT_EQ((group.get<0, 1>(e0)), (std::make_tuple(0, 0u))); | ||||
|     ASSERT_EQ((group.get<0, 1>(e1)), (std::make_tuple(1, 1u))); | ||||
|     ASSERT_EQ((group.get<0, 1>(e2)), (std::make_tuple(2, 2u))); | ||||
|  | ||||
|     ASSERT_FALSE(group.contains(e3)); | ||||
|  | ||||
| @@ -323,10 +327,10 @@ TEST(NonOwningGroup, SortAsAPool) { | ||||
|     } | ||||
|  | ||||
|     registry.sort<unsigned int>(std::less<unsigned int>{}); | ||||
|     group.sort<unsigned int>(); | ||||
|     group.sort_as(*group.storage<unsigned int>()); | ||||
|  | ||||
|     ASSERT_EQ((group.get<const int, unsigned int>(e0)), (std::make_tuple(0, 0u))); | ||||
|     ASSERT_EQ((group.get<const int, unsigned int>(e1)), (std::make_tuple(1, 1u))); | ||||
|     ASSERT_EQ((group.get<0, 1>(e1)), (std::make_tuple(1, 1u))); | ||||
|     ASSERT_EQ((group.get<const int, unsigned int>(e2)), (std::make_tuple(2, 2u))); | ||||
|  | ||||
|     ASSERT_FALSE(group.contains(e3)); | ||||
| @@ -384,9 +388,18 @@ TEST(NonOwningGroup, ConstNonConstAndAllInBetween) { | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 1u); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<0>({})), int &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<int>({})), int &>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<1>({})), void>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<empty_type>({})), void>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<2>({})), const char &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<const char>({})), const char &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<int, const char>({})), std::tuple<int &, const char &>>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<int, empty_type, const char>({})), std::tuple<int &, const char &>>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<0, 1, 2>({})), std::tuple<int &, const char &>>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &>>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists(entt::get<int, char>)), decltype(std::as_const(registry).group_if_exists(entt::get<const int, const char>))>); | ||||
| @@ -475,7 +488,7 @@ TEST(NonOwningGroup, ExcludedComponents) { | ||||
|         if(entity == e0) { | ||||
|             ASSERT_EQ(group.get<int>(e0), 0); | ||||
|         } else if(entity == e2) { | ||||
|             ASSERT_EQ(group.get<int>(e2), 2); | ||||
|             ASSERT_EQ(group.get<0>(e2), 2); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -493,7 +506,7 @@ TEST(NonOwningGroup, ExcludedComponents) { | ||||
|         if(entity == e1) { | ||||
|             ASSERT_EQ(group.get<int>(e1), 1); | ||||
|         } else if(entity == e3) { | ||||
|             ASSERT_EQ(group.get<int>(e3), 3); | ||||
|             ASSERT_EQ(group.get<0>(e3), 3); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -663,28 +676,100 @@ TEST(NonOwningGroup, IterableGroupAlgorithmCompatibility) { | ||||
| TEST(NonOwningGroup, Storage) { | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|     const auto group = registry.group(entt::get<int, const char>); | ||||
|     auto group = registry.group(entt::get<int, const char>, entt::exclude<double, const float>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const int>()), entt::storage_type_t<int> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<char>()), const entt::storage_type_t<char> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<2u>()), entt::storage_type_t<double> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<double>()), entt::storage_type_t<double> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const double>()), entt::storage_type_t<double> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<3u>()), const entt::storage_type_t<float> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<float>()), const entt::storage_type_t<float> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const float>()), const entt::storage_type_t<float> *>); | ||||
|  | ||||
|     ASSERT_TRUE(group); | ||||
|  | ||||
|     ASSERT_NE(group.storage<int>(), nullptr); | ||||
|     ASSERT_NE(group.storage<1u>(), nullptr); | ||||
|     ASSERT_NE(group.storage<double>(), nullptr); | ||||
|     ASSERT_NE(group.storage<3u>(), nullptr); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
|  | ||||
|     group.storage<int>().emplace(entity); | ||||
|     group.storage<int>()->emplace(entity); | ||||
|     group.storage<double>()->emplace(entity); | ||||
|     registry.emplace<char>(entity); | ||||
|     registry.emplace<float>(entity); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
|     ASSERT_EQ(group.begin(), group.end()); | ||||
|     ASSERT_TRUE(group.storage<int>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<const char>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<double>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<const float>()->contains(entity)); | ||||
|     ASSERT_TRUE((registry.all_of<int, char, double, float>(entity))); | ||||
|  | ||||
|     group.storage<double>()->erase(entity); | ||||
|     registry.erase<float>(entity); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 1u); | ||||
|     ASSERT_TRUE(group.storage<int>().contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<const char>().contains(entity)); | ||||
|     ASSERT_NE(group.begin(), group.end()); | ||||
|     ASSERT_TRUE(group.storage<const int>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<char>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<const double>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<float>()->contains(entity)); | ||||
|     ASSERT_TRUE((registry.all_of<int, char>(entity))); | ||||
|     ASSERT_FALSE((registry.any_of<double, float>(entity))); | ||||
|  | ||||
|     group.storage<0u>().erase(entity); | ||||
|     group.storage<0u>()->erase(entity); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
|     ASSERT_TRUE(group.storage<1u>().contains(entity)); | ||||
|     ASSERT_FALSE((registry.all_of<int, char>(entity))); | ||||
|     ASSERT_EQ(group.begin(), group.end()); | ||||
|     ASSERT_FALSE(group.storage<0u>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<1u>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<2u>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<3u>()->contains(entity)); | ||||
|     ASSERT_TRUE((registry.all_of<char>(entity))); | ||||
|     ASSERT_FALSE((registry.any_of<int, double, float>(entity))); | ||||
|  | ||||
|     group = {}; | ||||
|  | ||||
|     ASSERT_FALSE(group); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<0u>(), nullptr); | ||||
|     ASSERT_EQ(group.storage<const char>(), nullptr); | ||||
|     ASSERT_EQ(group.storage<2u>(), nullptr); | ||||
|     ASSERT_EQ(group.storage<const float>(), nullptr); | ||||
| } | ||||
|  | ||||
| TEST(NonOwningGroup, Overlapping) { | ||||
|     entt::registry registry; | ||||
|  | ||||
|     auto group = registry.group(entt::get<char>, entt::exclude<double>); | ||||
|     auto other = registry.group<int>(entt::get<char>, entt::exclude<double>); | ||||
|  | ||||
|     ASSERT_TRUE(group.empty()); | ||||
|     ASSERT_TRUE(other.empty()); | ||||
|  | ||||
|     const auto entity = registry.create(); | ||||
|     registry.emplace<char>(entity, '1'); | ||||
|  | ||||
|     ASSERT_FALSE(group.empty()); | ||||
|     ASSERT_TRUE(other.empty()); | ||||
|  | ||||
|     registry.emplace<int>(entity, 42); | ||||
|  | ||||
|     ASSERT_FALSE(group.empty()); | ||||
|     ASSERT_FALSE(other.empty()); | ||||
|  | ||||
|     registry.emplace<double>(entity, 3.); | ||||
|  | ||||
|     ASSERT_TRUE(group.empty()); | ||||
|     ASSERT_TRUE(other.empty()); | ||||
| } | ||||
|  | ||||
| TEST(OwningGroup, Functionalities) { | ||||
| @@ -721,16 +806,17 @@ TEST(OwningGroup, Functionalities) { | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 1u); | ||||
|  | ||||
|     ASSERT_EQ(cgroup.storage<const int>().raw()[0u][0u], 42); | ||||
|     ASSERT_EQ(group.storage<int>().raw()[0u][0u], 42); | ||||
|     ASSERT_EQ(cgroup.storage<const int>()->raw()[0u][0u], 42); | ||||
|     ASSERT_EQ(group.storage<int>()->raw()[0u][0u], 42); | ||||
|  | ||||
|     for(auto entity: group) { | ||||
|         ASSERT_EQ(std::get<0>(cgroup.get<const int, const char>(entity)), 42); | ||||
|         ASSERT_EQ(std::get<1>(group.get<int, char>(entity)), '2'); | ||||
|         ASSERT_EQ(cgroup.get<const char>(entity), '2'); | ||||
|         ASSERT_EQ(cgroup.get<1>(entity), '2'); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(group.storage<int>().raw()[0u][0u], 42); | ||||
|     ASSERT_EQ(group.handle().data()[0u], e1); | ||||
|     ASSERT_EQ(group.storage<int>()->raw()[0u][0u], 42); | ||||
|  | ||||
|     registry.erase<char>(e0); | ||||
|     registry.erase<char>(e1); | ||||
| @@ -748,6 +834,26 @@ TEST(OwningGroup, Functionalities) { | ||||
|     ASSERT_FALSE(invalid); | ||||
| } | ||||
|  | ||||
| TEST(OwningGroup, Handle) { | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|  | ||||
|     auto group = registry.group<int>(entt::get<char>); | ||||
|     auto &&handle = group.handle(); | ||||
|  | ||||
|     ASSERT_TRUE(handle.empty()); | ||||
|     ASSERT_FALSE(handle.contains(entity)); | ||||
|     ASSERT_EQ(&handle, &group.handle()); | ||||
|     ASSERT_EQ(&handle, group.storage<int>()); | ||||
|  | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.emplace<char>(entity); | ||||
|  | ||||
|     ASSERT_FALSE(handle.empty()); | ||||
|     ASSERT_TRUE(handle.contains(entity)); | ||||
|     ASSERT_EQ(&handle, &group.handle()); | ||||
| } | ||||
|  | ||||
| TEST(OwningGroup, Invalid) { | ||||
|     entt::registry registry{}; | ||||
|     auto group = std::as_const(registry).group_if_exists<const int>(entt::get<const empty_type>); | ||||
| @@ -831,10 +937,10 @@ TEST(OwningGroup, Each) { | ||||
|     auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<const char>); | ||||
|  | ||||
|     registry.emplace<int>(entity[0u], 0); | ||||
|     registry.emplace<char>(entity[0u], 0); | ||||
|     registry.emplace<char>(entity[0u], static_cast<char>(0)); | ||||
|  | ||||
|     registry.emplace<int>(entity[1u], 1); | ||||
|     registry.emplace<char>(entity[1u], 1); | ||||
|     registry.emplace<char>(entity[1u], static_cast<char>(1)); | ||||
|  | ||||
|     auto iterable = group.each(); | ||||
|     auto citerable = cgroup.each(); | ||||
| @@ -845,16 +951,18 @@ TEST(OwningGroup, Each) { | ||||
|  | ||||
|     auto it = iterable.begin(); | ||||
|  | ||||
|     ASSERT_EQ(it.base(), group.begin()); | ||||
|     ASSERT_EQ((it++, ++it), iterable.end()); | ||||
|     ASSERT_EQ(it.base(), group.end()); | ||||
|  | ||||
|     group.each([expected = 1u](auto entt, int &ivalue, char &cvalue) mutable { | ||||
|         ASSERT_EQ(entt::to_integral(entt), expected); | ||||
|     group.each([expected = 1](auto entt, int &ivalue, char &cvalue) mutable { | ||||
|         ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), expected); | ||||
|         ASSERT_EQ(ivalue, expected); | ||||
|         ASSERT_EQ(cvalue, expected); | ||||
|         --expected; | ||||
|     }); | ||||
|  | ||||
|     cgroup.each([expected = 1u](const int &ivalue, const char &cvalue) mutable { | ||||
|     cgroup.each([expected = 1](const int &ivalue, const char &cvalue) mutable { | ||||
|         ASSERT_EQ(ivalue, expected); | ||||
|         ASSERT_EQ(cvalue, expected); | ||||
|         --expected; | ||||
| @@ -868,8 +976,8 @@ TEST(OwningGroup, Each) { | ||||
|  | ||||
|     // do not use iterable, make sure an iterable group works when created from a temporary | ||||
|     for(auto [entt, ivalue, cvalue]: registry.group<int>(entt::get<char>).each()) { | ||||
|         ASSERT_EQ(entt::to_integral(entt), ivalue); | ||||
|         ASSERT_EQ(entt::to_integral(entt), cvalue); | ||||
|         ASSERT_EQ(static_cast<int>(entt::to_integral(entt)), ivalue); | ||||
|         ASSERT_EQ(static_cast<char>(entt::to_integral(entt)), cvalue); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -893,27 +1001,27 @@ TEST(OwningGroup, SortOrdered) { | ||||
|     registry.emplace<boxed_int>(entities[4], 2); | ||||
|  | ||||
|     group.sort([&group](const entt::entity lhs, const entt::entity rhs) { | ||||
|         return group.get<boxed_int>(lhs).value < group.get<boxed_int>(rhs).value; | ||||
|         return group.get<boxed_int>(lhs).value < group.get<0>(rhs).value; | ||||
|     }); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[0]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[1]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[2]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[3]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[4u], entities[4]); | ||||
|     ASSERT_EQ(group.handle().data()[0u], entities[0]); | ||||
|     ASSERT_EQ(group.handle().data()[1u], entities[1]); | ||||
|     ASSERT_EQ(group.handle().data()[2u], entities[2]); | ||||
|     ASSERT_EQ(group.handle().data()[3u], entities[3]); | ||||
|     ASSERT_EQ(group.handle().data()[4u], entities[4]); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 12); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 9); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 6); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][4u].value, 2); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 2); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<char>().raw()[0u][0u], 'a'); | ||||
|     ASSERT_EQ(group.storage<char>().raw()[0u][1u], 'b'); | ||||
|     ASSERT_EQ(group.storage<char>().raw()[0u][2u], 'c'); | ||||
|     ASSERT_EQ(group.storage<char>()->raw()[0u][0u], 'a'); | ||||
|     ASSERT_EQ(group.storage<char>()->raw()[0u][1u], 'b'); | ||||
|     ASSERT_EQ(group.storage<char>()->raw()[0u][2u], 'c'); | ||||
|  | ||||
|     ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{12}, 'a'))); | ||||
|     ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{9}, 'b'))); | ||||
|     ASSERT_EQ((group.get<0, 1>(entities[1])), (std::make_tuple(boxed_int{9}, 'b'))); | ||||
|     ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{6}, 'c'))); | ||||
|  | ||||
|     ASSERT_FALSE(group.contains(entities[3])); | ||||
| @@ -943,24 +1051,24 @@ TEST(OwningGroup, SortReverse) { | ||||
|         return lhs.value < rhs.value; | ||||
|     }); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[2]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[1]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[0]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[3]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[4u], entities[4]); | ||||
|     ASSERT_EQ(group.handle().data()[0u], entities[2]); | ||||
|     ASSERT_EQ(group.handle().data()[1u], entities[1]); | ||||
|     ASSERT_EQ(group.handle().data()[2u], entities[0]); | ||||
|     ASSERT_EQ(group.handle().data()[3u], entities[3]); | ||||
|     ASSERT_EQ(group.handle().data()[4u], entities[4]); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 12); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 9); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 6); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][4u].value, 2); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 2); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<char>().raw()[0u][0u], 'c'); | ||||
|     ASSERT_EQ(group.storage<char>().raw()[0u][1u], 'b'); | ||||
|     ASSERT_EQ(group.storage<char>().raw()[0u][2u], 'a'); | ||||
|     ASSERT_EQ(group.storage<char>()->raw()[0u][0u], 'c'); | ||||
|     ASSERT_EQ(group.storage<char>()->raw()[0u][1u], 'b'); | ||||
|     ASSERT_EQ(group.storage<char>()->raw()[0u][2u], 'a'); | ||||
|  | ||||
|     ASSERT_EQ((group.get<boxed_int, char>(entities[0])), (std::make_tuple(boxed_int{6}, 'a'))); | ||||
|     ASSERT_EQ((group.get<boxed_int, char>(entities[1])), (std::make_tuple(boxed_int{9}, 'b'))); | ||||
|     ASSERT_EQ((group.get<0, 1>(entities[1])), (std::make_tuple(boxed_int{9}, 'b'))); | ||||
|     ASSERT_EQ((group.get<boxed_int, char>(entities[2])), (std::make_tuple(boxed_int{12}, 'c'))); | ||||
|  | ||||
|     ASSERT_FALSE(group.contains(entities[3])); | ||||
| @@ -998,27 +1106,27 @@ TEST(OwningGroup, SortUnordered) { | ||||
|         return std::get<1>(lhs) < std::get<1>(rhs); | ||||
|     }); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[4]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[3]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[0]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[1]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[4u], entities[2]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[5u], entities[5]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[6u], entities[6]); | ||||
|     ASSERT_EQ(group.handle().data()[0u], entities[4]); | ||||
|     ASSERT_EQ(group.handle().data()[1u], entities[3]); | ||||
|     ASSERT_EQ(group.handle().data()[2u], entities[0]); | ||||
|     ASSERT_EQ(group.handle().data()[3u], entities[1]); | ||||
|     ASSERT_EQ(group.handle().data()[4u], entities[2]); | ||||
|     ASSERT_EQ(group.handle().data()[5u], entities[5]); | ||||
|     ASSERT_EQ(group.handle().data()[6u], entities[6]); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 12); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 9); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 6); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 3); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][4u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][5u].value, 4); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][6u].value, 5); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 12); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 9); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 6); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 3); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][4u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][5u].value, 4); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][6u].value, 5); | ||||
|  | ||||
|     ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[0u]), 'e'); | ||||
|     ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[1u]), 'd'); | ||||
|     ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[2u]), 'c'); | ||||
|     ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[3u]), 'b'); | ||||
|     ASSERT_EQ(group.get<char>(group.storage<boxed_int>().data()[4u]), 'a'); | ||||
|     ASSERT_EQ(group.get<char>(group.handle().data()[0u]), 'e'); | ||||
|     ASSERT_EQ(group.get<1>(group.handle().data()[1u]), 'd'); | ||||
|     ASSERT_EQ(group.get<char>(group.handle().data()[2u]), 'c'); | ||||
|     ASSERT_EQ(group.get<1>(group.handle().data()[3u]), 'b'); | ||||
|     ASSERT_EQ(group.get<char>(group.handle().data()[4u]), 'a'); | ||||
|  | ||||
|     ASSERT_FALSE(group.contains(entities[5])); | ||||
|     ASSERT_FALSE(group.contains(entities[6])); | ||||
| @@ -1026,7 +1134,7 @@ TEST(OwningGroup, SortUnordered) { | ||||
|  | ||||
| TEST(OwningGroup, SortWithExclusionList) { | ||||
|     entt::registry registry; | ||||
|     auto group = registry.group<boxed_int>({}, entt::exclude<char>); | ||||
|     auto group = registry.group<boxed_int>(entt::get<>, entt::exclude<char>); | ||||
|  | ||||
|     entt::entity entities[5]{}; | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
| @@ -1043,20 +1151,20 @@ TEST(OwningGroup, SortWithExclusionList) { | ||||
|         return lhs < rhs; | ||||
|     }); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[0u], entities[4]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[1u], entities[3]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[2u], entities[1]); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().data()[3u], entities[0]); | ||||
|     ASSERT_EQ(group.handle().data()[0u], entities[4]); | ||||
|     ASSERT_EQ(group.handle().data()[1u], entities[3]); | ||||
|     ASSERT_EQ(group.handle().data()[2u], entities[1]); | ||||
|     ASSERT_EQ(group.handle().data()[3u], entities[0]); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][0u].value, 4); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][1u].value, 3); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][2u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>().raw()[0u][3u].value, 0); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][0u].value, 4); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][1u].value, 3); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][2u].value, 1); | ||||
|     ASSERT_EQ(group.storage<boxed_int>()->raw()[0u][3u].value, 0); | ||||
|  | ||||
|     ASSERT_EQ(group.get<boxed_int>(entities[0]).value, 0); | ||||
|     ASSERT_EQ(group.get<boxed_int>(entities[1]).value, 1); | ||||
|     ASSERT_EQ(group.get<0>(entities[1]).value, 1); | ||||
|     ASSERT_EQ(group.get<boxed_int>(entities[3]).value, 3); | ||||
|     ASSERT_EQ(group.get<boxed_int>(entities[4]).value, 4); | ||||
|     ASSERT_EQ(group.get<0>(entities[4]).value, 4); | ||||
|  | ||||
|     ASSERT_FALSE(group.contains(entities[2])); | ||||
| } | ||||
| @@ -1110,11 +1218,24 @@ TEST(OwningGroup, ConstNonConstAndAllInBetween) { | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 1u); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<0>({})), int &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<int>({})), int &>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<1>({})), const char &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<const char>({})), const char &>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<2>({})), void>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<empty_type>({})), void>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<3>({})), double &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<double>({})), double &>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<4>({})), const float &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<const float>({})), const float &>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<int, const char, double, const float>({})), std::tuple<int &, const char &, double &, const float &>>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get<int, const char, empty_type, double, const float>({})), std::tuple<int &, const char &, double &, const float &>>); | ||||
|     static_assert(std::is_same_v<decltype(group.get<0, 1, 2, 3, 4>({})), std::tuple<int &, const char &, double &, const float &>>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.get({})), std::tuple<int &, const char &, double &, const float &>>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(std::as_const(registry).group_if_exists<int>(entt::get<char>)), decltype(std::as_const(registry).group_if_exists<const int>(entt::get<const char>))>); | ||||
| @@ -1192,7 +1313,7 @@ TEST(OwningGroup, ExcludedComponents) { | ||||
|     registry.emplace<int>(e1, 1); | ||||
|     registry.emplace<char>(e1); | ||||
|  | ||||
|     const auto group = registry.group<int>({}, entt::exclude<char, double>); | ||||
|     const auto group = registry.group<int>(entt::get<>, entt::exclude<char, double>); | ||||
|  | ||||
|     const auto e2 = registry.create(); | ||||
|     registry.emplace<int>(e2, 2); | ||||
| @@ -1207,7 +1328,7 @@ TEST(OwningGroup, ExcludedComponents) { | ||||
|         if(entity == e0) { | ||||
|             ASSERT_EQ(group.get<int>(e0), 0); | ||||
|         } else if(entity == e2) { | ||||
|             ASSERT_EQ(group.get<int>(e2), 2); | ||||
|             ASSERT_EQ(group.get<0>(e2), 2); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -1225,7 +1346,7 @@ TEST(OwningGroup, ExcludedComponents) { | ||||
|         if(entity == e1) { | ||||
|             ASSERT_EQ(group.get<int>(e1), 1); | ||||
|         } else if(entity == e3) { | ||||
|             ASSERT_EQ(group.get<int>(e3), 3); | ||||
|             ASSERT_EQ(group.get<0>(e3), 3); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1263,8 +1384,8 @@ TEST(OwningGroup, EmptyAndNonEmptyTypes) { | ||||
|  | ||||
| TEST(OwningGroup, TrackEntitiesOnComponentDestruction) { | ||||
|     entt::registry registry; | ||||
|     const auto group = registry.group<int>({}, entt::exclude<char>); | ||||
|     const auto cgroup = std::as_const(registry).group_if_exists<const int>({}, entt::exclude<char>); | ||||
|     const auto group = registry.group<int>(entt::get<>, entt::exclude<char>); | ||||
|     const auto cgroup = std::as_const(registry).group_if_exists<const int>(entt::get<>, entt::exclude<char>); | ||||
|  | ||||
|     const auto entity = registry.create(); | ||||
|     registry.emplace<int>(entity); | ||||
| @@ -1403,7 +1524,7 @@ TEST(OwningGroup, SwappingValuesIsAllowed) { | ||||
|  | ||||
|     // thanks to @andranik3949 for pointing out this missing test | ||||
|     registry.view<const boxed_int>().each([](const auto entity, const auto &value) { | ||||
|         ASSERT_EQ(entt::to_integral(entity), value.value); | ||||
|         ASSERT_EQ(static_cast<int>(entt::to_integral(entity)), value.value); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -1443,26 +1564,81 @@ TEST(OwningGroup, IterableGroupAlgorithmCompatibility) { | ||||
| TEST(OwningGroup, Storage) { | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|     const auto group = registry.group<int>(entt::get<const char>); | ||||
|     auto group = registry.group<int>(entt::get<const char>, entt::exclude<double, const float>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> &>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<0u>()), entt::storage_type_t<int> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<int>()), entt::storage_type_t<int> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const int>()), entt::storage_type_t<int> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<1u>()), const entt::storage_type_t<char> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<char>()), const entt::storage_type_t<char> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const char>()), const entt::storage_type_t<char> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<2u>()), entt::storage_type_t<double> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<double>()), entt::storage_type_t<double> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const double>()), entt::storage_type_t<double> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<3u>()), const entt::storage_type_t<float> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<float>()), const entt::storage_type_t<float> *>); | ||||
|     static_assert(std::is_same_v<decltype(group.storage<const float>()), const entt::storage_type_t<float> *>); | ||||
|  | ||||
|     ASSERT_TRUE(group); | ||||
|  | ||||
|     ASSERT_NE(group.storage<int>(), nullptr); | ||||
|     ASSERT_NE(group.storage<1u>(), nullptr); | ||||
|     ASSERT_NE(group.storage<double>(), nullptr); | ||||
|     ASSERT_NE(group.storage<3u>(), nullptr); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
|  | ||||
|     group.storage<int>().emplace(entity); | ||||
|     group.storage<int>()->emplace(entity); | ||||
|     group.storage<double>()->emplace(entity); | ||||
|     registry.emplace<char>(entity); | ||||
|     registry.emplace<float>(entity); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
|     ASSERT_EQ(group.begin(), group.end()); | ||||
|     ASSERT_TRUE(group.storage<int>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<const char>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<double>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<const float>()->contains(entity)); | ||||
|     ASSERT_TRUE((registry.all_of<int, char, double, float>(entity))); | ||||
|  | ||||
|     group.storage<double>()->erase(entity); | ||||
|     registry.erase<float>(entity); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 1u); | ||||
|     ASSERT_TRUE(group.storage<int>().contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<const char>().contains(entity)); | ||||
|     ASSERT_NE(group.begin(), group.end()); | ||||
|     ASSERT_TRUE(group.storage<const int>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<char>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<const double>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<float>()->contains(entity)); | ||||
|     ASSERT_TRUE((registry.all_of<int, char>(entity))); | ||||
|     ASSERT_FALSE((registry.any_of<double, float>(entity))); | ||||
|  | ||||
|     group.storage<0u>().erase(entity); | ||||
|     group.storage<0u>()->erase(entity); | ||||
|  | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
|     ASSERT_TRUE(group.storage<1u>().contains(entity)); | ||||
|     ASSERT_FALSE((registry.all_of<int, char>(entity))); | ||||
|     ASSERT_EQ(group.begin(), group.end()); | ||||
|     ASSERT_FALSE(group.storage<0u>()->contains(entity)); | ||||
|     ASSERT_TRUE(group.storage<1u>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<2u>()->contains(entity)); | ||||
|     ASSERT_FALSE(group.storage<3u>()->contains(entity)); | ||||
|     ASSERT_TRUE((registry.all_of<char>(entity))); | ||||
|     ASSERT_FALSE((registry.any_of<int, double, float>(entity))); | ||||
|  | ||||
|     group = {}; | ||||
|  | ||||
|     ASSERT_FALSE(group); | ||||
|  | ||||
|     ASSERT_EQ(group.storage<0u>(), nullptr); | ||||
|     ASSERT_EQ(group.storage<const char>(), nullptr); | ||||
|     ASSERT_EQ(group.storage<2u>(), nullptr); | ||||
|     ASSERT_EQ(group.storage<const float>(), nullptr); | ||||
| } | ||||
|  | ||||
| ENTT_DEBUG_TEST(OwningGroupDeathTest, Overlapping) { | ||||
|     entt::registry registry; | ||||
|     registry.group<char>(entt::get<int>, entt::exclude<double>); | ||||
|  | ||||
|     ASSERT_DEATH((registry.group<char, float>(entt::get<float>, entt::exclude<double>)), ""); | ||||
|     ASSERT_DEATH(registry.group<char>(entt::get<int, float>, entt::exclude<double>), ""); | ||||
|     ASSERT_DEATH(registry.group<char>(entt::get<int>, entt::exclude<double, float>), ""); | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								external/entt/entt/test/entt/entity/handle.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										17
									
								
								external/entt/entt/test/entt/entity/handle.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -83,8 +83,8 @@ TEST(BasicHandle, Destruction) { | ||||
|     ASSERT_FALSE(handle); | ||||
|     ASSERT_FALSE(handle.valid()); | ||||
|     ASSERT_NE(handle.registry(), nullptr); | ||||
|     ASSERT_EQ(handle.entity(), entity); | ||||
|     ASSERT_EQ(registry.current(entity), typename entt::registry::version_type{}); | ||||
|     ASSERT_EQ(handle.entity(), entt::entity{entt::null}); | ||||
|  | ||||
|     handle = entt::handle{registry, registry.create()}; | ||||
|  | ||||
| @@ -98,8 +98,8 @@ TEST(BasicHandle, Destruction) { | ||||
|     ASSERT_FALSE(handle); | ||||
|     ASSERT_FALSE(handle.valid()); | ||||
|     ASSERT_NE(handle.registry(), nullptr); | ||||
|     ASSERT_EQ(handle.entity(), entity); | ||||
|     ASSERT_NE(registry.current(entity), typename entt::registry::version_type{}); | ||||
|     ASSERT_EQ(handle.entity(), entt::entity{entt::null}); | ||||
| } | ||||
|  | ||||
| TEST(BasicHandle, Comparison) { | ||||
| @@ -275,6 +275,8 @@ TEST(BasicHandle, HandleStorageIterator) { | ||||
|  | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.emplace<double>(entity); | ||||
|     // required to test the find-first initialization step | ||||
|     registry.storage<entt::entity>().erase(entity); | ||||
|  | ||||
|     auto test = [](auto iterable) { | ||||
|         auto end{iterable.begin()}; | ||||
| @@ -290,6 +292,13 @@ TEST(BasicHandle, HandleStorageIterator) { | ||||
|         ASSERT_EQ(++begin, iterable.end()); | ||||
|     }; | ||||
|  | ||||
|     test(entt::handle{registry, entity}.storage()); | ||||
|     test(entt::const_handle{std::as_const(registry), entity}.storage()); | ||||
|     const auto handle = entt::handle{registry, entity}; | ||||
|     const auto chandle = entt::const_handle{std::as_const(registry), entity}; | ||||
|  | ||||
|     ASSERT_FALSE(registry.valid(entity)); | ||||
|     ASSERT_FALSE(handle); | ||||
|     ASSERT_FALSE(chandle); | ||||
|  | ||||
|     test(handle.storage()); | ||||
|     test(chandle.storage()); | ||||
| } | ||||
|   | ||||
							
								
								
									
										49
									
								
								external/entt/entt/test/entt/entity/helper.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										49
									
								
								external/entt/entt/test/entt/entity/helper.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -16,6 +16,10 @@ struct stable_type { | ||||
|     int value; | ||||
| }; | ||||
|  | ||||
| void sigh_callback(int &value) { | ||||
|     ++value; | ||||
| } | ||||
|  | ||||
| TEST(Helper, AsView) { | ||||
|     entt::registry registry; | ||||
|     const entt::registry cregistry; | ||||
| @@ -48,7 +52,7 @@ TEST(Helper, Invoke) { | ||||
| TEST(Helper, ToEntity) { | ||||
|     entt::registry registry; | ||||
|     const entt::entity null = entt::null; | ||||
|     constexpr auto page_size = entt::component_traits<int>::page_size; | ||||
|     constexpr auto page_size = entt::storage_type_t<int>::traits_type::page_size; | ||||
|     const int value = 42; | ||||
|  | ||||
|     ASSERT_EQ(entt::to_entity(registry, 42), null); | ||||
| @@ -88,7 +92,7 @@ TEST(Helper, ToEntity) { | ||||
| TEST(Helper, ToEntityStableType) { | ||||
|     entt::registry registry; | ||||
|     const entt::entity null = entt::null; | ||||
|     constexpr auto page_size = entt::component_traits<stable_type>::page_size; | ||||
|     constexpr auto page_size = entt::storage_type_t<stable_type>::traits_type::page_size; | ||||
|     const stable_type value{42}; | ||||
|  | ||||
|     ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null); | ||||
| @@ -124,3 +128,44 @@ TEST(Helper, ToEntityStableType) { | ||||
|     ASSERT_EQ(entt::to_entity(registry, stable_type{42}), null); | ||||
|     ASSERT_EQ(entt::to_entity(registry, value), null); | ||||
| } | ||||
|  | ||||
| TEST(Helper, SighHelper) { | ||||
|     using namespace entt::literals; | ||||
|  | ||||
|     entt::registry registry{}; | ||||
|     const auto entt = registry.create(); | ||||
|     entt::sigh_helper helper{registry}; | ||||
|     int counter{}; | ||||
|  | ||||
|     ASSERT_EQ(&helper.registry(), ®istry); | ||||
|  | ||||
|     helper.with<int>() | ||||
|         .on_construct<&sigh_callback>(counter) | ||||
|         .on_update<&sigh_callback>(counter) | ||||
|         .on_destroy<&sigh_callback>(counter); | ||||
|  | ||||
|     ASSERT_EQ(counter, 0); | ||||
|  | ||||
|     registry.emplace<int>(entt); | ||||
|     registry.replace<int>(entt); | ||||
|     registry.erase<int>(entt); | ||||
|  | ||||
|     ASSERT_EQ(counter, 3); | ||||
|  | ||||
|     helper.with<double>("other"_hs) | ||||
|         .on_construct<&sigh_callback>(counter) | ||||
|         .on_update<&sigh_callback>(counter) | ||||
|         .on_destroy<&sigh_callback>(counter); | ||||
|  | ||||
|     registry.emplace<double>(entt); | ||||
|     registry.replace<double>(entt); | ||||
|     registry.erase<double>(entt); | ||||
|  | ||||
|     ASSERT_EQ(counter, 3); | ||||
|  | ||||
|     registry.storage<double>("other"_hs).emplace(entt); | ||||
|     registry.storage<double>("other"_hs).patch(entt); | ||||
|     registry.storage<double>("other"_hs).erase(entt); | ||||
|  | ||||
|     ASSERT_EQ(counter, 6); | ||||
| } | ||||
|   | ||||
| @@ -1,5 +1,7 @@ | ||||
| #include <cstddef> | ||||
| #include <utility> | ||||
| #include <gtest/gtest.h> | ||||
| #include <entt/core/type_info.hpp> | ||||
| #include <entt/entity/organizer.hpp> | ||||
| #include <entt/entity/registry.hpp> | ||||
|  | ||||
| @@ -20,7 +22,7 @@ struct clazz { | ||||
|     static void ro_int_char_with_payload(clazz &, entt::view<entt::get_t<const int, const char>>) {} | ||||
| }; | ||||
|  | ||||
| void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry ®istry) { | ||||
| void to_args_integrity(entt::view<entt::get_t<int>> view, std::size_t &value, entt::registry &) { | ||||
|     value = view.size(); | ||||
| } | ||||
|  | ||||
| @@ -363,6 +365,10 @@ TEST(Organizer, Prepare) { | ||||
|     ASSERT_FALSE(registry.ctx().contains<char>()); | ||||
|     ASSERT_FALSE(registry.ctx().contains<double>()); | ||||
|  | ||||
|     ASSERT_EQ(std::as_const(registry).storage<int>(), nullptr); | ||||
|     ASSERT_EQ(std::as_const(registry).storage<char>(), nullptr); | ||||
|     ASSERT_EQ(std::as_const(registry).storage<double>(), nullptr); | ||||
|  | ||||
|     for(auto &&vertex: graph) { | ||||
|         vertex.prepare(registry); | ||||
|     } | ||||
| @@ -370,6 +376,10 @@ TEST(Organizer, Prepare) { | ||||
|     ASSERT_FALSE(registry.ctx().contains<int>()); | ||||
|     ASSERT_FALSE(registry.ctx().contains<char>()); | ||||
|     ASSERT_TRUE(registry.ctx().contains<double>()); | ||||
|  | ||||
|     ASSERT_NE(std::as_const(registry).storage<int>(), nullptr); | ||||
|     ASSERT_NE(std::as_const(registry).storage<char>(), nullptr); | ||||
|     ASSERT_EQ(std::as_const(registry).storage<double>(), nullptr); | ||||
| } | ||||
|  | ||||
| TEST(Organizer, Dependencies) { | ||||
| @@ -428,5 +438,5 @@ TEST(Organizer, ToArgsIntegrity) { | ||||
|     auto graph = organizer.graph(); | ||||
|     graph[0u].callback()(graph[0u].data(), registry); | ||||
|  | ||||
|     ASSERT_EQ(registry.ctx().at<std::size_t>(), 0u); | ||||
|     ASSERT_EQ(registry.ctx().get<std::size_t>(), 0u); | ||||
| } | ||||
|   | ||||
							
								
								
									
										457
									
								
								external/entt/entt/test/entt/entity/registry.cpp
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										457
									
								
								external/entt/entt/test/entt/entity/registry.cpp
									
									
									
									
										vendored
									
									
								
							| @@ -40,18 +40,16 @@ struct aggregate { | ||||
| }; | ||||
|  | ||||
| struct listener { | ||||
|     template<typename Component> | ||||
|     template<typename Type> | ||||
|     static void sort(entt::registry ®istry) { | ||||
|         registry.sort<Component>([](auto lhs, auto rhs) { return lhs < rhs; }); | ||||
|         registry.sort<Type>([](auto lhs, auto rhs) { return lhs < rhs; }); | ||||
|     } | ||||
|  | ||||
|     template<typename Component> | ||||
|     void incr(const entt::registry &, entt::entity entity) { | ||||
|         last = entity; | ||||
|         ++counter; | ||||
|     } | ||||
|  | ||||
|     template<typename Component> | ||||
|     void decr(const entt::registry &, entt::entity entity) { | ||||
|         last = entity; | ||||
|         --counter; | ||||
| @@ -75,11 +73,11 @@ struct destruction_order { | ||||
|     destruction_order(const entt::registry &ref, bool &ctx) | ||||
|         : registry{&ref}, | ||||
|           ctx_check{&ctx} { | ||||
|         *ctx_check = (registry->ctx().find<int>() != nullptr); | ||||
|         *ctx_check = (registry->ctx().find<ctx_check_type>() != nullptr); | ||||
|     } | ||||
|  | ||||
|     ~destruction_order() { | ||||
|         *ctx_check = *ctx_check && (registry->ctx().find<int>() != nullptr); | ||||
|         *ctx_check = *ctx_check && (registry->ctx().find<ctx_check_type>() != nullptr); | ||||
|     } | ||||
|  | ||||
| private: | ||||
| @@ -87,6 +85,22 @@ private: | ||||
|     bool *ctx_check{}; | ||||
| }; | ||||
|  | ||||
| enum class small_entity : std::uint32_t {}; | ||||
|  | ||||
| struct small_entity_traits { | ||||
|     using value_type = small_entity; | ||||
|     using entity_type = uint32_t; | ||||
|     using version_type = uint16_t; | ||||
|     static constexpr entity_type entity_mask = 0xFF; | ||||
|     static constexpr entity_type version_mask = 0x00; | ||||
| }; | ||||
|  | ||||
| template<> | ||||
| struct entt::entt_traits<small_entity>: entt::basic_entt_traits<small_entity_traits> { | ||||
|     using base_type = entt::basic_entt_traits<small_entity_traits>; | ||||
|     static constexpr auto page_size = ENTT_SPARSE_PAGE; | ||||
| }; | ||||
|  | ||||
| TEST(Registry, Context) { | ||||
|     entt::registry registry; | ||||
|     auto &ctx = registry.ctx(); | ||||
| @@ -126,8 +140,8 @@ TEST(Registry, Context) { | ||||
|  | ||||
|     ASSERT_EQ(ctx.emplace<const int>(0), 42); | ||||
|     ASSERT_EQ(ctx.find<const int>(), cctx.find<int>()); | ||||
|     ASSERT_EQ(ctx.at<int>(), cctx.at<const int>()); | ||||
|     ASSERT_EQ(ctx.at<int>(), 42); | ||||
|     ASSERT_EQ(ctx.get<int>(), cctx.get<const int>()); | ||||
|     ASSERT_EQ(ctx.get<int>(), 42); | ||||
|  | ||||
|     ASSERT_EQ(ctx.find<double>(), nullptr); | ||||
|     ASSERT_EQ(cctx.find<double>(), nullptr); | ||||
| @@ -139,8 +153,8 @@ TEST(Registry, Context) { | ||||
|  | ||||
|     ASSERT_EQ(ctx.insert_or_assign<const int>(0), 0); | ||||
|     ASSERT_EQ(ctx.find<const int>(), cctx.find<int>()); | ||||
|     ASSERT_EQ(ctx.at<int>(), cctx.at<const int>()); | ||||
|     ASSERT_EQ(ctx.at<int>(), 0); | ||||
|     ASSERT_EQ(ctx.get<int>(), cctx.get<const int>()); | ||||
|     ASSERT_EQ(ctx.get<int>(), 0); | ||||
| } | ||||
|  | ||||
| TEST(Registry, ContextHint) { | ||||
| @@ -376,13 +390,12 @@ TEST(Registry, Functionalities) { | ||||
| TEST(Registry, Constructors) { | ||||
|     entt::registry registry; | ||||
|     entt::registry other{42}; | ||||
|     const entt::entity entity = entt::tombstone; | ||||
|  | ||||
|     ASSERT_TRUE(registry.empty()); | ||||
|     ASSERT_TRUE(other.empty()); | ||||
|  | ||||
|     ASSERT_EQ(registry.released(), entity); | ||||
|     ASSERT_EQ(other.released(), entity); | ||||
|     ASSERT_EQ(registry.released(), 0u); | ||||
|     ASSERT_EQ(other.released(), 0u); | ||||
| } | ||||
|  | ||||
| TEST(Registry, Move) { | ||||
| @@ -486,6 +499,8 @@ TEST(Registry, Identifiers) { | ||||
| } | ||||
|  | ||||
| TEST(Registry, Data) { | ||||
|     using traits_type = entt::entt_traits<entt::entity>; | ||||
|  | ||||
|     entt::registry registry; | ||||
|  | ||||
|     ASSERT_EQ(std::as_const(registry).data(), nullptr); | ||||
| @@ -497,8 +512,8 @@ TEST(Registry, Data) { | ||||
|     const auto other = registry.create(); | ||||
|     registry.release(entity); | ||||
|  | ||||
|     ASSERT_NE(*std::as_const(registry).data(), entity); | ||||
|     ASSERT_EQ(*(std::as_const(registry).data() + 1u), other); | ||||
|     ASSERT_EQ(*std::as_const(registry).data(), other); | ||||
|     ASSERT_EQ(*(std::as_const(registry).data() + 1u), traits_type::next(entity)); | ||||
| } | ||||
|  | ||||
| TEST(Registry, CreateManyEntitiesAtOnce) { | ||||
| @@ -533,7 +548,7 @@ TEST(Registry, CreateManyEntitiesAtOnceWithListener) { | ||||
|     entt::entity entities[3]; | ||||
|     listener listener; | ||||
|  | ||||
|     registry.on_construct<int>().connect<&listener::incr<int>>(listener); | ||||
|     registry.on_construct<int>().connect<&listener::incr>(listener); | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|     registry.insert(std::begin(entities), std::end(entities), 42); | ||||
|     registry.insert(std::begin(entities), std::end(entities), 'c'); | ||||
| @@ -542,8 +557,8 @@ TEST(Registry, CreateManyEntitiesAtOnceWithListener) { | ||||
|     ASSERT_EQ(registry.get<char>(entities[1]), 'c'); | ||||
|     ASSERT_EQ(listener.counter, 3); | ||||
|  | ||||
|     registry.on_construct<int>().disconnect<&listener::incr<int>>(listener); | ||||
|     registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener); | ||||
|     registry.on_construct<int>().disconnect<&listener::incr>(listener); | ||||
|     registry.on_construct<empty_type>().connect<&listener::incr>(listener); | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|     registry.insert(std::begin(entities), std::end(entities), 'a'); | ||||
|     registry.insert<empty_type>(std::begin(entities), std::end(entities)); | ||||
| @@ -560,8 +575,9 @@ TEST(Registry, CreateWithHint) { | ||||
|     auto e3 = registry.create(entt::entity{3}); | ||||
|     auto e2 = registry.create(entt::entity{3}); | ||||
|  | ||||
|     ASSERT_EQ(e2, entt::entity{2}); | ||||
|     ASSERT_FALSE(registry.valid(entt::entity{1})); | ||||
|     ASSERT_EQ(e2, entt::entity{1}); | ||||
|     ASSERT_FALSE(registry.valid(entt::entity{0})); | ||||
|     ASSERT_FALSE(registry.valid(entt::entity{2})); | ||||
|     ASSERT_EQ(e3, entt::entity{3}); | ||||
|  | ||||
|     registry.release(e2); | ||||
| @@ -572,10 +588,10 @@ TEST(Registry, CreateWithHint) { | ||||
|     e2 = registry.create(); | ||||
|     auto e1 = registry.create(entt::entity{2}); | ||||
|  | ||||
|     ASSERT_EQ(traits_type::to_entity(e2), 2u); | ||||
|     ASSERT_EQ(traits_type::to_entity(e2), 1u); | ||||
|     ASSERT_EQ(traits_type::to_version(e2), 1u); | ||||
|  | ||||
|     ASSERT_EQ(traits_type::to_entity(e1), 1u); | ||||
|     ASSERT_EQ(traits_type::to_entity(e1), 2u); | ||||
|     ASSERT_EQ(traits_type::to_version(e1), 0u); | ||||
|  | ||||
|     registry.release(e1); | ||||
| @@ -640,6 +656,14 @@ TEST(Registry, CreateDestroyReleaseCornerCase) { | ||||
|     ASSERT_EQ(registry.current(e1), 1u); | ||||
| } | ||||
|  | ||||
| ENTT_DEBUG_TEST(RegistryDeathTest, CreateTooManyEntities) { | ||||
|     entt::basic_registry<small_entity> registry; | ||||
|     std::vector<small_entity> entities(entt::entt_traits<small_entity>::to_entity(entt::null)); | ||||
|     registry.create(entities.begin(), entities.end()); | ||||
|  | ||||
|     ASSERT_DEATH([[maybe_unused]] const auto entity = registry.create(), ""); | ||||
| } | ||||
|  | ||||
| TEST(Registry, DestroyVersion) { | ||||
|     entt::registry registry; | ||||
|  | ||||
| @@ -666,7 +690,7 @@ ENTT_DEBUG_TEST(RegistryDeathTest, DestroyVersion) { | ||||
|     ASSERT_DEATH(registry.destroy(entity, 3), ""); | ||||
| } | ||||
|  | ||||
| TEST(Registry, RangeDestroy) { | ||||
| TEST(Registry, DestroyRange) { | ||||
|     entt::registry registry; | ||||
|     const auto iview = registry.view<int>(); | ||||
|     const auto icview = registry.view<int, char>(); | ||||
| @@ -722,6 +746,24 @@ TEST(Registry, RangeDestroy) { | ||||
|     ASSERT_FALSE(registry.valid(entities[1u])); | ||||
|     ASSERT_FALSE(registry.valid(entities[2u])); | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 0u); | ||||
|  | ||||
|     entt::sparse_set managed{}; | ||||
|  | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|     managed.push(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<int>(managed.begin(), managed.end()); | ||||
|  | ||||
|     ASSERT_TRUE(registry.valid(managed[0u])); | ||||
|     ASSERT_TRUE(registry.valid(managed[1u])); | ||||
|     ASSERT_TRUE(registry.valid(managed[2u])); | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 3u); | ||||
|  | ||||
|     registry.destroy(managed.begin(), managed.end()); | ||||
|  | ||||
|     ASSERT_FALSE(registry.valid(managed[0u])); | ||||
|     ASSERT_FALSE(registry.valid(managed[1u])); | ||||
|     ASSERT_FALSE(registry.valid(managed[2u])); | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 0u); | ||||
| } | ||||
|  | ||||
| TEST(Registry, StableDestroy) { | ||||
| @@ -792,7 +834,7 @@ ENTT_DEBUG_TEST(RegistryDeathTest, ReleaseVersion) { | ||||
|     ASSERT_DEATH(registry.release(entity, 3), ""); | ||||
| } | ||||
|  | ||||
| TEST(Registry, RangeRelease) { | ||||
| TEST(Registry, ReleaseRange) { | ||||
|     entt::registry registry; | ||||
|     entt::entity entities[3u]; | ||||
|  | ||||
| @@ -930,10 +972,12 @@ TEST(Registry, Orphans) { | ||||
|  | ||||
| TEST(Registry, View) { | ||||
|     entt::registry registry; | ||||
|     auto mview = registry.view<int, char>(); | ||||
|     entt::entity entities[3u]; | ||||
|  | ||||
|     auto iview = registry.view<int>(); | ||||
|     auto cview = registry.view<char>(); | ||||
|     entt::entity entities[3u]; | ||||
|     auto mview = registry.view<int, char>(); | ||||
|     auto fview = registry.view<int>(entt::exclude<char>); | ||||
|  | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|  | ||||
| @@ -948,10 +992,55 @@ TEST(Registry, View) { | ||||
|     ASSERT_EQ(iview.size(), 3u); | ||||
|     ASSERT_EQ(cview.size(), 2u); | ||||
|  | ||||
|     std::size_t cnt{}; | ||||
|     mview.each([&cnt](auto...) { ++cnt; }); | ||||
|     ASSERT_EQ(mview.size_hint(), 3u); | ||||
|     ASSERT_EQ(fview.size_hint(), 3u); | ||||
|  | ||||
|     ASSERT_EQ(cnt, 2u); | ||||
|     mview.refresh(); | ||||
|     fview.refresh(); | ||||
|  | ||||
|     ASSERT_EQ(mview.size_hint(), 2u); | ||||
|     ASSERT_EQ(fview.size_hint(), 3u); | ||||
|  | ||||
|     ASSERT_NE(mview.begin(), mview.end()); | ||||
|     ASSERT_NE(fview.begin(), fview.end()); | ||||
|  | ||||
|     ASSERT_EQ(std::distance(mview.begin(), mview.end()), 2); | ||||
|     ASSERT_EQ(std::distance(fview.begin(), fview.end()), 1); | ||||
|  | ||||
|     mview.each([&entities, first = true](auto entity, auto &&...) mutable { | ||||
|         ASSERT_EQ(entity, entities[2u * first]); | ||||
|         first = false; | ||||
|     }); | ||||
|  | ||||
|     fview.each([&entities](auto entity, auto &&...) { | ||||
|         ASSERT_EQ(entity, entities[1u]); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Registry, ExcludeOnlyView) { | ||||
|     entt::registry registry; | ||||
|     entt::entity entities[4u]; | ||||
|  | ||||
|     auto view = registry.view<entt::entity>(entt::exclude<int>); | ||||
|  | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|  | ||||
|     registry.emplace<int>(entities[0u], 0); | ||||
|     registry.emplace<int>(entities[2u], 0); | ||||
|     registry.emplace<int>(entities[3u], 0); | ||||
|  | ||||
|     registry.destroy(entities[3u]); | ||||
|  | ||||
|     ASSERT_EQ(view.size_hint(), 4u); | ||||
|     ASSERT_NE(view.begin(), view.end()); | ||||
|  | ||||
|     // returns all matching identifiers, both in-use and available ones | ||||
|     ASSERT_EQ(std::distance(view.begin(), view.end()), 2); | ||||
|  | ||||
|     // skips available identifiers automatically, only returns in-use elements | ||||
|     view.each([&entities](auto entity, auto &&...) { | ||||
|         ASSERT_EQ(entity, entities[1u]); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| TEST(Registry, NonOwningGroupInitOnFirstUse) { | ||||
| @@ -964,7 +1053,7 @@ TEST(Registry, NonOwningGroupInitOnFirstUse) { | ||||
|     registry.emplace<char>(entities[2u], 'c'); | ||||
|  | ||||
|     std::size_t cnt{}; | ||||
|     auto group = registry.group<>(entt::get<int, char>); | ||||
|     auto group = registry.group(entt::get<int, char>); | ||||
|     group.each([&cnt](auto...) { ++cnt; }); | ||||
|  | ||||
|     ASSERT_FALSE((registry.owned<int, char>())); | ||||
| @@ -974,7 +1063,7 @@ TEST(Registry, NonOwningGroupInitOnFirstUse) { | ||||
| TEST(Registry, NonOwningGroupInitOnEmplace) { | ||||
|     entt::registry registry; | ||||
|     entt::entity entities[3u]; | ||||
|     auto group = registry.group<>(entt::get<int, char>); | ||||
|     auto group = registry.group(entt::get<int, char>); | ||||
|  | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<int>(std::begin(entities), std::end(entities), 0); | ||||
| @@ -1097,7 +1186,7 @@ TEST(Registry, CleanViewAfterRemoveAndClear) { | ||||
|  | ||||
| TEST(Registry, CleanNonOwningGroupViewAfterRemoveAndClear) { | ||||
|     entt::registry registry; | ||||
|     auto group = registry.group<>(entt::get<int, char>); | ||||
|     auto group = registry.group(entt::get<int, char>); | ||||
|  | ||||
|     const auto entity = registry.create(); | ||||
|     registry.emplace<int>(entity, 0); | ||||
| @@ -1188,107 +1277,21 @@ TEST(Registry, CleanPartialOwningGroupViewAfterRemoveAndClear) { | ||||
|     ASSERT_EQ(group.size(), 0u); | ||||
| } | ||||
|  | ||||
| TEST(Registry, NestedGroups) { | ||||
| ENTT_DEBUG_TEST(RegistryDeathTest, NestedGroups) { | ||||
|     entt::registry registry; | ||||
|     entt::entity entities[10]; | ||||
|     registry.group<int, double>(entt::get<char>); | ||||
|  | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<int>(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<char>(std::begin(entities), std::end(entities)); | ||||
|     const auto g1 = registry.group<int>(entt::get<char>, entt::exclude<double>); | ||||
|     ASSERT_DEATH(registry.group<int>(entt::get<char>), ""); | ||||
|     ASSERT_DEATH(registry.group<int>(entt::get<char, double>), ""); | ||||
|     ASSERT_DEATH(registry.group<int>(entt::get<char>, entt::exclude<double>), ""); | ||||
|     ASSERT_DEATH((registry.group<int, double>()), ""); | ||||
| } | ||||
|  | ||||
|     ASSERT_TRUE(registry.sortable(g1)); | ||||
|     ASSERT_EQ(g1.size(), 10u); | ||||
| ENTT_DEBUG_TEST(RegistryDeathTest, ConflictingGroups) { | ||||
|     entt::registry registry; | ||||
|  | ||||
|     const auto g2 = registry.group<int>(entt::get<char>); | ||||
|  | ||||
|     ASSERT_TRUE(registry.sortable(g1)); | ||||
|     ASSERT_FALSE(registry.sortable(g2)); | ||||
|     ASSERT_EQ(g1.size(), 10u); | ||||
|     ASSERT_EQ(g2.size(), 10u); | ||||
|  | ||||
|     for(auto i = 0u; i < 5u; ++i) { | ||||
|         ASSERT_TRUE(g1.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g1.contains(entities[i * 2])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2])); | ||||
|         registry.emplace<double>(entities[i * 2]); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 5u); | ||||
|     ASSERT_EQ(g2.size(), 10u); | ||||
|  | ||||
|     for(auto i = 0u; i < 5u; ++i) { | ||||
|         ASSERT_TRUE(g1.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_FALSE(g1.contains(entities[i * 2])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2])); | ||||
|         registry.erase<int>(entities[i * 2 + 1]); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 0u); | ||||
|     ASSERT_EQ(g2.size(), 5u); | ||||
|  | ||||
|     const auto g3 = registry.group<int, float>(entt::get<char>, entt::exclude<double>); | ||||
|  | ||||
|     ASSERT_FALSE(registry.sortable(g1)); | ||||
|     ASSERT_FALSE(registry.sortable(g2)); | ||||
|     ASSERT_TRUE(registry.sortable(g3)); | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 0u); | ||||
|     ASSERT_EQ(g2.size(), 5u); | ||||
|     ASSERT_EQ(g3.size(), 0u); | ||||
|  | ||||
|     for(auto i = 0u; i < 5u; ++i) { | ||||
|         ASSERT_FALSE(g1.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_FALSE(g1.contains(entities[i * 2])); | ||||
|         ASSERT_FALSE(g2.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2])); | ||||
|         ASSERT_FALSE(g3.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_FALSE(g3.contains(entities[i * 2])); | ||||
|         registry.emplace<int>(entities[i * 2 + 1]); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 5u); | ||||
|     ASSERT_EQ(g2.size(), 10u); | ||||
|     ASSERT_EQ(g3.size(), 0u); | ||||
|  | ||||
|     for(auto i = 0u; i < 5u; ++i) { | ||||
|         ASSERT_TRUE(g1.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_FALSE(g1.contains(entities[i * 2])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2])); | ||||
|         ASSERT_FALSE(g3.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_FALSE(g3.contains(entities[i * 2])); | ||||
|         registry.emplace<float>(entities[i * 2]); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 5u); | ||||
|     ASSERT_EQ(g2.size(), 10u); | ||||
|     ASSERT_EQ(g3.size(), 0u); | ||||
|  | ||||
|     for(auto i = 0u; i < 5u; ++i) { | ||||
|         registry.erase<double>(entities[i * 2]); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 10u); | ||||
|     ASSERT_EQ(g2.size(), 10u); | ||||
|     ASSERT_EQ(g3.size(), 5u); | ||||
|  | ||||
|     for(auto i = 0u; i < 5u; ++i) { | ||||
|         ASSERT_TRUE(g1.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g1.contains(entities[i * 2])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g2.contains(entities[i * 2])); | ||||
|         ASSERT_FALSE(g3.contains(entities[i * 2 + 1])); | ||||
|         ASSERT_TRUE(g3.contains(entities[i * 2])); | ||||
|         registry.erase<int>(entities[i * 2 + 1]); | ||||
|         registry.erase<int>(entities[i * 2]); | ||||
|     } | ||||
|  | ||||
|     ASSERT_EQ(g1.size(), 0u); | ||||
|     ASSERT_EQ(g2.size(), 0u); | ||||
|     ASSERT_EQ(g3.size(), 0u); | ||||
|     registry.group<char>(entt::get<int>, entt::exclude<double>); | ||||
|     ASSERT_DEATH(registry.group<char>(entt::get<float>, entt::exclude<double>), ""); | ||||
| } | ||||
|  | ||||
| TEST(Registry, SortSingle) { | ||||
| @@ -1379,10 +1382,10 @@ TEST(Registry, Signals) { | ||||
|     entt::entity entities[2u]; | ||||
|     listener listener; | ||||
|  | ||||
|     registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener); | ||||
|     registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener); | ||||
|     registry.on_construct<int>().connect<&listener::incr<int>>(listener); | ||||
|     registry.on_destroy<int>().connect<&listener::decr<int>>(listener); | ||||
|     registry.on_construct<empty_type>().connect<&listener::incr>(listener); | ||||
|     registry.on_destroy<empty_type>().connect<&listener::decr>(listener); | ||||
|     registry.on_construct<int>().connect<&listener::incr>(listener); | ||||
|     registry.on_destroy<int>().connect<&listener::decr>(listener); | ||||
|  | ||||
|     registry.create(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<empty_type>(std::begin(entities), std::end(entities)); | ||||
| @@ -1400,16 +1403,16 @@ TEST(Registry, Signals) { | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, entities[0u]); | ||||
|  | ||||
|     registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(listener); | ||||
|     registry.on_destroy<int>().disconnect<&listener::decr<int>>(listener); | ||||
|     registry.on_destroy<empty_type>().disconnect<&listener::decr>(listener); | ||||
|     registry.on_destroy<int>().disconnect<&listener::decr>(listener); | ||||
|  | ||||
|     registry.erase<empty_type, int>(entities[1u]); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, entities[0u]); | ||||
|  | ||||
|     registry.on_construct<empty_type>().disconnect<&listener::incr<empty_type>>(listener); | ||||
|     registry.on_construct<int>().disconnect<&listener::incr<int>>(listener); | ||||
|     registry.on_construct<empty_type>().disconnect<&listener::incr>(listener); | ||||
|     registry.on_construct<int>().disconnect<&listener::incr>(listener); | ||||
|  | ||||
|     registry.emplace<empty_type>(entities[1u]); | ||||
|     registry.emplace<int>(entities[1u]); | ||||
| @@ -1417,8 +1420,8 @@ TEST(Registry, Signals) { | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, entities[0u]); | ||||
|  | ||||
|     registry.on_construct<int>().connect<&listener::incr<int>>(listener); | ||||
|     registry.on_destroy<int>().connect<&listener::decr<int>>(listener); | ||||
|     registry.on_construct<int>().connect<&listener::incr>(listener); | ||||
|     registry.on_destroy<int>().connect<&listener::decr>(listener); | ||||
|  | ||||
|     registry.emplace<int>(entities[0u]); | ||||
|     registry.erase<int>(entities[1u]); | ||||
| @@ -1426,8 +1429,8 @@ TEST(Registry, Signals) { | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, entities[1u]); | ||||
|  | ||||
|     registry.on_construct<empty_type>().connect<&listener::incr<empty_type>>(listener); | ||||
|     registry.on_destroy<empty_type>().connect<&listener::decr<empty_type>>(listener); | ||||
|     registry.on_construct<empty_type>().connect<&listener::incr>(listener); | ||||
|     registry.on_destroy<empty_type>().connect<&listener::decr>(listener); | ||||
|  | ||||
|     registry.erase<empty_type>(entities[1u]); | ||||
|     registry.emplace<empty_type>(entities[0u]); | ||||
| @@ -1454,8 +1457,8 @@ TEST(Registry, Signals) { | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, entities[0u]); | ||||
|  | ||||
|     registry.on_destroy<empty_type>().disconnect<&listener::decr<empty_type>>(listener); | ||||
|     registry.on_destroy<int>().disconnect<&listener::decr<int>>(listener); | ||||
|     registry.on_destroy<empty_type>().disconnect<&listener::decr>(listener); | ||||
|     registry.on_destroy<int>().disconnect<&listener::decr>(listener); | ||||
|  | ||||
|     registry.emplace_or_replace<empty_type>(entities[0u]); | ||||
|     registry.emplace_or_replace<int>(entities[0u]); | ||||
| @@ -1463,8 +1466,8 @@ TEST(Registry, Signals) { | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, entities[0u]); | ||||
|  | ||||
|     registry.on_update<empty_type>().connect<&listener::incr<empty_type>>(listener); | ||||
|     registry.on_update<int>().connect<&listener::incr<int>>(listener); | ||||
|     registry.on_update<empty_type>().connect<&listener::incr>(listener); | ||||
|     registry.on_update<int>().connect<&listener::incr>(listener); | ||||
|  | ||||
|     registry.emplace_or_replace<empty_type>(entities[0u]); | ||||
|     registry.emplace_or_replace<int>(entities[0u]); | ||||
| @@ -1479,6 +1482,84 @@ TEST(Registry, Signals) { | ||||
|     ASSERT_EQ(listener.last, entities[0u]); | ||||
| } | ||||
|  | ||||
| TEST(Registry, SignalsOnRuntimePool) { | ||||
|     using namespace entt::literals; | ||||
|  | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|     listener listener; | ||||
|  | ||||
|     registry.on_construct<int>("custom"_hs).connect<&listener::incr>(listener); | ||||
|     registry.on_update<int>("custom"_hs).connect<&listener::incr>(listener); | ||||
|     registry.on_destroy<int>("custom"_hs).connect<&listener::incr>(listener); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 0); | ||||
|  | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.patch<int>(entity); | ||||
|     registry.erase<int>(entity); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 0); | ||||
|  | ||||
|     registry.storage<int>("custom"_hs).emplace(entity); | ||||
|     registry.storage<int>("custom"_hs).patch(entity); | ||||
|     registry.storage<int>("custom"_hs).erase(entity); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 3); | ||||
| } | ||||
|  | ||||
| TEST(Registry, SignalsOnEntity) { | ||||
|     entt::registry registry; | ||||
|     listener listener; | ||||
|  | ||||
|     registry.on_construct<entt::entity>().connect<&listener::incr>(listener); | ||||
|  | ||||
|     entt::entity entity = registry.create(); | ||||
|     entt::entity other = registry.create(); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, other); | ||||
|  | ||||
|     registry.destroy(other); | ||||
|     registry.destroy(entity); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_EQ(listener.last, other); | ||||
|  | ||||
|     registry.on_construct<entt::entity>().disconnect(&listener); | ||||
|  | ||||
|     other = registry.create(); | ||||
|     entity = registry.create(); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 2); | ||||
|     ASSERT_NE(listener.last, entity); | ||||
|     ASSERT_NE(listener.last, other); | ||||
|  | ||||
|     registry.on_update<entt::entity>().connect<&listener::decr>(listener); | ||||
|     registry.patch<entt::entity>(entity); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 1); | ||||
|     ASSERT_EQ(listener.last, entity); | ||||
|  | ||||
|     registry.on_update<entt::entity>().disconnect(&listener); | ||||
|     registry.patch<entt::entity>(other); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 1); | ||||
|     ASSERT_NE(listener.last, other); | ||||
|  | ||||
|     registry.on_destroy<entt::entity>().connect<&listener::decr>(listener); | ||||
|     registry.destroy(entity); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 0); | ||||
|     ASSERT_EQ(listener.last, entity); | ||||
|  | ||||
|     registry.on_destroy<entt::entity>().disconnect(&listener); | ||||
|     registry.destroy(other); | ||||
|  | ||||
|     ASSERT_EQ(listener.counter, 0); | ||||
|     ASSERT_NE(listener.last, other); | ||||
| } | ||||
|  | ||||
| TEST(Registry, SignalWhenDestroying) { | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
| @@ -1576,6 +1657,17 @@ TEST(Registry, Erase) { | ||||
|     ASSERT_EQ(registry.storage<char>().size(), 0u); | ||||
|     ASSERT_EQ(registry.storage<double>().size(), 1u); | ||||
|  | ||||
|     registry.insert<int>(std::begin(entities) + 1, std::end(entities) - 1u); | ||||
|     registry.insert<char>(std::begin(entities) + 1, std::end(entities) - 1u); | ||||
|  | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 1u); | ||||
|     ASSERT_EQ(registry.storage<char>().size(), 1u); | ||||
|  | ||||
|     registry.erase<int, char>(iview.begin(), iview.end()); | ||||
|  | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 0u); | ||||
|     ASSERT_EQ(registry.storage<char>().size(), 0u); | ||||
|  | ||||
|     registry.insert<int>(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<char>(std::begin(entities), std::end(entities)); | ||||
|  | ||||
| @@ -1643,6 +1735,39 @@ TEST(Registry, StableErase) { | ||||
|     ASSERT_EQ(registry.storage<double>().size(), 1u); | ||||
| } | ||||
|  | ||||
| TEST(Registry, EraseIf) { | ||||
|     using namespace entt::literals; | ||||
|  | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|  | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.storage<int>("other"_hs).emplace(entity); | ||||
|     registry.emplace<char>(entity); | ||||
|  | ||||
|     ASSERT_TRUE(registry.storage<int>().contains(entity)); | ||||
|     ASSERT_TRUE(registry.storage<int>("other"_hs).contains(entity)); | ||||
|     ASSERT_TRUE(registry.storage<char>().contains(entity)); | ||||
|  | ||||
|     registry.erase_if(entity, [](auto &&...) { return false; }); | ||||
|  | ||||
|     ASSERT_TRUE(registry.storage<int>().contains(entity)); | ||||
|     ASSERT_TRUE(registry.storage<int>("other"_hs).contains(entity)); | ||||
|     ASSERT_TRUE(registry.storage<char>().contains(entity)); | ||||
|  | ||||
|     registry.erase_if(entity, [](entt::id_type id, auto &&...) { return id == "other"_hs; }); | ||||
|  | ||||
|     ASSERT_TRUE(registry.storage<int>().contains(entity)); | ||||
|     ASSERT_FALSE(registry.storage<int>("other"_hs).contains(entity)); | ||||
|     ASSERT_TRUE(registry.storage<char>().contains(entity)); | ||||
|  | ||||
|     registry.erase_if(entity, [](auto, const auto &storage) { return storage.type() == entt::type_id<char>(); }); | ||||
|  | ||||
|     ASSERT_TRUE(registry.storage<int>().contains(entity)); | ||||
|     ASSERT_FALSE(registry.storage<int>("other"_hs).contains(entity)); | ||||
|     ASSERT_FALSE(registry.storage<char>().contains(entity)); | ||||
| } | ||||
|  | ||||
| TEST(Registry, Remove) { | ||||
|     entt::registry registry; | ||||
|     const auto iview = registry.view<int>(); | ||||
| @@ -1689,6 +1814,18 @@ TEST(Registry, Remove) { | ||||
|     ASSERT_EQ(registry.storage<char>().size(), 0u); | ||||
|     ASSERT_EQ(registry.storage<double>().size(), 1u); | ||||
|  | ||||
|     registry.insert<int>(std::begin(entities) + 1, std::end(entities) - 1u); | ||||
|     registry.insert<char>(std::begin(entities) + 1, std::end(entities) - 1u); | ||||
|  | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 1u); | ||||
|     ASSERT_EQ(registry.storage<char>().size(), 1u); | ||||
|  | ||||
|     registry.remove<int, char>(iview.begin(), iview.end()); | ||||
|     registry.remove<int, char>(iview.begin(), iview.end()); | ||||
|  | ||||
|     ASSERT_EQ(registry.storage<int>().size(), 0u); | ||||
|     ASSERT_EQ(registry.storage<char>().size(), 0u); | ||||
|  | ||||
|     registry.insert<int>(std::begin(entities), std::end(entities)); | ||||
|     registry.insert<char>(std::begin(entities), std::end(entities)); | ||||
|  | ||||
| @@ -1791,7 +1928,7 @@ TEST(Registry, NonOwningGroupInterleaved) { | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.emplace<char>(entity); | ||||
|  | ||||
|     const auto group = registry.group<>(entt::get<int, char>); | ||||
|     const auto group = registry.group(entt::get<int, char>); | ||||
|  | ||||
|     entity = registry.create(); | ||||
|     registry.emplace<int>(entity); | ||||
| @@ -1845,7 +1982,7 @@ TEST(Registry, PartialOwningGroupInterleaved) { | ||||
|  | ||||
| TEST(Registry, NonOwningGroupSortInterleaved) { | ||||
|     entt::registry registry; | ||||
|     const auto group = registry.group<>(entt::get<int, char>); | ||||
|     const auto group = registry.group(entt::get<int, char>); | ||||
|  | ||||
|     const auto e0 = registry.create(); | ||||
|     registry.emplace<int>(e0, 0); | ||||
| @@ -2009,7 +2146,7 @@ TEST(Registry, ScramblingPoolsIsAllowed) { | ||||
|  | ||||
|     // thanks to @andranik3949 for pointing out this missing test | ||||
|     registry.view<const int>().each([](const auto entity, const auto &value) { | ||||
|         ASSERT_EQ(entt::to_integral(entity), value); | ||||
|         ASSERT_EQ(static_cast<int>(entt::to_integral(entity)), value); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| @@ -2021,7 +2158,7 @@ TEST(Registry, RuntimePools) { | ||||
|     const auto entity = registry.create(); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(registry.storage<empty_type>()), entt::storage_type_t<empty_type> &>); | ||||
|     static_assert(std::is_same_v<decltype(std::as_const(registry).storage<empty_type>()), const entt::storage_type_t<empty_type> &>); | ||||
|     static_assert(std::is_same_v<decltype(std::as_const(registry).storage<empty_type>()), const entt::storage_type_t<empty_type> *>); | ||||
|  | ||||
|     static_assert(std::is_same_v<decltype(registry.storage("other"_hs)), entt::storage_type_t<empty_type>::base_type *>); | ||||
|     static_assert(std::is_same_v<decltype(std::as_const(registry).storage("other"_hs)), const entt::storage_type_t<empty_type>::base_type *>); | ||||
| @@ -2030,7 +2167,7 @@ TEST(Registry, RuntimePools) { | ||||
|     ASSERT_EQ(std::as_const(registry).storage("rehto"_hs), nullptr); | ||||
|  | ||||
|     ASSERT_EQ(®istry.storage<empty_type>("other"_hs), &storage); | ||||
|     ASSERT_NE(&std::as_const(registry).storage<empty_type>(), &storage); | ||||
|     ASSERT_NE(std::as_const(registry).storage<empty_type>(), &storage); | ||||
|  | ||||
|     ASSERT_FALSE(registry.any_of<empty_type>(entity)); | ||||
|     ASSERT_FALSE(storage.contains(entity)); | ||||
| @@ -2070,6 +2207,7 @@ TEST(Registry, Storage) { | ||||
|  | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|  | ||||
|     auto &storage = registry.storage<int>("int"_hs); | ||||
|     storage.emplace(entity); | ||||
|  | ||||
| @@ -2149,8 +2287,7 @@ TEST(Registry, RegistryStorageIterator) { | ||||
|  | ||||
| TEST(Registry, RegistryStorageIteratorConversion) { | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|     registry.emplace<int>(entity); | ||||
|     registry.storage<int>(); | ||||
|  | ||||
|     auto proxy = registry.storage(); | ||||
|     auto cproxy = std::as_const(registry).storage(); | ||||
| @@ -2176,6 +2313,22 @@ TEST(Registry, RegistryStorageIteratorConversion) { | ||||
|     ASSERT_NE(++cit, it); | ||||
| } | ||||
|  | ||||
| TEST(Registry, VoidType) { | ||||
|     using namespace entt::literals; | ||||
|  | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|     auto &storage = registry.storage<void>("custom"_hs); | ||||
|     storage.emplace(entity); | ||||
|  | ||||
|     ASSERT_TRUE(registry.storage<void>().empty()); | ||||
|     ASSERT_FALSE(registry.storage<void>("custom"_hs).empty()); | ||||
|     ASSERT_TRUE(registry.storage<void>("custom"_hs).contains(entity)); | ||||
|  | ||||
|     ASSERT_EQ(registry.storage<void>().type(), entt::type_id<void>()); | ||||
|     ASSERT_EQ(registry.storage<void>("custom"_hs).type(), entt::type_id<void>()); | ||||
| } | ||||
|  | ||||
| TEST(Registry, NoEtoType) { | ||||
|     entt::registry registry; | ||||
|     const auto entity = registry.create(); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| #include <iterator> | ||||
| #include <gtest/gtest.h> | ||||
| #include <entt/entity/mixin.hpp> | ||||
| #include <entt/entity/registry.hpp> | ||||
| #include <entt/entity/storage.hpp> | ||||
| #include "../common/throwing_allocator.hpp" | ||||
| @@ -30,9 +31,20 @@ void listener(counter &counter, Registry &, typename Registry::entity_type) { | ||||
|     ++counter.value; | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, GenericType) { | ||||
| struct empty_each_tag final {}; | ||||
| 
 | ||||
| template<> | ||||
| struct entt::basic_storage<empty_each_tag, entt::entity, std::allocator<empty_each_tag>>: entt::basic_storage<void, entt::entity, std::allocator<void>> { | ||||
|     basic_storage(const std::allocator<empty_each_tag> &) {} | ||||
| 
 | ||||
|     [[nodiscard]] iterable each() noexcept { | ||||
|         return {internal::extended_storage_iterator{base_type::end()}, internal::extended_storage_iterator{base_type::end()}}; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| TEST(SighMixin, GenericType) { | ||||
|     entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; | ||||
|     entt::sigh_storage_mixin<entt::storage<int>> pool; | ||||
|     entt::sigh_mixin<entt::storage<int>> pool; | ||||
|     entt::sparse_set &base = pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
| @@ -40,10 +52,21 @@ TEST(SighStorageMixin, GenericType) { | ||||
|     counter on_destroy{}; | ||||
| 
 | ||||
|     pool.bind(entt::forward_as_any(registry)); | ||||
| 
 | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| 
 | ||||
|     pool.insert(entities, entities + 1u); | ||||
|     pool.erase(entities[0u]); | ||||
| 
 | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| 
 | ||||
|     ASSERT_EQ(on_construct.value, 0); | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
| 
 | ||||
|     pool.on_construct().connect<&listener<entt::registry>>(on_construct); | ||||
|     pool.on_destroy().connect<&listener<entt::registry>>(on_destroy); | ||||
| 
 | ||||
|     ASSERT_NE(base.emplace(entities[0u]), base.end()); | ||||
|     ASSERT_NE(base.push(entities[0u]), base.end()); | ||||
| 
 | ||||
|     pool.emplace(entities[1u]); | ||||
| 
 | ||||
| @@ -61,7 +84,7 @@ TEST(SighStorageMixin, GenericType) { | ||||
|     ASSERT_EQ(on_destroy.value, 2); | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| 
 | ||||
|     ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end()); | ||||
|     ASSERT_NE(base.push(std::begin(entities), std::end(entities)), base.end()); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.get(entities[0u]), 0); | ||||
|     ASSERT_EQ(pool.get(entities[1u]), 0); | ||||
| @@ -95,9 +118,9 @@ TEST(SighStorageMixin, GenericType) { | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, StableType) { | ||||
| TEST(SighMixin, StableType) { | ||||
|     entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; | ||||
|     entt::sigh_storage_mixin<entt::storage<stable_type>> pool; | ||||
|     entt::sigh_mixin<entt::storage<stable_type>> pool; | ||||
|     entt::sparse_set &base = pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
| @@ -108,7 +131,7 @@ TEST(SighStorageMixin, StableType) { | ||||
|     pool.on_construct().connect<&listener<entt::registry>>(on_construct); | ||||
|     pool.on_destroy().connect<&listener<entt::registry>>(on_destroy); | ||||
| 
 | ||||
|     ASSERT_NE(base.emplace(entities[0u]), base.end()); | ||||
|     ASSERT_NE(base.push(entities[0u]), base.end()); | ||||
| 
 | ||||
|     pool.emplace(entities[1u]); | ||||
| 
 | ||||
| @@ -126,7 +149,7 @@ TEST(SighStorageMixin, StableType) { | ||||
|     ASSERT_EQ(on_destroy.value, 2); | ||||
|     ASSERT_FALSE(pool.empty()); | ||||
| 
 | ||||
|     ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end()); | ||||
|     ASSERT_NE(base.push(std::begin(entities), std::end(entities)), base.end()); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.get(entities[0u]).value, 0); | ||||
|     ASSERT_EQ(pool.get(entities[1u]).value, 0); | ||||
| @@ -160,9 +183,9 @@ TEST(SighStorageMixin, StableType) { | ||||
|     ASSERT_FALSE(pool.empty()); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, EmptyType) { | ||||
| TEST(SighMixin, EmptyType) { | ||||
|     entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; | ||||
|     entt::sigh_storage_mixin<entt::storage<empty_type>> pool; | ||||
|     entt::sigh_mixin<entt::storage<empty_type>> pool; | ||||
|     entt::sparse_set &base = pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
| @@ -173,7 +196,7 @@ TEST(SighStorageMixin, EmptyType) { | ||||
|     pool.on_construct().connect<&listener<entt::registry>>(on_construct); | ||||
|     pool.on_destroy().connect<&listener<entt::registry>>(on_destroy); | ||||
| 
 | ||||
|     ASSERT_NE(base.emplace(entities[0u]), base.end()); | ||||
|     ASSERT_NE(base.push(entities[0u]), base.end()); | ||||
| 
 | ||||
|     pool.emplace(entities[1u]); | ||||
| 
 | ||||
| @@ -191,7 +214,7 @@ TEST(SighStorageMixin, EmptyType) { | ||||
|     ASSERT_EQ(on_destroy.value, 2); | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| 
 | ||||
|     ASSERT_NE(base.insert(std::begin(entities), std::end(entities)), base.end()); | ||||
|     ASSERT_NE(base.push(std::begin(entities), std::end(entities)), base.end()); | ||||
| 
 | ||||
|     ASSERT_TRUE(pool.contains(entities[0u])); | ||||
|     ASSERT_TRUE(pool.contains(entities[1u])); | ||||
| @@ -225,9 +248,9 @@ TEST(SighStorageMixin, EmptyType) { | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, NonDefaultConstructibleType) { | ||||
| TEST(SighMixin, NonDefaultConstructibleType) { | ||||
|     entt::entity entities[2u]{entt::entity{3}, entt::entity{42}}; | ||||
|     entt::sigh_storage_mixin<entt::storage<non_default_constructible>> pool; | ||||
|     entt::sigh_mixin<entt::storage<non_default_constructible>> pool; | ||||
|     entt::sparse_set &base = pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
| @@ -238,12 +261,12 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) { | ||||
|     pool.on_construct().connect<&listener<entt::registry>>(on_construct); | ||||
|     pool.on_destroy().connect<&listener<entt::registry>>(on_destroy); | ||||
| 
 | ||||
|     ASSERT_EQ(base.emplace(entities[0u]), base.end()); | ||||
|     ASSERT_EQ(base.push(entities[0u]), base.end()); | ||||
| 
 | ||||
|     pool.emplace(entities[1u], 3); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.size(), 1u); | ||||
|     ASSERT_EQ(on_construct.value, 2); | ||||
|     ASSERT_EQ(on_construct.value, 1); | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
|     ASSERT_FALSE(pool.empty()); | ||||
| 
 | ||||
| @@ -253,11 +276,11 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) { | ||||
|     base.erase(entities[1u]); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.size(), 0u); | ||||
|     ASSERT_EQ(on_construct.value, 2); | ||||
|     ASSERT_EQ(on_construct.value, 1); | ||||
|     ASSERT_EQ(on_destroy.value, 1); | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| 
 | ||||
|     ASSERT_EQ(base.insert(std::begin(entities), std::end(entities)), base.end()); | ||||
|     ASSERT_EQ(base.push(std::begin(entities), std::end(entities)), base.end()); | ||||
| 
 | ||||
|     ASSERT_FALSE(pool.contains(entities[0u])); | ||||
|     ASSERT_FALSE(pool.contains(entities[1u])); | ||||
| @@ -266,7 +289,7 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) { | ||||
|     pool.insert(std::begin(entities), std::end(entities), 3); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.size(), 2u); | ||||
|     ASSERT_EQ(on_construct.value, 6); | ||||
|     ASSERT_EQ(on_construct.value, 3); | ||||
|     ASSERT_EQ(on_destroy.value, 1); | ||||
|     ASSERT_FALSE(pool.empty()); | ||||
| 
 | ||||
| @@ -276,13 +299,13 @@ TEST(SighStorageMixin, NonDefaultConstructibleType) { | ||||
|     pool.erase(std::begin(entities), std::end(entities)); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.size(), 0u); | ||||
|     ASSERT_EQ(on_construct.value, 6); | ||||
|     ASSERT_EQ(on_construct.value, 3); | ||||
|     ASSERT_EQ(on_destroy.value, 3); | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, VoidType) { | ||||
|     entt::sigh_storage_mixin<entt::storage<void>> pool; | ||||
| TEST(SighMixin, VoidType) { | ||||
|     entt::sigh_mixin<entt::storage<void>> pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
|     counter on_construct{}; | ||||
| @@ -297,7 +320,7 @@ TEST(SighStorageMixin, VoidType) { | ||||
|     ASSERT_EQ(pool.type(), entt::type_id<void>()); | ||||
|     ASSERT_TRUE(pool.contains(entt::entity{99})); | ||||
| 
 | ||||
|     entt::sigh_storage_mixin<entt::storage<void>> other{std::move(pool)}; | ||||
|     entt::sigh_mixin<entt::storage<void>> other{std::move(pool)}; | ||||
| 
 | ||||
|     ASSERT_FALSE(pool.contains(entt::entity{99})); | ||||
|     ASSERT_TRUE(other.contains(entt::entity{99})); | ||||
| @@ -313,8 +336,8 @@ TEST(SighStorageMixin, VoidType) { | ||||
|     ASSERT_EQ(on_destroy.value, 1); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, Move) { | ||||
|     entt::sigh_storage_mixin<entt::storage<int>> pool; | ||||
| TEST(SighMixin, Move) { | ||||
|     entt::sigh_mixin<entt::storage<int>> pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
|     counter on_construct{}; | ||||
| @@ -330,7 +353,7 @@ TEST(SighStorageMixin, Move) { | ||||
|     ASSERT_TRUE(std::is_move_assignable_v<decltype(pool)>); | ||||
|     ASSERT_EQ(pool.type(), entt::type_id<int>()); | ||||
| 
 | ||||
|     entt::sigh_storage_mixin<entt::storage<int>> other{std::move(pool)}; | ||||
|     entt::sigh_mixin<entt::storage<int>> other{std::move(pool)}; | ||||
| 
 | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
|     ASSERT_FALSE(other.empty()); | ||||
| @@ -347,7 +370,7 @@ TEST(SighStorageMixin, Move) { | ||||
|     ASSERT_EQ(pool.get(entt::entity{3}), 3); | ||||
|     ASSERT_EQ(other.at(0u), static_cast<entt::entity>(entt::null)); | ||||
| 
 | ||||
|     other = entt::sigh_storage_mixin<entt::storage<int>>{}; | ||||
|     other = entt::sigh_mixin<entt::storage<int>>{}; | ||||
|     other.bind(entt::forward_as_any(registry)); | ||||
| 
 | ||||
|     other.emplace(entt::entity{42}, 42); | ||||
| @@ -365,9 +388,9 @@ TEST(SighStorageMixin, Move) { | ||||
|     ASSERT_EQ(on_destroy.value, 1); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, Swap) { | ||||
|     entt::sigh_storage_mixin<entt::storage<int>> pool; | ||||
|     entt::sigh_storage_mixin<entt::storage<int>> other; | ||||
| TEST(SighMixin, Swap) { | ||||
|     entt::sigh_mixin<entt::storage<int>> pool; | ||||
|     entt::sigh_mixin<entt::storage<int>> other; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
|     counter on_construct{}; | ||||
| @@ -411,7 +434,100 @@ TEST(SighStorageMixin, Swap) { | ||||
|     ASSERT_EQ(on_destroy.value, 3); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, CustomAllocator) { | ||||
| TEST(SighMixin, EmptyEachStorage) { | ||||
|     entt::sigh_mixin<entt::storage<empty_each_tag>> pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
|     counter on_destroy{}; | ||||
| 
 | ||||
|     pool.bind(entt::forward_as_any(registry)); | ||||
|     pool.on_destroy().connect<&listener<entt::registry>>(on_destroy); | ||||
| 
 | ||||
|     ASSERT_TRUE(pool.empty()); | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
| 
 | ||||
|     pool.push(entt::entity{42}); | ||||
| 
 | ||||
|     ASSERT_FALSE(pool.empty()); | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
| 
 | ||||
|     ASSERT_NE(pool.begin(), pool.end()); | ||||
|     ASSERT_EQ(pool.each().begin(), pool.each().end()); | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
| 
 | ||||
|     pool.clear(); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.begin(), pool.end()); | ||||
|     ASSERT_EQ(pool.each().begin(), pool.each().end()); | ||||
|     // no signal at all because of the (fake) empty iterable
 | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
| } | ||||
| 
 | ||||
| TEST(SighMixin, StorageEntity) { | ||||
|     using traits_type = entt::entt_traits<entt::entity>; | ||||
| 
 | ||||
|     entt::sigh_mixin<entt::storage<entt::entity>> pool; | ||||
|     entt::registry registry; | ||||
| 
 | ||||
|     counter on_construct{}; | ||||
|     counter on_destroy{}; | ||||
| 
 | ||||
|     pool.bind(entt::forward_as_any(registry)); | ||||
|     pool.on_construct().connect<&listener<entt::registry>>(on_construct); | ||||
|     pool.on_destroy().connect<&listener<entt::registry>>(on_destroy); | ||||
| 
 | ||||
|     pool.push(entt::entity{1}); | ||||
| 
 | ||||
|     ASSERT_EQ(on_construct.value, 1); | ||||
|     ASSERT_EQ(on_destroy.value, 0); | ||||
|     ASSERT_EQ(pool.size(), 2u); | ||||
|     ASSERT_EQ(pool.in_use(), 1u); | ||||
| 
 | ||||
|     pool.erase(entt::entity{1}); | ||||
| 
 | ||||
|     ASSERT_EQ(on_construct.value, 1); | ||||
|     ASSERT_EQ(on_destroy.value, 1); | ||||
|     ASSERT_EQ(pool.size(), 2u); | ||||
|     ASSERT_EQ(pool.in_use(), 0u); | ||||
| 
 | ||||
|     pool.push(traits_type::construct(0, 2)); | ||||
|     pool.push(traits_type::construct(2, 1)); | ||||
| 
 | ||||
|     ASSERT_TRUE(pool.contains(traits_type::construct(0, 2))); | ||||
|     ASSERT_TRUE(pool.contains(traits_type::construct(1, 1))); | ||||
|     ASSERT_TRUE(pool.contains(traits_type::construct(2, 1))); | ||||
| 
 | ||||
|     ASSERT_EQ(on_construct.value, 3); | ||||
|     ASSERT_EQ(on_destroy.value, 1); | ||||
|     ASSERT_EQ(pool.size(), 3u); | ||||
|     ASSERT_EQ(pool.in_use(), 2u); | ||||
| 
 | ||||
|     pool.clear(); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.size(), 0u); | ||||
|     ASSERT_EQ(pool.in_use(), 0u); | ||||
| 
 | ||||
|     ASSERT_EQ(on_construct.value, 3); | ||||
|     ASSERT_EQ(on_destroy.value, 3); | ||||
| 
 | ||||
|     pool.emplace(); | ||||
|     pool.emplace(entt::entity{0}); | ||||
| 
 | ||||
|     entt::entity entities[1u]{}; | ||||
|     pool.insert(entities, entities + 1u); | ||||
| 
 | ||||
|     ASSERT_EQ(on_construct.value, 6); | ||||
|     ASSERT_EQ(on_destroy.value, 3); | ||||
|     ASSERT_EQ(pool.size(), 3u); | ||||
|     ASSERT_EQ(pool.in_use(), 3u); | ||||
| 
 | ||||
|     pool.clear(); | ||||
| 
 | ||||
|     ASSERT_EQ(pool.size(), 0u); | ||||
|     ASSERT_EQ(pool.in_use(), 0u); | ||||
| } | ||||
| 
 | ||||
| TEST(SighMixin, CustomAllocator) { | ||||
|     auto test = [](auto pool, auto alloc) { | ||||
|         using registry_type = typename decltype(pool)::registry_type; | ||||
|         registry_type registry; | ||||
| @@ -466,12 +582,12 @@ TEST(SighStorageMixin, CustomAllocator) { | ||||
| 
 | ||||
|     test::throwing_allocator<entt::entity> allocator{}; | ||||
| 
 | ||||
|     test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{allocator}, allocator); | ||||
|     test(entt::sigh_storage_mixin<entt::basic_storage<std::true_type, entt::entity, test::throwing_allocator<std::true_type>>>{allocator}, allocator); | ||||
|     test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{allocator}, allocator); | ||||
|     test(entt::sigh_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{allocator}, allocator); | ||||
|     test(entt::sigh_mixin<entt::basic_storage<std::true_type, entt::entity, test::throwing_allocator<std::true_type>>>{allocator}, allocator); | ||||
|     test(entt::sigh_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{allocator}, allocator); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, ThrowingAllocator) { | ||||
| TEST(SighMixin, ThrowingAllocator) { | ||||
|     auto test = [](auto pool) { | ||||
|         using pool_allocator_type = typename decltype(pool)::allocator_type; | ||||
|         using value_type = typename decltype(pool)::value_type; | ||||
| @@ -511,7 +627,7 @@ TEST(SighStorageMixin, ThrowingAllocator) { | ||||
| 
 | ||||
|         test::throwing_allocator<entt::entity>::trigger_on_allocate = true; | ||||
| 
 | ||||
|         ASSERT_THROW(base.emplace(entt::entity{0}), test::throwing_allocator<entt::entity>::exception_type); | ||||
|         ASSERT_THROW(base.push(entt::entity{0}), test::throwing_allocator<entt::entity>::exception_type); | ||||
|         ASSERT_FALSE(base.contains(entt::entity{0})); | ||||
|         ASSERT_TRUE(base.empty()); | ||||
| 
 | ||||
| @@ -543,12 +659,12 @@ TEST(SighStorageMixin, ThrowingAllocator) { | ||||
|         ASSERT_EQ(on_destroy.value, 1); | ||||
|     }; | ||||
| 
 | ||||
|     test(entt::sigh_storage_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{}); | ||||
|     test(entt::sigh_storage_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{}); | ||||
|     test(entt::sigh_mixin<entt::basic_storage<int, entt::entity, test::throwing_allocator<int>>>{}); | ||||
|     test(entt::sigh_mixin<entt::basic_storage<stable_type, entt::entity, test::throwing_allocator<stable_type>>>{}); | ||||
| } | ||||
| 
 | ||||
| TEST(SighStorageMixin, ThrowingComponent) { | ||||
|     entt::sigh_storage_mixin<entt::storage<test::throwing_type>> pool; | ||||
| TEST(SighMixin, ThrowingComponent) { | ||||
|     entt::sigh_mixin<entt::storage<test::throwing_type>> pool; | ||||
|     using registry_type = typename decltype(pool)::registry_type; | ||||
|     registry_type registry; | ||||
| 
 | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user