mirror of
https://github.com/godotengine/godot-cpp.git
synced 2025-12-31 01:48:45 +03:00
Merge pull request #1871 from dsnopek/4.4-cherrypicks-4
Cherry-picks for the godot-cpp 4.4 branch - 4th batch
This commit is contained in:
@@ -21,7 +21,8 @@ compromises need to be made to resolve those differences.
|
||||
As we are attempting to maintain feature parity, and ease of maintenance, these
|
||||
CMake scripts are built to resemble the SCons build system wherever possible.
|
||||
Where they are not, we will attempt to document common difference in
|
||||
doc/cmake.rst and platform specific differences in their respective
|
||||
the docs (https://docs.godotengine.org/en/latest/tutorials/scripting/cpp/build_system/cmake.html)
|
||||
and platform specific differences in their respective
|
||||
cmake/<platform>.cmake file.
|
||||
|
||||
The file structure and file content are made to match, if not in content then
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
> from Godot's `master` branch.
|
||||
>
|
||||
> For users of stable branches, switch to the branch matching your target Godot version:
|
||||
> - [`4.5`](https://github.com/godotengine/godot-cpp/tree/4.5)
|
||||
> - [`4.4`](https://github.com/godotengine/godot-cpp/tree/4.4)
|
||||
> - [`4.3`](https://github.com/godotengine/godot-cpp/tree/4.3)
|
||||
> - [`4.2`](https://github.com/godotengine/godot-cpp/tree/4.2)
|
||||
@@ -62,15 +63,11 @@ See [Updating your GDExtension for 4.1](https://docs.godotengine.org/en/latest/t
|
||||
## Contributing
|
||||
|
||||
We greatly appreciate help in maintaining and extending this project. If you
|
||||
wish to help out, ensure you have an account on GitHub and create a "fork" of
|
||||
this repository. See [Pull request workflow](https://docs.godotengine.org/en/stable/community/contributing/pr_workflow.html)
|
||||
for instructions.
|
||||
|
||||
Please install clang-format and the [pre-commit](https://pre-commit.com/) Python framework so formatting is done before your changes are submitted. See the [code style guidelines](https://docs.godotengine.org/en/latest/contributing/development/code_style_guidelines.html#pre-commit-hook) for instructions.
|
||||
wish to help out, please visit the [godot-cpp section of the Contributing docs](https://contributing.godotengine.org/en/latest/other/godot-cpp.html).
|
||||
|
||||
## Getting started
|
||||
|
||||
You need the same C++ pre-requisites installed that are required for the `godot` repository. Follow the [official build instructions for your target platform](https://docs.godotengine.org/en/latest/contributing/development/compiling/index.html#building-for-target-platforms).
|
||||
You need the same C++ pre-requisites installed that are required for the `godot` repository. Follow the [official build instructions for your target platform](https://docs.godotengine.org/en/latest/engine_details/development/compiling/index.html).
|
||||
|
||||
Getting started with GDExtensions is a bit similar to what it was for 3.x but also a bit different.
|
||||
|
||||
|
||||
@@ -2746,12 +2746,12 @@ def correct_typed_dictionary(type_name):
|
||||
def correct_type(type_name, meta=None, use_alias=True):
|
||||
type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"}
|
||||
if meta is not None:
|
||||
if "int" in meta:
|
||||
if meta in ["int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64"]:
|
||||
return f"{meta}_t"
|
||||
elif "char" in meta:
|
||||
return f"{meta}_t"
|
||||
else:
|
||||
elif meta in ["float", "double"]:
|
||||
return meta
|
||||
elif meta in ["char16", "char32"]:
|
||||
return f"{meta}_t"
|
||||
if type_name in type_conversion:
|
||||
return type_conversion[type_name]
|
||||
if type_name.startswith("typedarray::"):
|
||||
|
||||
@@ -22,7 +22,7 @@ Android platforms.
|
||||
|
||||
.. _built-in support:https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android
|
||||
|
||||
There is further information and examples in the doc/cmake.rst file.
|
||||
There is further information and examples in the docs: https://docs.godotengine.org/en/latest/tutorials/scripting/cpp/build_system/cmake.html
|
||||
|
||||
]=======================================================================]
|
||||
|
||||
@@ -30,8 +30,8 @@ There is further information and examples in the doc/cmake.rst file.
|
||||
function(android_options)
|
||||
#[[ Options from SCons
|
||||
|
||||
The options below are managed by CMake toolchain files, doc.cmake.rst has
|
||||
more information
|
||||
The options below are managed by CMake toolchain files, the docs have more information:
|
||||
https://docs.godotengine.org/en/latest/tutorials/scripting/cpp/build_system/cmake.html
|
||||
|
||||
android_api_level : Target Android API level.
|
||||
Default = 21
|
||||
|
||||
@@ -12,7 +12,8 @@ function(linux_options)
|
||||
#[[ Options from SCons
|
||||
use_llvm : Use the LLVM compiler
|
||||
Not implemented as compiler selection is managed by CMake. Look to
|
||||
doc/cmake.rst for examples.
|
||||
the docs (https://docs.godotengine.org/en/latest/tutorials/scripting/cpp/build_system/cmake.html)
|
||||
for examples.
|
||||
]]
|
||||
option(GODOTCPP_USE_STATIC_CPP "Link libgcc and libstdc++ statically for better portability" ON)
|
||||
endfunction()
|
||||
|
||||
@@ -62,7 +62,9 @@ function(windows_options)
|
||||
Default: True
|
||||
|
||||
These three options will not implemented as compiler selection is managed
|
||||
by CMake toolchain files. Look to doc/cmake.rst for examples.
|
||||
by CMake toolchain files. Look to the docs
|
||||
(https://docs.godotengine.org/en/latest/tutorials/scripting/cpp/build_system/cmake.html)
|
||||
for examples.
|
||||
use_mingw: Use the MinGW compiler instead of MSVC - only effective on Windows
|
||||
use_llvm: Use the LLVM compiler (MVSC or MinGW depending on the use_mingw flag
|
||||
mingw_prefix: MinGW prefix
|
||||
|
||||
353
doc/cmake.rst
353
doc/cmake.rst
@@ -1,353 +0,0 @@
|
||||
CMake
|
||||
=====
|
||||
|
||||
.. warning::
|
||||
|
||||
The CMake scripts do not have feature parity with the SCons ones at this
|
||||
stage and are still a work in progress. There are a number of people who
|
||||
have been working on alternative CMake solutions that are frequently
|
||||
referenced in the discord chats: Ivan's cmake-rewrite_ branch and
|
||||
Vorlac's godot-roguelite_ Project
|
||||
|
||||
.. _cmake-rewrite: https://github.com/IvanInventor/godot-cpp/tree/cmake-rewrite
|
||||
.. _godot-roguelite: https://github.com/vorlac/godot-roguelite
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
Compiling godot-cpp independently of an extension project is mainly for
|
||||
godot-cpp developers, package maintainers, and CI/CD. Look to the
|
||||
godot-cpp-template_ for a practical example on how to consume the godot-cpp
|
||||
library as part of a Godot extension.
|
||||
|
||||
Configuration examples are listed at the bottom of the page.
|
||||
|
||||
.. _godot-cpp-template: https://github.com/godotengine/godot-cpp-template
|
||||
|
||||
Debug vs template_debug
|
||||
-----------------------
|
||||
|
||||
Something I've seen come up many times is the conflation of a compilation of c++
|
||||
source code with debug symbols enabled, and compiling a Godot extension with
|
||||
debug features enabled. The two concepts are not mutually inclusive.
|
||||
|
||||
- debug_features
|
||||
Enables a pre-processor definition to selectively compile code to help
|
||||
users of a Godot extension with their own project.
|
||||
|
||||
debug features are enabled in editor and template_debug builds, which can be specified during the configure phase like so
|
||||
|
||||
``cmake -S . -B cmake-build -DGODOTCPP_TARGET=<target choice>``
|
||||
|
||||
- Debug
|
||||
Sets compiler flags so that debug symbols are generated to help godot
|
||||
extension developers debug their extension.
|
||||
|
||||
``Debug`` is the default build type for CMake projects, to select another it depends on the generator used
|
||||
|
||||
For single configuration generators, add to the configure command:
|
||||
|
||||
``-DCMAKE_BUILD_TYPE=<type>``
|
||||
|
||||
For multi-config generators add to the build command:
|
||||
|
||||
``--config <type>``
|
||||
|
||||
where ``<type>`` is one of ``Debug``, ``Release``, ``RelWithDebInfo``, ``MinSizeRel``
|
||||
|
||||
|
||||
SCons Deviations
|
||||
----------------
|
||||
|
||||
Not everything from SCons can be perfectly representable in CMake, here are
|
||||
the notable differences.
|
||||
|
||||
- debug_symbols
|
||||
No longer has an explicit option, and is enabled via Debug-like CMake
|
||||
build configurations; ``Debug``, ``RelWithDebInfo``.
|
||||
|
||||
- dev_build
|
||||
Does not define ``NDEBUG`` when disabled, ``NDEBUG`` is set via Release-like
|
||||
CMake build configurations; ``Release``, ``MinSizeRel``.
|
||||
|
||||
- arch
|
||||
CMake sets the architecture via the toolchain files, macos universal is controlled vua the ``CMAKE_OSX_ARCHITECTURES``
|
||||
property which is copied to targets when they are defined.
|
||||
|
||||
- debug_crt
|
||||
CMake controls linking to windows runtime libraries by copying the value of ``CMAKE_MSVC_RUNTIME_LIBRARIES`` to targets as they are defined.
|
||||
godot-cpp will set this variable if it isn't already set. so include it before other dependencies to have the value propagate across the projects.
|
||||
|
||||
Testing Integration
|
||||
-------------------
|
||||
The testing target ``godot-cpp-test`` is guarded by ``GODOTCPP_ENABLE_TESTING`` which is off by default.
|
||||
|
||||
To configure and build the godot-cpp project to enable the integration
|
||||
testing targets the command will look something like:
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build -DGODOTCPP_ENABLE_TESTING=YES
|
||||
cmake --build cmake-build --target godot-cpp-test
|
||||
|
||||
Basic walkthrough
|
||||
-----------------
|
||||
|
||||
.. topic:: Clone the git repository
|
||||
|
||||
.. code-block::
|
||||
|
||||
git clone https://github.com/godotengine/godot-cpp.git
|
||||
Cloning into 'godot-cpp'...
|
||||
...
|
||||
cd godot-cpp
|
||||
|
||||
.. topic:: Options
|
||||
|
||||
To list the available options CMake use the ``-L[AH]`` option. ``A`` is for
|
||||
advanced, and ``H`` is for help strings.
|
||||
|
||||
.. code-block::
|
||||
|
||||
cmake .. -LH
|
||||
|
||||
Options are specified on the command line when configuring eg.
|
||||
|
||||
.. code-block::
|
||||
|
||||
cmake .. -DGODOTCPP_USE_HOT_RELOAD:BOOL=ON \
|
||||
-DGODOTCPP_PRECISION:STRING=double \
|
||||
-DCMAKE_BUILD_TYPE:STRING=Debug
|
||||
|
||||
Review setting-build-variables_ and build-configurations_ for more information.
|
||||
|
||||
.. _setting-build-variables: https://cmake.org/cmake/help/latest/guide/user-interaction/index.html#setting-build-variables
|
||||
.. _build-configurations: https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#build-configurations
|
||||
|
||||
A non-exhaustive list of options:
|
||||
|
||||
.. code-block::
|
||||
|
||||
// Path to a custom GDExtension API JSON file (takes precedence over `GODOTCPP_GDEXTENSION_DIR`) ( /path/to/custom_api_file )
|
||||
`GODOTCPP_CUSTOM_API_FILE:FILEPATH=`
|
||||
|
||||
// Force disabling exception handling code (ON|OFF)
|
||||
GODOTCPP_DISABLE_EXCEPTIONS:BOOL=ON
|
||||
|
||||
// Path to a custom directory containing GDExtension interface header and API JSON file ( /path/to/gdextension_dir )
|
||||
GODOTCPP_GDEXTENSION_DIR:PATH=gdextension
|
||||
|
||||
// Set the floating-point precision level (single|double)
|
||||
GODOTCPP_PRECISION:STRING=single
|
||||
|
||||
// Enable the extra accounting required to support hot reload. (ON|OFF)
|
||||
GODOTCPP_USE_HOT_RELOAD:BOOL=
|
||||
|
||||
.. topic:: Configure the build
|
||||
|
||||
.. code-block::
|
||||
|
||||
cmake -S . -B cmake-build -G Ninja
|
||||
|
||||
``-S .`` Specifies the source directory
|
||||
|
||||
``-B cmake-build`` Specifies the build directory
|
||||
|
||||
``-G Ninja`` Specifies the Generator
|
||||
|
||||
The source directory in this example is the source code for godot-cpp.
|
||||
The build directory is so that generated files do not clutter up the source tree.
|
||||
CMake doesn't build the code, it generates the files that another tool uses
|
||||
to build the code, in this case Ninja.
|
||||
To see the list of generators run ``cmake --help``.
|
||||
|
||||
.. topic:: Compiling
|
||||
|
||||
Tell cmake to invoke the build system it generated in the specified directory.
|
||||
The default target is template_debug and the default build configuration is Debug.
|
||||
|
||||
.. code-block::
|
||||
|
||||
cmake --build cmake-build
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Windows and MSVC - Release
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
So long as CMake is installed from the `CMake Downloads`_ page and in the PATH,
|
||||
and Microsoft Visual Studio is installed with c++ support, CMake will detect
|
||||
the MSVC compiler.
|
||||
|
||||
Note that Visual Studio is a Multi-Config Generator so the build configuration
|
||||
needs to be specified at build time ie ``--config Release``
|
||||
|
||||
.. _CMake downloads: https://cmake.org/download/
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build -DGODOTCPP_ENABLE_TESTING=YES
|
||||
cmake --build cmake-build -t godot-cpp-test --config Release
|
||||
|
||||
|
||||
MSys2/clang64, "Ninja" - Debug
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Assumes the ming-w64-clang-x86_64-toolchain is installed
|
||||
|
||||
Note that Ninja is a Single-Config Generator so the build type
|
||||
needs to be specified at Configure time.
|
||||
|
||||
Using the msys2/clang64 shell
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build -G"Ninja" -DGODOTCPP_ENABLE_TESTING=YES -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build cmake-build -t godot-cpp-test
|
||||
|
||||
MSys2/clang64, "Ninja Multi-Config" - dev_build, Debug Symbols
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Assumes the ming-w64-clang-x86_64-toolchain is installed
|
||||
|
||||
This time we are choosing the 'Ninja Multi-Config' generator, so the build
|
||||
type is specified at build time.
|
||||
|
||||
Using the msys2/clang64 shell
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build -G"Ninja Multi-Config" -DGODOTCPP_ENABLE_TESTING=YES -DGODOTCPP_DEV_BUILD:BOOL=ON
|
||||
cmake --build cmake-build -t godot-cpp-test --config Debug
|
||||
|
||||
Emscripten for web platform
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
I've only tested this on windows so far.
|
||||
|
||||
I cloned and installed the latest Emscripten tools to ``c:\emsdk``
|
||||
At the time of writing that was v3.1.69
|
||||
|
||||
I've been using ``C:\emsdk\emsdk.ps1 activate latest`` to enable the
|
||||
environment from powershell in the current shell.
|
||||
|
||||
The ``emcmake.bat`` utility adds the emscripten toolchain to the CMake command
|
||||
It can also be added manually, the location is listed inside the emcmake.bat file
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
C:\emsdk\emsdk.ps1 activate latest
|
||||
emcmake.bat cmake -S . -B cmake-build-web -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build cmake-build-web
|
||||
|
||||
Android Cross Compile from Windows
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
There are two separate paths you can choose when configuring for android.
|
||||
|
||||
Use the ``CMAKE_ANDROID_*`` variables specified on the commandline or in your
|
||||
own toolchain file as listed in the cmake-toolchains_ documentation
|
||||
|
||||
.. _cmake-toolchains: https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling-for-android-with-the-ndk
|
||||
|
||||
Or use the toolchain and scripts provided by the Android SDK and make changes
|
||||
using the ``ANDROID_*`` variables listed there. Where ``<version>`` is whatever
|
||||
ndk version you have installed (tested with `23.2.8568313`) and ``<platform>``
|
||||
is for android sdk platform, (tested with ``android-29``)
|
||||
|
||||
.. warning::
|
||||
|
||||
The Android SDK website explicitly states that they do not support using
|
||||
the CMake built-in method, and recommends you stick with their toolchain
|
||||
files.
|
||||
|
||||
.. topic:: Using your own toolchain file as described in the CMake documentation
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build --toolchain my_toolchain.cmake
|
||||
cmake --build cmake-build -t template_release
|
||||
|
||||
Doing the equivalent on just using the command line
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build \
|
||||
-DCMAKE_SYSTEM_NAME=Android \
|
||||
-DCMAKE_SYSTEM_VERSION=<platform> \
|
||||
-DCMAKE_ANDROID_ARCH_ABI=<arch> \
|
||||
-DCMAKE_ANDROID_NDK=/path/to/android-ndk
|
||||
cmake --build cmake-build
|
||||
|
||||
.. topic:: Using the toolchain file from the Android SDK
|
||||
|
||||
Defaults to minimum supported version( android-16 in my case) and armv7-a.
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build --toolchain $ANDROID_HOME/ndk/<version>/build/cmake/android.toolchain.cmake
|
||||
cmake --build cmake-build
|
||||
|
||||
Specify Android platform and ABI
|
||||
|
||||
.. code-block::
|
||||
|
||||
# Assuming our current directory is the godot-cpp source root
|
||||
cmake -S . -B cmake-build --toolchain $ANDROID_HOME/ndk/<version>/build/cmake/android.toolchain.cmake \
|
||||
-DANDROID_PLATFORM:STRING=android-29 \
|
||||
-DANDROID_ABI:STRING=armeabi-v7a
|
||||
cmake --build cmake-build
|
||||
|
||||
|
||||
Toolchains
|
||||
----------
|
||||
This section attempts to list the host and target combinations that have been
|
||||
at tested.
|
||||
|
||||
Linux Host
|
||||
~~~~~~~~~~
|
||||
|
||||
Macos Host
|
||||
~~~~~~~~~~
|
||||
|
||||
:System: Mac Mini
|
||||
:OS Name: Sequoia 15.0.1
|
||||
:Processor: Apple M2
|
||||
|
||||
* AppleClang
|
||||
|
||||
Windows Host
|
||||
~~~~~~~~~~~~
|
||||
|
||||
:OS Name: Windows 11
|
||||
:Processor: AMD Ryzen 7 6800HS Creator Edition
|
||||
|
||||
|
||||
* `Microsoft Visual Studio 17 2022 <https://visualstudio.microsoft.com/vs/>`_
|
||||
* `LLVM <https://llvm.org/>`_
|
||||
* `LLVM-MinGW <https://github.com/mstorsjo/llvm-mingw/releases>`_
|
||||
|
||||
* aarch64-w64-mingw32
|
||||
* armv7-w64-mingw32
|
||||
* i686-w64-mingw32
|
||||
* x86_64-w64-mingw32
|
||||
|
||||
* `AndroidSDK <https://developer.android.com/studio/#command-tools>`_
|
||||
* `Emscripten <https://emscripten.org/>`_
|
||||
* `MinGW-W64-builds <https://github.com/niXman/mingw-builds-binaries/releases>`_
|
||||
* `Jetbrains-CLion <https://www.jetbrains.com/clion/>`_
|
||||
|
||||
Jetbrains builtin compiler is just the MingW64 above.
|
||||
|
||||
* `MSYS2 <https://www.msys2.org/>`_
|
||||
Necessary reading about MSYS2 `environments <https://www.msys2.org/docs/environments/>`_
|
||||
|
||||
* ucrt64
|
||||
* clang64
|
||||
* mingw32
|
||||
* mingw64
|
||||
* clangarm64
|
||||
@@ -37,7 +37,6 @@
|
||||
#include <godot_cpp/core/type_info.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace godot {
|
||||
|
||||
@@ -262,6 +261,7 @@ void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), co
|
||||
#else
|
||||
r_ret = (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...);
|
||||
#endif
|
||||
(void)p_args; // Avoid warning.
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... P, size_t... Is>
|
||||
@@ -273,7 +273,7 @@ void call_with_variant_args_retc_helper(T *p_instance, R (T::*p_method)(P...) co
|
||||
#else
|
||||
r_ret = (p_instance->*p_method)(VariantCaster<P>::cast(*p_args[Is])...);
|
||||
#endif
|
||||
(void)p_args;
|
||||
(void)p_args; // Avoid warning.
|
||||
}
|
||||
|
||||
template <typename T, typename... P>
|
||||
@@ -331,7 +331,7 @@ void call_with_variant_args_retc(T *p_instance, R (T::*p_method)(P...) const, co
|
||||
}
|
||||
|
||||
template <typename T, typename... P>
|
||||
void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
|
||||
void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const LocalVector<Variant> &default_values) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((size_t)p_argcount > sizeof...(P)) {
|
||||
r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
@@ -366,7 +366,7 @@ void call_with_variant_args_dv(T *p_instance, void (T::*p_method)(P...), const G
|
||||
}
|
||||
|
||||
template <typename T, typename... P>
|
||||
void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const, const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
|
||||
void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const, const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const LocalVector<Variant> &default_values) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((size_t)p_argcount > sizeof...(P)) {
|
||||
r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
@@ -401,7 +401,7 @@ void call_with_variant_argsc_dv(T *p_instance, void (T::*p_method)(P...) const,
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... P>
|
||||
void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
|
||||
void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const LocalVector<Variant> &default_values) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((size_t)p_argcount > sizeof...(P)) {
|
||||
r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
@@ -436,7 +436,7 @@ void call_with_variant_args_ret_dv(T *p_instance, R (T::*p_method)(P...), const
|
||||
}
|
||||
|
||||
template <typename T, typename R, typename... P>
|
||||
void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const, const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
|
||||
void call_with_variant_args_retc_dv(T *p_instance, R (T::*p_method)(P...) const, const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const LocalVector<Variant> &default_values) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((size_t)p_argcount > sizeof...(P)) {
|
||||
r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
@@ -548,7 +548,7 @@ void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_arg
|
||||
}
|
||||
|
||||
template <typename... P>
|
||||
void call_with_variant_args_static_dv(void (*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
|
||||
void call_with_variant_args_static_dv(void (*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, GDExtensionCallError &r_error, const LocalVector<Variant> &default_values) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((size_t)p_argcount > sizeof...(P)) {
|
||||
r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
@@ -640,7 +640,7 @@ void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_ar
|
||||
}
|
||||
|
||||
template <typename R, typename... P>
|
||||
void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const std::vector<Variant> &default_values) {
|
||||
void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const GDExtensionConstVariantPtr *p_args, int p_argcount, Variant &r_ret, GDExtensionCallError &r_error, const LocalVector<Variant> &default_values) {
|
||||
#ifdef DEBUG_ENABLED
|
||||
if ((size_t)p_argcount > sizeof...(P)) {
|
||||
r_error.error = GDEXTENSION_CALL_ERROR_TOO_MANY_ARGUMENTS;
|
||||
|
||||
@@ -44,20 +44,10 @@
|
||||
// Needs to come after method_bind and object have been included.
|
||||
#include <godot_cpp/variant/callable_method_pointer.hpp>
|
||||
|
||||
#include <godot_cpp/templates/a_hash_map.hpp>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
// Needed to use StringName as key in `std::unordered_map`
|
||||
template <>
|
||||
struct std::hash<godot::StringName> {
|
||||
std::size_t operator()(godot::StringName const &s) const noexcept {
|
||||
return s.hash();
|
||||
}
|
||||
};
|
||||
|
||||
namespace godot {
|
||||
|
||||
@@ -95,9 +85,9 @@ public:
|
||||
StringName name;
|
||||
StringName parent_name;
|
||||
GDExtensionInitializationLevel level = GDEXTENSION_INITIALIZATION_SCENE;
|
||||
std::unordered_map<StringName, MethodBind *> method_map;
|
||||
AHashMap<StringName, MethodBind *> method_map;
|
||||
std::set<StringName> signal_names;
|
||||
std::unordered_map<StringName, VirtualMethod> virtual_methods;
|
||||
AHashMap<StringName, VirtualMethod> virtual_methods;
|
||||
std::set<StringName> property_names;
|
||||
std::set<StringName> constant_names;
|
||||
// Pointer to the parent custom class, if any. Will be null if the parent class is a Godot class.
|
||||
@@ -106,11 +96,11 @@ public:
|
||||
|
||||
private:
|
||||
// This may only contain custom classes, not Godot classes
|
||||
static std::unordered_map<StringName, ClassInfo> classes;
|
||||
static std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> instance_binding_callbacks;
|
||||
static AHashMap<StringName, ClassInfo> classes;
|
||||
static AHashMap<StringName, const GDExtensionInstanceBindingCallbacks *> instance_binding_callbacks;
|
||||
// Used to remember the custom class registration order.
|
||||
static std::vector<StringName> class_register_order;
|
||||
static std::unordered_map<StringName, Object *> engine_singletons;
|
||||
static LocalVector<StringName> class_register_order;
|
||||
static AHashMap<StringName, Object *> engine_singletons;
|
||||
static std::mutex engine_singletons_mutex;
|
||||
|
||||
static MethodBind *bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const void **p_defs, int p_defcount);
|
||||
@@ -169,9 +159,9 @@ public:
|
||||
|
||||
static void _register_engine_singleton(const StringName &p_class_name, Object *p_singleton) {
|
||||
std::lock_guard<std::mutex> lock(engine_singletons_mutex);
|
||||
std::unordered_map<StringName, Object *>::const_iterator i = engine_singletons.find(p_class_name);
|
||||
AHashMap<StringName, Object *>::ConstIterator i = engine_singletons.find(p_class_name);
|
||||
if (i != engine_singletons.end()) {
|
||||
ERR_FAIL_COND((*i).second != p_singleton);
|
||||
ERR_FAIL_COND((*i).value != p_singleton);
|
||||
return;
|
||||
}
|
||||
engine_singletons[p_class_name] = p_singleton;
|
||||
@@ -189,7 +179,7 @@ public:
|
||||
static MethodBind *bind_static_method(StringName p_class, N p_method_name, M p_method, VarArgs... p_args);
|
||||
|
||||
template <typename M>
|
||||
static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const std::vector<Variant> &p_default_args = std::vector<Variant>{}, bool p_return_nil_is_variant = true);
|
||||
static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const LocalVector<Variant> &p_default_args = LocalVector<Variant>{}, bool p_return_nil_is_variant = true);
|
||||
|
||||
static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix);
|
||||
static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix);
|
||||
@@ -241,10 +231,10 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed, bool p_runtime) {
|
||||
cl.name = T::get_class_static();
|
||||
cl.parent_name = T::get_parent_class_static();
|
||||
cl.level = current_level;
|
||||
std::unordered_map<StringName, ClassInfo>::iterator parent_it = classes.find(cl.parent_name);
|
||||
AHashMap<StringName, ClassInfo>::Iterator parent_it = classes.find(cl.parent_name);
|
||||
if (parent_it != classes.end()) {
|
||||
// Assign parent if it is also a custom class
|
||||
cl.parent_ptr = &parent_it->second;
|
||||
cl.parent_ptr = &parent_it->value;
|
||||
}
|
||||
classes[cl.name] = cl;
|
||||
class_register_order.push_back(cl.name);
|
||||
@@ -329,7 +319,7 @@ MethodBind *ClassDB::bind_static_method(StringName p_class, N p_method_name, M p
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info, const std::vector<Variant> &p_default_args, bool p_return_nil_is_variant) {
|
||||
MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info, const LocalVector<Variant> &p_default_args, bool p_return_nil_is_variant) {
|
||||
MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant);
|
||||
ERR_FAIL_NULL_V(bind, nullptr);
|
||||
|
||||
@@ -338,13 +328,13 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p
|
||||
|
||||
StringName instance_type = bind->get_instance_class();
|
||||
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(instance_type);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(instance_type);
|
||||
if (type_it == classes.end()) {
|
||||
memdelete(bind);
|
||||
ERR_FAIL_V_MSG(nullptr, String("Class '{0}' doesn't exist.").format(Array::make(instance_type)));
|
||||
}
|
||||
|
||||
ClassInfo &type = type_it->second;
|
||||
ClassInfo &type = type_it->value;
|
||||
|
||||
if (type.method_map.find(p_name) != type.method_map.end()) {
|
||||
memdelete(bind);
|
||||
|
||||
@@ -38,11 +38,7 @@
|
||||
#include <gdextension_interface.h>
|
||||
|
||||
#include <godot_cpp/classes/global_constants.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <iostream>
|
||||
#include <godot_cpp/templates/local_vector.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
@@ -57,9 +53,9 @@ class MethodBind {
|
||||
bool _returns = false;
|
||||
bool _vararg = false;
|
||||
|
||||
std::vector<StringName> argument_names;
|
||||
LocalVector<StringName> argument_names;
|
||||
GDExtensionVariantType *argument_types = nullptr;
|
||||
std::vector<Variant> default_arguments;
|
||||
LocalVector<Variant> default_arguments;
|
||||
|
||||
protected:
|
||||
void _set_const(bool p_const);
|
||||
@@ -73,7 +69,7 @@ protected:
|
||||
void set_argument_count(int p_count) { argument_count = p_count; }
|
||||
|
||||
public:
|
||||
_FORCE_INLINE_ const std::vector<Variant> &get_default_arguments() const { return default_arguments; }
|
||||
_FORCE_INLINE_ const LocalVector<Variant> &get_default_arguments() const { return default_arguments; }
|
||||
_FORCE_INLINE_ int get_default_argument_count() const { return (int)default_arguments.size(); }
|
||||
|
||||
_FORCE_INLINE_ Variant has_default_argument(int p_arg) const {
|
||||
@@ -104,8 +100,8 @@ public:
|
||||
|
||||
PropertyInfo get_argument_info(int p_argument) const;
|
||||
|
||||
std::vector<PropertyInfo> get_arguments_info_list() const {
|
||||
std::vector<PropertyInfo> vec;
|
||||
LocalVector<PropertyInfo> get_arguments_info_list() const {
|
||||
LocalVector<PropertyInfo> vec;
|
||||
// First element is return value
|
||||
vec.reserve(argument_count + 1);
|
||||
for (int i = 0; i < argument_count + 1; i++) {
|
||||
@@ -114,8 +110,8 @@ public:
|
||||
return vec;
|
||||
}
|
||||
|
||||
void set_argument_names(const std::vector<StringName> &p_names);
|
||||
std::vector<StringName> get_argument_names() const;
|
||||
void set_argument_names(const LocalVector<StringName> &p_names);
|
||||
LocalVector<StringName> get_argument_names() const;
|
||||
|
||||
virtual GDExtensionClassMethodArgumentMetadata get_argument_metadata(int p_argument) const = 0;
|
||||
|
||||
@@ -136,10 +132,10 @@ public:
|
||||
_FORCE_INLINE_ bool is_vararg() const { return _vararg; }
|
||||
_FORCE_INLINE_ bool has_return() const { return _returns; }
|
||||
|
||||
void set_default_arguments(const std::vector<Variant> &p_default_arguments) { default_arguments = p_default_arguments; }
|
||||
void set_default_arguments(const LocalVector<Variant> &p_default_arguments) { default_arguments = p_default_arguments; }
|
||||
|
||||
std::vector<GDExtensionClassMethodArgumentMetadata> get_arguments_metadata_list() const {
|
||||
std::vector<GDExtensionClassMethodArgumentMetadata> vec;
|
||||
LocalVector<GDExtensionClassMethodArgumentMetadata> get_arguments_metadata_list() const {
|
||||
LocalVector<GDExtensionClassMethodArgumentMetadata> vec;
|
||||
// First element is return value
|
||||
vec.reserve(argument_count + 1);
|
||||
for (int i = 0; i < argument_count + 1; i++) {
|
||||
@@ -158,7 +154,7 @@ template <typename Derived, typename T, typename R, bool should_returns>
|
||||
class MethodBindVarArgBase : public MethodBind {
|
||||
protected:
|
||||
R (T::*method)(const Variant **, GDExtensionInt, GDExtensionCallError &);
|
||||
std::vector<PropertyInfo> arguments;
|
||||
LocalVector<PropertyInfo> arguments;
|
||||
|
||||
public:
|
||||
virtual PropertyInfo gen_argument_type_info(int p_arg) const {
|
||||
@@ -194,7 +190,7 @@ public:
|
||||
if (p_method_info.arguments.size()) {
|
||||
arguments = p_method_info.arguments;
|
||||
|
||||
std::vector<StringName> names;
|
||||
LocalVector<StringName> names;
|
||||
names.reserve(p_method_info.arguments.size());
|
||||
for (size_t i = 0; i < p_method_info.arguments.size(); i++) {
|
||||
names.push_back(p_method_info.arguments[i].name);
|
||||
|
||||
@@ -38,14 +38,14 @@
|
||||
|
||||
#include <godot_cpp/variant/variant.hpp>
|
||||
|
||||
#include <godot_cpp/templates/local_vector.hpp>
|
||||
|
||||
#include <godot_cpp/classes/object.hpp>
|
||||
|
||||
#include <godot_cpp/godot.hpp>
|
||||
|
||||
#include <gdextension_interface.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#define ADD_SIGNAL(m_signal) ::godot::ClassDB::add_signal(get_class_static(), m_signal)
|
||||
#define ADD_GROUP(m_name, m_prefix) ::godot::ClassDB::add_property_group(get_class_static(), m_name, m_prefix)
|
||||
#define ADD_SUBGROUP(m_name, m_prefix) ::godot::ClassDB::add_property_subgroup(get_class_static(), m_name, m_prefix)
|
||||
@@ -65,10 +65,10 @@ struct MethodInfo {
|
||||
PropertyInfo return_val;
|
||||
uint32_t flags;
|
||||
int id = 0;
|
||||
std::vector<PropertyInfo> arguments;
|
||||
std::vector<Variant> default_arguments;
|
||||
LocalVector<PropertyInfo> arguments;
|
||||
LocalVector<Variant> default_arguments;
|
||||
GDExtensionClassMethodArgumentMetadata return_val_metadata;
|
||||
std::vector<GDExtensionClassMethodArgumentMetadata> arguments_metadata;
|
||||
LocalVector<GDExtensionClassMethodArgumentMetadata> arguments_metadata;
|
||||
|
||||
inline bool operator==(const MethodInfo &p_method) const { return id == p_method.id; }
|
||||
inline bool operator<(const MethodInfo &p_method) const { return id == p_method.id ? (name < p_method.name) : (id < p_method.id); }
|
||||
@@ -92,21 +92,17 @@ struct MethodInfo {
|
||||
|
||||
template <typename... Args>
|
||||
MethodInfo::MethodInfo(StringName p_name, const Args &...args) :
|
||||
name(p_name), flags(GDEXTENSION_METHOD_FLAG_NORMAL) {
|
||||
arguments = { args... };
|
||||
}
|
||||
name(p_name), flags(GDEXTENSION_METHOD_FLAG_NORMAL), arguments({ args... }) {}
|
||||
|
||||
template <typename... Args>
|
||||
MethodInfo::MethodInfo(Variant::Type ret, StringName p_name, const Args &...args) :
|
||||
name(p_name), flags(GDEXTENSION_METHOD_FLAG_NORMAL) {
|
||||
name(p_name), flags(GDEXTENSION_METHOD_FLAG_NORMAL), arguments({ args... }) {
|
||||
return_val.type = ret;
|
||||
arguments = { args... };
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
MethodInfo::MethodInfo(const PropertyInfo &p_ret, StringName p_name, const Args &...args) :
|
||||
name(p_name), return_val(p_ret), flags(GDEXTENSION_METHOD_FLAG_NORMAL) {
|
||||
arguments = { args... };
|
||||
name(p_name), return_val(p_ret), flags(GDEXTENSION_METHOD_FLAG_NORMAL), arguments({ args... }) {
|
||||
}
|
||||
|
||||
class ObjectDB {
|
||||
|
||||
734
include/godot_cpp/templates/a_hash_map.hpp
Normal file
734
include/godot_cpp/templates/a_hash_map.hpp
Normal file
@@ -0,0 +1,734 @@
|
||||
/**************************************************************************/
|
||||
/* a_hash_map.hpp */
|
||||
/**************************************************************************/
|
||||
/* This file is part of: */
|
||||
/* GODOT ENGINE */
|
||||
/* https://godotengine.org */
|
||||
/**************************************************************************/
|
||||
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/**************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/templates/hash_map.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
struct HashMapData {
|
||||
union {
|
||||
uint64_t data;
|
||||
struct
|
||||
{
|
||||
uint32_t hash;
|
||||
uint32_t hash_to_key;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(HashMapData) == 8);
|
||||
|
||||
/**
|
||||
* An array-based implementation of a hash map. It is very efficient in terms of performance and
|
||||
* memory usage. Works like a dynamic array, adding elements to the end of the array, and
|
||||
* allows you to access array elements by their index by using `get_by_index` method.
|
||||
* Example:
|
||||
* ```
|
||||
* AHashMap<int, Object *> map;
|
||||
*
|
||||
* int get_object_id_by_number(int p_number) {
|
||||
* int id = map.get_index(p_number);
|
||||
* return id;
|
||||
* }
|
||||
*
|
||||
* Object *get_object_by_id(int p_id) {
|
||||
* map.get_by_index(p_id).value;
|
||||
* }
|
||||
* ```
|
||||
* Still, don`t erase the elements because ID can break.
|
||||
*
|
||||
* When an element erase, its place is taken by the element from the end.
|
||||
*
|
||||
* <-------------
|
||||
* | |
|
||||
* 6 8 X 9 32 -1 5 -10 7 X X X
|
||||
* 6 8 7 9 32 -1 5 -10 X X X X
|
||||
*
|
||||
*
|
||||
* Use RBMap if you need to iterate over sorted elements.
|
||||
*
|
||||
* Use HashMap if:
|
||||
* - You need to keep an iterator or const pointer to Key and you intend to add/remove elements in the meantime.
|
||||
* - You need to preserve the insertion order when using erase.
|
||||
*
|
||||
* It is recommended to use `HashMap` if `KeyValue` size is very large.
|
||||
*/
|
||||
template <typename TKey, typename TValue,
|
||||
typename Hasher = HashMapHasherDefault,
|
||||
typename Comparator = HashMapComparatorDefault<TKey>>
|
||||
class AHashMap {
|
||||
public:
|
||||
// Must be a power of two.
|
||||
static constexpr uint32_t INITIAL_CAPACITY = 16;
|
||||
static constexpr uint32_t EMPTY_HASH = 0;
|
||||
static_assert(EMPTY_HASH == 0, "EMPTY_HASH must always be 0 for the memcpy() optimization.");
|
||||
|
||||
private:
|
||||
typedef KeyValue<TKey, TValue> MapKeyValue;
|
||||
MapKeyValue *elements = nullptr;
|
||||
HashMapData *map_data = nullptr;
|
||||
|
||||
// Due to optimization, this is `capacity - 1`. Use + 1 to get normal capacity.
|
||||
uint32_t capacity = 0;
|
||||
uint32_t num_elements = 0;
|
||||
|
||||
uint32_t _hash(const TKey &p_key) const {
|
||||
uint32_t hash = Hasher::hash(p_key);
|
||||
|
||||
if (unlikely(hash == EMPTY_HASH)) {
|
||||
hash = EMPTY_HASH + 1;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t _get_resize_count(uint32_t p_capacity) {
|
||||
return p_capacity ^ (p_capacity + 1) >> 2; // = get_capacity() * 0.75 - 1; Works only if p_capacity = 2^n - 1.
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t _get_probe_length(uint32_t p_pos, uint32_t p_hash, uint32_t p_local_capacity) {
|
||||
const uint32_t original_pos = p_hash & p_local_capacity;
|
||||
return (p_pos - original_pos + p_local_capacity + 1) & p_local_capacity;
|
||||
}
|
||||
|
||||
bool _lookup_pos(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos) const {
|
||||
if (unlikely(elements == nullptr)) {
|
||||
return false; // Failed lookups, no elements.
|
||||
}
|
||||
return _lookup_pos_with_hash(p_key, r_pos, r_hash_pos, _hash(p_key));
|
||||
}
|
||||
|
||||
bool _lookup_pos_with_hash(const TKey &p_key, uint32_t &r_pos, uint32_t &r_hash_pos, uint32_t p_hash) const {
|
||||
if (unlikely(elements == nullptr)) {
|
||||
return false; // Failed lookups, no elements.
|
||||
}
|
||||
|
||||
uint32_t pos = p_hash & capacity;
|
||||
HashMapData data = map_data[pos];
|
||||
if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) {
|
||||
r_pos = data.hash_to_key;
|
||||
r_hash_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data.data == EMPTY_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// A collision occurred.
|
||||
pos = (pos + 1) & capacity;
|
||||
uint32_t distance = 1;
|
||||
while (true) {
|
||||
data = map_data[pos];
|
||||
if (data.hash == p_hash && Comparator::compare(elements[data.hash_to_key].key, p_key)) {
|
||||
r_pos = data.hash_to_key;
|
||||
r_hash_pos = pos;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (data.data == EMPTY_HASH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (distance > _get_probe_length(pos, data.hash, capacity)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = (pos + 1) & capacity;
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t _insert_with_hash(uint32_t p_hash, uint32_t p_index) {
|
||||
uint32_t pos = p_hash & capacity;
|
||||
|
||||
if (map_data[pos].data == EMPTY_HASH) {
|
||||
uint64_t data = ((uint64_t)p_index << 32) | p_hash;
|
||||
map_data[pos].data = data;
|
||||
return pos;
|
||||
}
|
||||
|
||||
uint32_t distance = 1;
|
||||
pos = (pos + 1) & capacity;
|
||||
HashMapData c_data;
|
||||
c_data.hash = p_hash;
|
||||
c_data.hash_to_key = p_index;
|
||||
|
||||
while (true) {
|
||||
if (map_data[pos].data == EMPTY_HASH) {
|
||||
#ifdef DEV_ENABLED
|
||||
if (unlikely(distance > 12)) {
|
||||
WARN_PRINT("Excessive collision count (" +
|
||||
itos(distance) + "), is the right hash function being used?");
|
||||
}
|
||||
#endif
|
||||
map_data[pos] = c_data;
|
||||
return pos;
|
||||
}
|
||||
|
||||
// Not an empty slot, let's check the probing length of the existing one.
|
||||
uint32_t existing_probe_len = _get_probe_length(pos, map_data[pos].hash, capacity);
|
||||
if (existing_probe_len < distance) {
|
||||
SWAP(c_data, map_data[pos]);
|
||||
distance = existing_probe_len;
|
||||
}
|
||||
|
||||
pos = (pos + 1) & capacity;
|
||||
distance++;
|
||||
}
|
||||
}
|
||||
|
||||
void _resize_and_rehash(uint32_t p_new_capacity) {
|
||||
uint32_t real_old_capacity = capacity + 1;
|
||||
// Capacity can't be 0 and must be 2^n - 1.
|
||||
capacity = MAX(4u, p_new_capacity);
|
||||
uint32_t real_capacity = next_power_of_2(capacity);
|
||||
capacity = real_capacity - 1;
|
||||
|
||||
HashMapData *old_map_data = map_data;
|
||||
|
||||
map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
|
||||
memset(map_data, 0, sizeof(HashMapData) * real_capacity);
|
||||
elements = reinterpret_cast<MapKeyValue *>(Memory::realloc_static(elements, sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
|
||||
|
||||
if (num_elements != 0) {
|
||||
for (uint32_t i = 0; i < real_old_capacity; i++) {
|
||||
HashMapData data = old_map_data[i];
|
||||
if (data.data != EMPTY_HASH) {
|
||||
_insert_with_hash(data.hash, data.hash_to_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Memory::free_static(old_map_data);
|
||||
}
|
||||
|
||||
int32_t _insert_element(const TKey &p_key, const TValue &p_value, uint32_t p_hash) {
|
||||
if (unlikely(elements == nullptr)) {
|
||||
// Allocate on demand to save memory.
|
||||
|
||||
uint32_t real_capacity = capacity + 1;
|
||||
map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
|
||||
memset(map_data, 0, sizeof(HashMapData) * real_capacity);
|
||||
elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
|
||||
}
|
||||
|
||||
if (unlikely(num_elements > _get_resize_count(capacity))) {
|
||||
_resize_and_rehash(capacity * 2);
|
||||
}
|
||||
|
||||
memnew_placement(&elements[num_elements], MapKeyValue(p_key, p_value));
|
||||
|
||||
_insert_with_hash(p_hash, num_elements);
|
||||
num_elements++;
|
||||
return num_elements - 1;
|
||||
}
|
||||
|
||||
void _init_from(const AHashMap &p_other) {
|
||||
capacity = p_other.capacity;
|
||||
uint32_t real_capacity = capacity + 1;
|
||||
num_elements = p_other.num_elements;
|
||||
|
||||
if (p_other.num_elements == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
map_data = reinterpret_cast<HashMapData *>(Memory::alloc_static(sizeof(HashMapData) * real_capacity));
|
||||
elements = reinterpret_cast<MapKeyValue *>(Memory::alloc_static(sizeof(MapKeyValue) * (_get_resize_count(capacity) + 1)));
|
||||
|
||||
if constexpr (std::is_trivially_copyable_v<TKey> && std::is_trivially_copyable_v<TValue>) {
|
||||
void *destination = elements;
|
||||
const void *source = p_other.elements;
|
||||
memcpy(destination, source, sizeof(MapKeyValue) * num_elements);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
memnew_placement(&elements[i], MapKeyValue(p_other.elements[i]));
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(map_data, p_other.map_data, sizeof(HashMapData) * real_capacity);
|
||||
}
|
||||
|
||||
public:
|
||||
/* Standard Godot Container API */
|
||||
|
||||
_FORCE_INLINE_ uint32_t get_capacity() const { return capacity + 1; }
|
||||
_FORCE_INLINE_ uint32_t size() const { return num_elements; }
|
||||
|
||||
_FORCE_INLINE_ bool is_empty() const {
|
||||
return num_elements == 0;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
if (elements == nullptr || num_elements == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
memset(map_data, EMPTY_HASH, (capacity + 1) * sizeof(HashMapData));
|
||||
if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) {
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
elements[i].key.~TKey();
|
||||
elements[i].value.~TValue();
|
||||
}
|
||||
}
|
||||
|
||||
num_elements = 0;
|
||||
}
|
||||
|
||||
TValue &get(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
CRASH_COND_MSG(!exists, "AHashMap key not found.");
|
||||
return elements[pos].value;
|
||||
}
|
||||
|
||||
const TValue &get(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
CRASH_COND_MSG(!exists, "AHashMap key not found.");
|
||||
return elements[pos].value;
|
||||
}
|
||||
|
||||
const TValue *getptr(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
|
||||
if (exists) {
|
||||
return &elements[pos].value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TValue *getptr(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t hash_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, hash_pos);
|
||||
|
||||
if (exists) {
|
||||
return &elements[pos].value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool has(const TKey &p_key) const {
|
||||
uint32_t _pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
return _lookup_pos(p_key, _pos, h_pos);
|
||||
}
|
||||
|
||||
bool erase(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t element_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, element_pos, pos);
|
||||
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t next_pos = (pos + 1) & capacity;
|
||||
while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) {
|
||||
SWAP(map_data[next_pos], map_data[pos]);
|
||||
|
||||
pos = next_pos;
|
||||
next_pos = (next_pos + 1) & capacity;
|
||||
}
|
||||
|
||||
map_data[pos].data = EMPTY_HASH;
|
||||
elements[element_pos].key.~TKey();
|
||||
elements[element_pos].value.~TValue();
|
||||
num_elements--;
|
||||
|
||||
if (element_pos < num_elements) {
|
||||
void *destination = &elements[element_pos];
|
||||
const void *source = &elements[num_elements];
|
||||
memcpy(destination, source, sizeof(MapKeyValue));
|
||||
uint32_t h_pos = 0;
|
||||
_lookup_pos(elements[num_elements].key, pos, h_pos);
|
||||
map_data[h_pos].hash_to_key = element_pos;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Replace the key of an entry in-place, without invalidating iterators or changing the entries position during iteration.
|
||||
// p_old_key must exist in the map and p_new_key must not, unless it is equal to p_old_key.
|
||||
bool replace_key(const TKey &p_old_key, const TKey &p_new_key) {
|
||||
if (p_old_key == p_new_key) {
|
||||
return true;
|
||||
}
|
||||
uint32_t pos = 0;
|
||||
uint32_t element_pos = 0;
|
||||
ERR_FAIL_COND_V(_lookup_pos(p_new_key, element_pos, pos), false);
|
||||
ERR_FAIL_COND_V(!_lookup_pos(p_old_key, element_pos, pos), false);
|
||||
MapKeyValue &element = elements[element_pos];
|
||||
const_cast<TKey &>(element.key) = p_new_key;
|
||||
|
||||
uint32_t next_pos = (pos + 1) & capacity;
|
||||
while (map_data[next_pos].hash != EMPTY_HASH && _get_probe_length(next_pos, map_data[next_pos].hash, capacity) != 0) {
|
||||
SWAP(map_data[next_pos], map_data[pos]);
|
||||
|
||||
pos = next_pos;
|
||||
next_pos = (next_pos + 1) & capacity;
|
||||
}
|
||||
|
||||
map_data[pos].data = EMPTY_HASH;
|
||||
|
||||
uint32_t hash = _hash(p_new_key);
|
||||
_insert_with_hash(hash, element_pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reserves space for a number of elements, useful to avoid many resizes and rehashes.
|
||||
// If adding a known (possibly large) number of elements at once, must be larger than old capacity.
|
||||
void reserve(uint32_t p_new_capacity) {
|
||||
ERR_FAIL_COND_MSG(p_new_capacity < size(), "reserve() called with a capacity smaller than the current size. This is likely a mistake.");
|
||||
if (elements == nullptr) {
|
||||
capacity = MAX(4u, p_new_capacity);
|
||||
capacity = next_power_of_2(capacity) - 1;
|
||||
return; // Unallocated yet.
|
||||
}
|
||||
if (p_new_capacity <= get_capacity()) {
|
||||
return;
|
||||
}
|
||||
_resize_and_rehash(p_new_capacity);
|
||||
}
|
||||
|
||||
/** Iterator API **/
|
||||
|
||||
struct ConstIterator {
|
||||
_FORCE_INLINE_ const MapKeyValue &operator*() const {
|
||||
return *pair;
|
||||
}
|
||||
_FORCE_INLINE_ const MapKeyValue *operator->() const {
|
||||
return pair;
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator &operator++() {
|
||||
pair++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ConstIterator &operator--() {
|
||||
pair--;
|
||||
if (pair < begin) {
|
||||
pair = end;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return pair == b.pair; }
|
||||
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return pair != b.pair; }
|
||||
|
||||
_FORCE_INLINE_ explicit operator bool() const {
|
||||
return pair != end;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ConstIterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) {
|
||||
pair = p_key;
|
||||
begin = p_begin;
|
||||
end = p_end;
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator() {}
|
||||
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
_FORCE_INLINE_ void operator=(const ConstIterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
|
||||
private:
|
||||
MapKeyValue *pair = nullptr;
|
||||
MapKeyValue *begin = nullptr;
|
||||
MapKeyValue *end = nullptr;
|
||||
};
|
||||
|
||||
struct Iterator {
|
||||
_FORCE_INLINE_ MapKeyValue &operator*() const {
|
||||
return *pair;
|
||||
}
|
||||
_FORCE_INLINE_ MapKeyValue *operator->() const {
|
||||
return pair;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator &operator++() {
|
||||
pair++;
|
||||
return *this;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator &operator--() {
|
||||
pair--;
|
||||
if (pair < begin) {
|
||||
pair = end;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return pair == b.pair; }
|
||||
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return pair != b.pair; }
|
||||
|
||||
_FORCE_INLINE_ explicit operator bool() const {
|
||||
return pair != end;
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ Iterator(MapKeyValue *p_key, MapKeyValue *p_begin, MapKeyValue *p_end) {
|
||||
pair = p_key;
|
||||
begin = p_begin;
|
||||
end = p_end;
|
||||
}
|
||||
_FORCE_INLINE_ Iterator() {}
|
||||
_FORCE_INLINE_ Iterator(const Iterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
_FORCE_INLINE_ void operator=(const Iterator &p_it) {
|
||||
pair = p_it.pair;
|
||||
begin = p_it.begin;
|
||||
end = p_it.end;
|
||||
}
|
||||
|
||||
operator ConstIterator() const {
|
||||
return ConstIterator(pair, begin, end);
|
||||
}
|
||||
|
||||
private:
|
||||
MapKeyValue *pair = nullptr;
|
||||
MapKeyValue *begin = nullptr;
|
||||
MapKeyValue *end = nullptr;
|
||||
};
|
||||
|
||||
_FORCE_INLINE_ Iterator begin() {
|
||||
return Iterator(elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ Iterator end() {
|
||||
return Iterator(elements + num_elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ Iterator last() {
|
||||
if (unlikely(num_elements == 0)) {
|
||||
return Iterator(nullptr, nullptr, nullptr);
|
||||
}
|
||||
return Iterator(elements + num_elements - 1, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
Iterator find(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
if (!exists) {
|
||||
return end();
|
||||
}
|
||||
return Iterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
void remove(const Iterator &p_iter) {
|
||||
if (p_iter) {
|
||||
erase(p_iter->key);
|
||||
}
|
||||
}
|
||||
|
||||
_FORCE_INLINE_ ConstIterator begin() const {
|
||||
return ConstIterator(elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator end() const {
|
||||
return ConstIterator(elements + num_elements, elements, elements + num_elements);
|
||||
}
|
||||
_FORCE_INLINE_ ConstIterator last() const {
|
||||
if (unlikely(num_elements == 0)) {
|
||||
return ConstIterator(nullptr, nullptr, nullptr);
|
||||
}
|
||||
return ConstIterator(elements + num_elements - 1, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
ConstIterator find(const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
if (!exists) {
|
||||
return end();
|
||||
}
|
||||
return ConstIterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
/* Indexing */
|
||||
|
||||
const TValue &operator[](const TKey &p_key) const {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
CRASH_COND(!exists);
|
||||
return elements[pos].value;
|
||||
}
|
||||
|
||||
TValue &operator[](const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
uint32_t hash = _hash(p_key);
|
||||
bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash);
|
||||
|
||||
if (exists) {
|
||||
return elements[pos].value;
|
||||
} else {
|
||||
pos = _insert_element(p_key, TValue(), hash);
|
||||
return elements[pos].value;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert */
|
||||
|
||||
Iterator insert(const TKey &p_key, const TValue &p_value) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
uint32_t hash = _hash(p_key);
|
||||
bool exists = _lookup_pos_with_hash(p_key, pos, h_pos, hash);
|
||||
|
||||
if (!exists) {
|
||||
pos = _insert_element(p_key, p_value, hash);
|
||||
} else {
|
||||
elements[pos].value = p_value;
|
||||
}
|
||||
return Iterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
// Inserts an element without checking if it already exists.
|
||||
Iterator insert_new(const TKey &p_key, const TValue &p_value) {
|
||||
DEV_ASSERT(!has(p_key));
|
||||
uint32_t hash = _hash(p_key);
|
||||
uint32_t pos = _insert_element(p_key, p_value, hash);
|
||||
return Iterator(elements + pos, elements, elements + num_elements);
|
||||
}
|
||||
|
||||
/* Array methods. */
|
||||
|
||||
// Unsafe. Changing keys and going outside the bounds of an array can lead to undefined behavior.
|
||||
KeyValue<TKey, TValue> *get_elements_ptr() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
// Returns the element index. If not found, returns -1.
|
||||
int get_index(const TKey &p_key) {
|
||||
uint32_t pos = 0;
|
||||
uint32_t h_pos = 0;
|
||||
bool exists = _lookup_pos(p_key, pos, h_pos);
|
||||
if (!exists) {
|
||||
return -1;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
KeyValue<TKey, TValue> &get_by_index(uint32_t p_index) {
|
||||
CRASH_BAD_UNSIGNED_INDEX(p_index, num_elements);
|
||||
return elements[p_index];
|
||||
}
|
||||
|
||||
bool erase_by_index(uint32_t p_index) {
|
||||
if (p_index >= size()) {
|
||||
return false;
|
||||
}
|
||||
return erase(elements[p_index].key);
|
||||
}
|
||||
|
||||
/* Constructors */
|
||||
|
||||
AHashMap(const AHashMap &p_other) {
|
||||
_init_from(p_other);
|
||||
}
|
||||
|
||||
AHashMap(const HashMap<TKey, TValue> &p_other) {
|
||||
reserve(p_other.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_other) {
|
||||
uint32_t hash = _hash(E.key);
|
||||
_insert_element(E.key, E.value, hash);
|
||||
}
|
||||
}
|
||||
|
||||
void operator=(const AHashMap &p_other) {
|
||||
if (this == &p_other) {
|
||||
return; // Ignore self assignment.
|
||||
}
|
||||
|
||||
reset();
|
||||
|
||||
_init_from(p_other);
|
||||
}
|
||||
|
||||
void operator=(const HashMap<TKey, TValue> &p_other) {
|
||||
reset();
|
||||
reserve(p_other.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_other) {
|
||||
uint32_t hash = _hash(E.key);
|
||||
_insert_element(E.key, E.value, hash);
|
||||
}
|
||||
}
|
||||
|
||||
AHashMap(uint32_t p_initial_capacity) {
|
||||
// Capacity can't be 0 and must be 2^n - 1.
|
||||
capacity = MAX(4u, p_initial_capacity);
|
||||
capacity = next_power_of_2(capacity) - 1;
|
||||
}
|
||||
AHashMap() :
|
||||
capacity(INITIAL_CAPACITY - 1) {
|
||||
}
|
||||
|
||||
AHashMap(std::initializer_list<KeyValue<TKey, TValue>> p_init) {
|
||||
reserve(p_init.size());
|
||||
for (const KeyValue<TKey, TValue> &E : p_init) {
|
||||
insert(E.key, E.value);
|
||||
}
|
||||
}
|
||||
|
||||
void reset() {
|
||||
if (elements != nullptr) {
|
||||
if constexpr (!(std::is_trivially_destructible_v<TKey> && std::is_trivially_destructible_v<TValue>)) {
|
||||
for (uint32_t i = 0; i < num_elements; i++) {
|
||||
elements[i].key.~TKey();
|
||||
elements[i].value.~TValue();
|
||||
}
|
||||
}
|
||||
Memory::free_static(elements);
|
||||
Memory::free_static(map_data);
|
||||
elements = nullptr;
|
||||
}
|
||||
capacity = INITIAL_CAPACITY - 1;
|
||||
num_elements = 0;
|
||||
}
|
||||
|
||||
~AHashMap() {
|
||||
reset();
|
||||
}
|
||||
};
|
||||
|
||||
} //namespace godot
|
||||
@@ -37,6 +37,7 @@
|
||||
|
||||
#include <godot_cpp/core/math.hpp>
|
||||
#include <godot_cpp/core/object.hpp>
|
||||
#include <godot_cpp/templates/pair.hpp>
|
||||
#include <godot_cpp/variant/aabb.hpp>
|
||||
#include <godot_cpp/variant/node_path.hpp>
|
||||
#include <godot_cpp/variant/rect2.hpp>
|
||||
@@ -319,6 +320,13 @@ struct HashMapHasherDefault {
|
||||
template <typename T>
|
||||
static _FORCE_INLINE_ uint32_t hash(const Ref<T> &p_ref) { return hash_one_uint64((uint64_t)p_ref.operator->()); }
|
||||
|
||||
template <typename F, typename S>
|
||||
static _FORCE_INLINE_ uint32_t hash(const Pair<F, S> &p_pair) {
|
||||
uint64_t h1 = hash(p_pair.first);
|
||||
uint64_t h2 = hash(p_pair.second);
|
||||
return hash_one_uint64((h1 << 32) | h2);
|
||||
}
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const String &p_string) { return p_string.hash(); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return hash_fmix32(uint32_t(p_wchar)); }
|
||||
@@ -329,6 +337,7 @@ struct HashMapHasherDefault {
|
||||
static _FORCE_INLINE_ uint32_t hash(const StringName &p_string_name) { return p_string_name.hash(); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const NodePath &p_path) { return p_path.hash(); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const ObjectID &p_id) { return hash_one_uint64(p_id); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const Callable &p_callable) { return p_callable.hash(); }
|
||||
|
||||
static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
|
||||
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash_one_uint64(uint64_t(p_int)); }
|
||||
@@ -376,6 +385,13 @@ struct HashMapHasherDefault {
|
||||
h = hash_murmur3_one_real(p_vec.w, h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Color &p_vec) {
|
||||
uint32_t h = hash_murmur3_one_float(p_vec.r);
|
||||
h = hash_murmur3_one_float(p_vec.g, h);
|
||||
h = hash_murmur3_one_float(p_vec.b, h);
|
||||
h = hash_murmur3_one_float(p_vec.a, h);
|
||||
return hash_fmix32(h);
|
||||
}
|
||||
static _FORCE_INLINE_ uint32_t hash(const Rect2i &p_rect) {
|
||||
uint32_t h = hash_murmur3_one_32(uint32_t(p_rect.position.x));
|
||||
h = hash_murmur3_one_32(uint32_t(p_rect.position.y), h);
|
||||
|
||||
@@ -30,86 +30,70 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <godot_cpp/templates/hashfuncs.hpp>
|
||||
#include <godot_cpp/core/defs.hpp>
|
||||
|
||||
namespace godot {
|
||||
|
||||
template <typename F, typename S>
|
||||
struct Pair {
|
||||
F first;
|
||||
S second;
|
||||
F first{};
|
||||
S second{};
|
||||
|
||||
Pair() :
|
||||
first(),
|
||||
second() {
|
||||
}
|
||||
constexpr Pair() = default;
|
||||
constexpr Pair(const F &p_first, const S &p_second) :
|
||||
first(p_first), second(p_second) {}
|
||||
|
||||
Pair(F p_first, const S &p_second) :
|
||||
first(p_first),
|
||||
second(p_second) {
|
||||
}
|
||||
constexpr bool operator==(const Pair &p_other) const { return first == p_other.first && second == p_other.second; }
|
||||
constexpr bool operator!=(const Pair &p_other) const { return first != p_other.first || second != p_other.second; }
|
||||
constexpr bool operator<(const Pair &p_other) const { return first == p_other.first ? (second < p_other.second) : (first < p_other.first); }
|
||||
constexpr bool operator<=(const Pair &p_other) const { return first == p_other.first ? (second <= p_other.second) : (first < p_other.first); }
|
||||
constexpr bool operator>(const Pair &p_other) const { return first == p_other.first ? (second > p_other.second) : (first > p_other.first); }
|
||||
constexpr bool operator>=(const Pair &p_other) const { return first == p_other.first ? (second >= p_other.second) : (first > p_other.first); }
|
||||
};
|
||||
|
||||
template <typename F, typename S>
|
||||
bool operator==(const Pair<F, S> &pair, const Pair<F, S> &other) {
|
||||
return (pair.first == other.first) && (pair.second == other.second);
|
||||
}
|
||||
|
||||
template <typename F, typename S>
|
||||
bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) {
|
||||
return (pair.first != other.first) || (pair.second != other.second);
|
||||
}
|
||||
|
||||
template <typename F, typename S>
|
||||
struct PairSort {
|
||||
bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const {
|
||||
if (A.first != B.first) {
|
||||
return A.first < B.first;
|
||||
}
|
||||
return A.second < B.second;
|
||||
constexpr bool operator()(const Pair<F, S> &p_lhs, const Pair<F, S> &p_rhs) const {
|
||||
return p_lhs < p_rhs;
|
||||
}
|
||||
};
|
||||
|
||||
// Pair is zero-constructible if and only if both constrained types are zero-constructible.
|
||||
template <typename F, typename S>
|
||||
struct PairHash {
|
||||
static uint32_t hash(const Pair<F, S> &P) {
|
||||
uint64_t h1 = HashMapHasherDefault::hash(P.first);
|
||||
uint64_t h2 = HashMapHasherDefault::hash(P.second);
|
||||
return hash_one_uint64((h1 << 32) | h2);
|
||||
}
|
||||
};
|
||||
struct is_zero_constructible<Pair<F, S>> : std::conjunction<is_zero_constructible<F>, is_zero_constructible<S>> {};
|
||||
|
||||
template <typename K, typename V>
|
||||
struct KeyValue {
|
||||
const K key;
|
||||
V value;
|
||||
const K key{};
|
||||
V value{};
|
||||
|
||||
void operator=(const KeyValue &p_kv) = delete;
|
||||
_FORCE_INLINE_ KeyValue(const KeyValue &p_kv) :
|
||||
key(p_kv.key),
|
||||
value(p_kv.value) {
|
||||
}
|
||||
_FORCE_INLINE_ KeyValue(const K &p_key, const V &p_value) :
|
||||
key(p_key),
|
||||
value(p_value) {
|
||||
}
|
||||
KeyValue &operator=(const KeyValue &p_kv) = delete;
|
||||
KeyValue &operator=(KeyValue &&p_kv) = delete;
|
||||
|
||||
constexpr KeyValue(const KeyValue &p_kv) = default;
|
||||
constexpr KeyValue(KeyValue &&p_kv) = default;
|
||||
constexpr KeyValue(const K &p_key, const V &p_value) :
|
||||
key(p_key), value(p_value) {}
|
||||
constexpr KeyValue(const Pair<K, V> &p_pair) :
|
||||
key(p_pair.first), value(p_pair.second) {}
|
||||
|
||||
constexpr bool operator==(const KeyValue &p_other) const { return key == p_other.key && value == p_other.value; }
|
||||
constexpr bool operator!=(const KeyValue &p_other) const { return key != p_other.key || value != p_other.value; }
|
||||
constexpr bool operator<(const KeyValue &p_other) const { return key == p_other.key ? (value < p_other.value) : (key < p_other.key); }
|
||||
constexpr bool operator<=(const KeyValue &p_other) const { return key == p_other.key ? (value <= p_other.value) : (key < p_other.key); }
|
||||
constexpr bool operator>(const KeyValue &p_other) const { return key == p_other.key ? (value > p_other.value) : (key > p_other.key); }
|
||||
constexpr bool operator>=(const KeyValue &p_other) const { return key == p_other.key ? (value >= p_other.value) : (key > p_other.key); }
|
||||
};
|
||||
|
||||
template <typename K, typename V>
|
||||
bool operator==(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
|
||||
return (pair.key == other.key) && (pair.value == other.value);
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
bool operator!=(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
|
||||
return (pair.key != other.key) || (pair.value != other.value);
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
struct KeyValueSort {
|
||||
bool operator()(const KeyValue<K, V> &A, const KeyValue<K, V> &B) const {
|
||||
return A.key < B.key;
|
||||
constexpr bool operator()(const KeyValue<K, V> &p_lhs, const KeyValue<K, V> &p_rhs) const {
|
||||
return p_lhs.key < p_rhs.key;
|
||||
}
|
||||
};
|
||||
|
||||
// KeyValue is zero-constructible if and only if both constrained types are zero-constructible.
|
||||
template <typename K, typename V>
|
||||
struct is_zero_constructible<KeyValue<K, V>> : std::conjunction<is_zero_constructible<K>, is_zero_constructible<V>> {};
|
||||
|
||||
} // namespace godot
|
||||
|
||||
@@ -36,14 +36,12 @@
|
||||
|
||||
#include <godot_cpp/core/memory.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace godot {
|
||||
|
||||
std::unordered_map<StringName, ClassDB::ClassInfo> ClassDB::classes;
|
||||
std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *> ClassDB::instance_binding_callbacks;
|
||||
std::vector<StringName> ClassDB::class_register_order;
|
||||
std::unordered_map<StringName, Object *> ClassDB::engine_singletons;
|
||||
AHashMap<StringName, ClassDB::ClassInfo> ClassDB::classes;
|
||||
AHashMap<StringName, const GDExtensionInstanceBindingCallbacks *> ClassDB::instance_binding_callbacks;
|
||||
LocalVector<StringName> ClassDB::class_register_order;
|
||||
AHashMap<StringName, Object *> ClassDB::engine_singletons;
|
||||
std::mutex ClassDB::engine_singletons_mutex;
|
||||
GDExtensionInitializationLevel ClassDB::current_level = GDEXTENSION_INITIALIZATION_CORE;
|
||||
|
||||
@@ -116,9 +114,9 @@ MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_m
|
||||
|
||||
ClassInfo *type = &classes[p_class];
|
||||
while (type) {
|
||||
std::unordered_map<StringName, MethodBind *>::iterator method = type->method_map.find(p_method);
|
||||
AHashMap<StringName, MethodBind *>::Iterator method = type->method_map.find(p_method);
|
||||
if (method != type->method_map.end()) {
|
||||
return method->second;
|
||||
return method->value;
|
||||
}
|
||||
type = type->parent_ptr;
|
||||
continue;
|
||||
@@ -130,13 +128,13 @@ MethodBind *ClassDB::get_method(const StringName &p_class, const StringName &p_m
|
||||
MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const MethodDefinition &method_name, const void **p_defs, int p_defcount) {
|
||||
StringName instance_type = p_bind->get_instance_class();
|
||||
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(instance_type);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(instance_type);
|
||||
if (type_it == classes.end()) {
|
||||
memdelete(p_bind);
|
||||
ERR_FAIL_V_MSG(nullptr, String("Class '{0}' doesn't exist.").format(Array::make(instance_type)));
|
||||
}
|
||||
|
||||
ClassInfo &type = type_it->second;
|
||||
ClassInfo &type = type_it->value;
|
||||
|
||||
if (type.method_map.find(method_name.name) != type.method_map.end()) {
|
||||
memdelete(p_bind);
|
||||
@@ -157,7 +155,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const M
|
||||
|
||||
p_bind->set_hint_flags(p_flags);
|
||||
|
||||
std::vector<StringName> args;
|
||||
LocalVector<StringName> args;
|
||||
args.resize(method_name.args.size());
|
||||
size_t arg_index = 0;
|
||||
for (StringName arg : method_name.args) {
|
||||
@@ -166,7 +164,7 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const M
|
||||
|
||||
p_bind->set_argument_names(args);
|
||||
|
||||
std::vector<Variant> defvals;
|
||||
LocalVector<Variant> defvals;
|
||||
|
||||
defvals.resize(p_defcount);
|
||||
for (int i = 0; i < p_defcount; i++) {
|
||||
@@ -186,34 +184,34 @@ MethodBind *ClassDB::bind_methodfi(uint32_t p_flags, MethodBind *p_bind, const M
|
||||
}
|
||||
|
||||
void ClassDB::bind_method_godot(const StringName &p_class_name, MethodBind *p_method) {
|
||||
std::vector<GDExtensionVariantPtr> def_args;
|
||||
const std::vector<Variant> &def_args_val = p_method->get_default_arguments();
|
||||
LocalVector<GDExtensionVariantPtr> def_args;
|
||||
const LocalVector<Variant> &def_args_val = p_method->get_default_arguments();
|
||||
def_args.resize(def_args_val.size());
|
||||
for (size_t i = 0; i < def_args_val.size(); i++) {
|
||||
def_args[i] = (GDExtensionVariantPtr)&def_args_val[i];
|
||||
}
|
||||
|
||||
std::vector<PropertyInfo> return_value_and_arguments_info = p_method->get_arguments_info_list();
|
||||
std::vector<GDExtensionClassMethodArgumentMetadata> return_value_and_arguments_metadata = p_method->get_arguments_metadata_list();
|
||||
LocalVector<PropertyInfo> return_value_and_arguments_info = p_method->get_arguments_info_list();
|
||||
LocalVector<GDExtensionClassMethodArgumentMetadata> return_value_and_arguments_metadata = p_method->get_arguments_metadata_list();
|
||||
|
||||
std::vector<GDExtensionPropertyInfo> return_value_and_arguments_gdextension_info;
|
||||
LocalVector<GDExtensionPropertyInfo> return_value_and_arguments_gdextension_info;
|
||||
return_value_and_arguments_gdextension_info.reserve(return_value_and_arguments_info.size());
|
||||
for (std::vector<PropertyInfo>::iterator it = return_value_and_arguments_info.begin(); it != return_value_and_arguments_info.end(); it++) {
|
||||
for (const PropertyInfo &info : return_value_and_arguments_info) {
|
||||
return_value_and_arguments_gdextension_info.push_back(
|
||||
GDExtensionPropertyInfo{
|
||||
static_cast<GDExtensionVariantType>(it->type), // GDExtensionVariantType type;
|
||||
it->name._native_ptr(), // GDExtensionStringNamePtr name;
|
||||
it->class_name._native_ptr(), // GDExtensionStringNamePtr class_name;
|
||||
it->hint, // uint32_t hint;
|
||||
it->hint_string._native_ptr(), // GDExtensionStringPtr hint_string;
|
||||
it->usage, // uint32_t usage;
|
||||
static_cast<GDExtensionVariantType>(info.type), // GDExtensionVariantType type;
|
||||
info.name._native_ptr(), // GDExtensionStringNamePtr name;
|
||||
info.class_name._native_ptr(), // GDExtensionStringNamePtr class_name;
|
||||
info.hint, // uint32_t hint;
|
||||
info.hint_string._native_ptr(), // GDExtensionStringPtr hint_string;
|
||||
info.usage, // uint32_t usage;
|
||||
});
|
||||
}
|
||||
|
||||
GDExtensionPropertyInfo *return_value_info = return_value_and_arguments_gdextension_info.data();
|
||||
GDExtensionClassMethodArgumentMetadata *return_value_metadata = return_value_and_arguments_metadata.data();
|
||||
GDExtensionPropertyInfo *arguments_info = return_value_and_arguments_gdextension_info.data() + 1;
|
||||
GDExtensionClassMethodArgumentMetadata *arguments_metadata = return_value_and_arguments_metadata.data() + 1;
|
||||
GDExtensionPropertyInfo *return_value_info = return_value_and_arguments_gdextension_info.ptr();
|
||||
GDExtensionClassMethodArgumentMetadata *return_value_metadata = return_value_and_arguments_metadata.ptr();
|
||||
GDExtensionPropertyInfo *arguments_info = return_value_and_arguments_gdextension_info.ptr() + 1;
|
||||
GDExtensionClassMethodArgumentMetadata *arguments_metadata = return_value_and_arguments_metadata.ptr() + 1;
|
||||
|
||||
StringName name = p_method->get_name();
|
||||
GDExtensionClassMethodInfo method_info = {
|
||||
@@ -229,17 +227,17 @@ void ClassDB::bind_method_godot(const StringName &p_class_name, MethodBind *p_me
|
||||
arguments_info, // GDExtensionPropertyInfo *
|
||||
arguments_metadata, // GDExtensionClassMethodArgumentMetadata *
|
||||
(uint32_t)p_method->get_default_argument_count(), // uint32_t default_argument_count;
|
||||
def_args.data(), // GDExtensionVariantPtr *default_arguments;
|
||||
def_args.ptr(), // GDExtensionVariantPtr *default_arguments;
|
||||
};
|
||||
internal::gdextension_interface_classdb_register_extension_class_method(internal::library, p_class_name._native_ptr(), &method_info);
|
||||
}
|
||||
|
||||
void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal) {
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(p_class);
|
||||
|
||||
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
|
||||
|
||||
ClassInfo &cl = type_it->second;
|
||||
ClassInfo &cl = type_it->value;
|
||||
|
||||
// Check if this signal is already register
|
||||
ClassInfo *check = &cl;
|
||||
@@ -252,7 +250,7 @@ void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal)
|
||||
cl.signal_names.insert(p_signal.name);
|
||||
|
||||
// register our signal in godot
|
||||
std::vector<GDExtensionPropertyInfo> parameters;
|
||||
LocalVector<GDExtensionPropertyInfo> parameters;
|
||||
parameters.reserve(p_signal.arguments.size());
|
||||
|
||||
for (const PropertyInfo &par : p_signal.arguments) {
|
||||
@@ -266,15 +264,15 @@ void ClassDB::add_signal(const StringName &p_class, const MethodInfo &p_signal)
|
||||
});
|
||||
}
|
||||
|
||||
internal::gdextension_interface_classdb_register_extension_class_signal(internal::library, cl.name._native_ptr(), p_signal.name._native_ptr(), parameters.data(), parameters.size());
|
||||
internal::gdextension_interface_classdb_register_extension_class_signal(internal::library, cl.name._native_ptr(), p_signal.name._native_ptr(), parameters.ptr(), parameters.size());
|
||||
}
|
||||
|
||||
void ClassDB::bind_integer_constant(const StringName &p_class_name, const StringName &p_enum_name, const StringName &p_constant_name, GDExtensionInt p_constant_value, bool p_is_bitfield) {
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class_name);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(p_class_name);
|
||||
|
||||
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class_name)));
|
||||
|
||||
ClassInfo &type = type_it->second;
|
||||
ClassInfo &type = type_it->value;
|
||||
|
||||
// check if it already exists
|
||||
ERR_FAIL_COND_MSG(type.constant_names.find(p_constant_name) != type.constant_names.end(), String("Constant '{0}::{1}' already registered.").format(Array::make(p_class_name, p_constant_name)));
|
||||
@@ -292,17 +290,17 @@ GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtens
|
||||
const StringName *class_name = reinterpret_cast<const StringName *>(p_userdata);
|
||||
const StringName *name = reinterpret_cast<const StringName *>(p_name);
|
||||
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(*class_name);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(*class_name);
|
||||
ERR_FAIL_COND_V_MSG(type_it == classes.end(), nullptr, String("Class '{0}' doesn't exist.").format(Array::make(*class_name)));
|
||||
|
||||
const ClassInfo *type = &type_it->second;
|
||||
const ClassInfo *type = &type_it->value;
|
||||
|
||||
// Find method in current class, or any of its parent classes (Godot classes not included)
|
||||
while (type != nullptr) {
|
||||
std::unordered_map<StringName, ClassInfo::VirtualMethod>::const_iterator method_it = type->virtual_methods.find(*name);
|
||||
AHashMap<StringName, ClassInfo::VirtualMethod>::ConstIterator method_it = type->virtual_methods.find(*name);
|
||||
|
||||
if (method_it != type->virtual_methods.end() && method_it->second.hash == p_hash) {
|
||||
return method_it->second.func;
|
||||
if (method_it != type->virtual_methods.end() && method_it->value.hash == p_hash) {
|
||||
return method_it->value.func;
|
||||
}
|
||||
|
||||
type = type->parent_ptr;
|
||||
@@ -312,9 +310,9 @@ GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtens
|
||||
}
|
||||
|
||||
const GDExtensionInstanceBindingCallbacks *ClassDB::get_instance_binding_callbacks(const StringName &p_class) {
|
||||
std::unordered_map<StringName, const GDExtensionInstanceBindingCallbacks *>::iterator callbacks_it = instance_binding_callbacks.find(p_class);
|
||||
AHashMap<StringName, const GDExtensionInstanceBindingCallbacks *>::Iterator callbacks_it = instance_binding_callbacks.find(p_class);
|
||||
if (likely(callbacks_it != instance_binding_callbacks.end())) {
|
||||
return callbacks_it->second;
|
||||
return callbacks_it->value;
|
||||
}
|
||||
|
||||
// If we don't have an instance binding callback for the given class, find the closest parent where we do.
|
||||
@@ -325,14 +323,14 @@ const GDExtensionInstanceBindingCallbacks *ClassDB::get_instance_binding_callbac
|
||||
callbacks_it = instance_binding_callbacks.find(class_name);
|
||||
} while (callbacks_it == instance_binding_callbacks.end());
|
||||
|
||||
return callbacks_it->second;
|
||||
return callbacks_it->value;
|
||||
}
|
||||
|
||||
void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call, uint32_t p_hash) {
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(p_class);
|
||||
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
|
||||
|
||||
ClassInfo &type = type_it->second;
|
||||
ClassInfo &type = type_it->value;
|
||||
|
||||
ERR_FAIL_COND_MSG(type.method_map.find(p_method) != type.method_map.end(), String("Method '{0}::{1}()' already registered as non-virtual.").format(Array::make(p_class, p_method)));
|
||||
ERR_FAIL_COND_MSG(type.virtual_methods.find(p_method) != type.virtual_methods.end(), String("Virtual '{0}::{1}()' method already registered.").format(Array::make(p_class, p_method)));
|
||||
@@ -344,7 +342,7 @@ void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p
|
||||
}
|
||||
|
||||
void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names) {
|
||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
|
||||
AHashMap<StringName, ClassInfo>::Iterator type_it = classes.find(p_class);
|
||||
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
|
||||
|
||||
GDExtensionClassVirtualMethodInfo mi;
|
||||
@@ -392,8 +390,8 @@ void ClassDB::initialize_class(const ClassInfo &p_cl) {
|
||||
}
|
||||
|
||||
void ClassDB::initialize(GDExtensionInitializationLevel p_level) {
|
||||
for (const std::pair<const StringName, ClassInfo> &pair : classes) {
|
||||
const ClassInfo &cl = pair.second;
|
||||
for (const KeyValue<StringName, ClassInfo> &pair : classes) {
|
||||
const ClassInfo &cl = pair.value;
|
||||
if (cl.level != p_level) {
|
||||
continue;
|
||||
}
|
||||
@@ -404,8 +402,8 @@ void ClassDB::initialize(GDExtensionInitializationLevel p_level) {
|
||||
|
||||
void ClassDB::deinitialize(GDExtensionInitializationLevel p_level) {
|
||||
std::set<StringName> to_erase;
|
||||
for (std::vector<StringName>::reverse_iterator i = class_register_order.rbegin(); i != class_register_order.rend(); ++i) {
|
||||
const StringName &name = *i;
|
||||
for (int i = class_register_order.size() - 1; i >= 0; --i) {
|
||||
const StringName &name = class_register_order[i];
|
||||
const ClassInfo &cl = classes[name];
|
||||
|
||||
if (cl.level != p_level) {
|
||||
@@ -414,8 +412,8 @@ void ClassDB::deinitialize(GDExtensionInitializationLevel p_level) {
|
||||
|
||||
internal::gdextension_interface_classdb_unregister_extension_class(internal::library, name._native_ptr());
|
||||
|
||||
for (const std::pair<const StringName, MethodBind *> &method : cl.method_map) {
|
||||
memdelete(method.second);
|
||||
for (const KeyValue<StringName, MethodBind *> &method : cl.method_map) {
|
||||
memdelete(method.value);
|
||||
}
|
||||
|
||||
classes.erase(name);
|
||||
@@ -423,26 +421,24 @@ void ClassDB::deinitialize(GDExtensionInitializationLevel p_level) {
|
||||
}
|
||||
|
||||
{
|
||||
// The following is equivalent to c++20 `std::erase_if(class_register_order, [&](const StringName& name){ return to_erase.contains(name); });`
|
||||
std::vector<StringName>::iterator it = std::remove_if(class_register_order.begin(), class_register_order.end(), [&](const StringName &p_name) {
|
||||
return to_erase.count(p_name) > 0;
|
||||
});
|
||||
class_register_order.erase(it, class_register_order.end());
|
||||
for (const StringName &x : to_erase) {
|
||||
class_register_order.erase(x);
|
||||
}
|
||||
}
|
||||
|
||||
if (p_level == GDEXTENSION_INITIALIZATION_CORE) {
|
||||
// Make a new list of the singleton objects, since freeing the instance bindings will lead to
|
||||
// elements getting removed from engine_singletons.
|
||||
std::vector<Object *> singleton_objects;
|
||||
LocalVector<Object *> singleton_objects;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(engine_singletons_mutex);
|
||||
singleton_objects.reserve(engine_singletons.size());
|
||||
for (const std::pair<const StringName, Object *> &pair : engine_singletons) {
|
||||
singleton_objects.push_back(pair.second);
|
||||
for (const KeyValue<StringName, Object *> &pair : engine_singletons) {
|
||||
singleton_objects.push_back(pair.value);
|
||||
}
|
||||
}
|
||||
for (std::vector<Object *>::iterator i = singleton_objects.begin(); i != singleton_objects.end(); i++) {
|
||||
internal::gdextension_interface_object_free_instance_binding((*i)->_owner, internal::token);
|
||||
for (const Object *i : singleton_objects) {
|
||||
internal::gdextension_interface_object_free_instance_binding((*i)._owner, internal::token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,11 +56,11 @@ void MethodBind::set_name(const StringName &p_name) {
|
||||
name = p_name;
|
||||
}
|
||||
|
||||
void MethodBind::set_argument_names(const std::vector<StringName> &p_names) {
|
||||
void MethodBind::set_argument_names(const LocalVector<StringName> &p_names) {
|
||||
argument_names = p_names;
|
||||
}
|
||||
|
||||
std::vector<StringName> MethodBind::get_argument_names() const {
|
||||
LocalVector<StringName> MethodBind::get_argument_names() const {
|
||||
return argument_names;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ Object *get_object_instance_binding(GodotObject *p_engine_object) {
|
||||
return reinterpret_cast<Object *>(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks));
|
||||
}
|
||||
|
||||
TypedArray<Dictionary> convert_property_list(const std::vector<PropertyInfo> &p_list) {
|
||||
TypedArray<Dictionary> convert_property_list(const LocalVector<PropertyInfo> &p_list) {
|
||||
TypedArray<Dictionary> va;
|
||||
for (const PropertyInfo &pi : p_list) {
|
||||
va.push_back(Dictionary(pi));
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
#include <godot_cpp/core/error_macros.hpp>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
namespace godot {
|
||||
|
||||
namespace internal {
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
import header_builders
|
||||
from SCons import __version__ as scons_raw_version
|
||||
from SCons.Action import Action
|
||||
from SCons.Builder import Builder
|
||||
@@ -529,6 +530,10 @@ def generate(env):
|
||||
BUILDERS={
|
||||
"GodotCPPBindings": Builder(action=Action(scons_generate_bindings, "$GENCOMSTR"), emitter=scons_emit_files),
|
||||
"GodotCPPDocData": Builder(action=scons_generate_doc_source),
|
||||
"GLSL_HEADER": Builder(
|
||||
action=header_builders.build_raw_headers_action,
|
||||
suffix="glsl.gen.h",
|
||||
),
|
||||
}
|
||||
)
|
||||
env.AddMethod(_godot_cpp, "GodotCPP")
|
||||
|
||||
69
tools/header_builders.py
Normal file
69
tools/header_builders.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import os.path
|
||||
|
||||
|
||||
## See https://github.com/godotengine/godot/blob/master/glsl_builders.py
|
||||
def build_raw_header(source_filename: str, constant_name: str) -> None:
|
||||
# Read the source file content.
|
||||
with open(source_filename, "r") as source_file:
|
||||
source_content = source_file.read()
|
||||
constant_name = constant_name.replace(".", "_")
|
||||
# Build header content using a C raw string literal.
|
||||
header_content = (
|
||||
"/* THIS FILE IS GENERATED. EDITS WILL BE LOST. */\n\n"
|
||||
"#pragma once\n\n"
|
||||
f"inline constexpr const char *{constant_name}"
|
||||
" = "
|
||||
f'R"<!>({source_content})<!>"'
|
||||
";\n"
|
||||
)
|
||||
# Write the header to the provided file name with a ".gen.h" suffix.
|
||||
header_filename = f"{source_filename}.gen.h"
|
||||
with open(header_filename, "w") as header_file:
|
||||
header_file.write(header_content)
|
||||
|
||||
|
||||
def build_raw_headers_action(target, source, env):
|
||||
env.NoCache(target)
|
||||
for src in source:
|
||||
source_filename = str(src)
|
||||
# To match Godot, replace ".glsl" with "_shader_glsl". Does nothing for non-GLSL files.
|
||||
constant_name = os.path.basename(source_filename).replace(".glsl", "_shader_glsl")
|
||||
build_raw_header(source_filename, constant_name)
|
||||
|
||||
|
||||
def escape_svg(filename: str) -> str:
|
||||
with open(filename, encoding="utf-8", newline="\n") as svg_file:
|
||||
svg_content = svg_file.read()
|
||||
return f'R"<!>({svg_content})<!>"'
|
||||
|
||||
|
||||
## See https://github.com/godotengine/godot/blob/master/editor/icons/editor_icons_builders.py
|
||||
## See https://github.com/godotengine/godot/blob/master/scene/theme/icons/default_theme_icons_builders.py
|
||||
def make_svg_icons_action(target, source, env):
|
||||
destination = str(target[0])
|
||||
constant_prefix = os.path.basename(destination).replace(".gen.h", "")
|
||||
svg_icons = [str(x) for x in source]
|
||||
# Convert the SVG icons to escaped strings and convert their names to C strings.
|
||||
icon_names = [f'"{os.path.basename(fname)[:-4]}"' for fname in svg_icons]
|
||||
icon_sources = [escape_svg(fname) for fname in svg_icons]
|
||||
# Join them as indented comma-separated items for use in an array initializer.
|
||||
icon_names_str = ",\n\t".join(icon_names)
|
||||
icon_sources_str = ",\n\t".join(icon_sources)
|
||||
# Write the file to disk.
|
||||
with open(destination, "w", encoding="utf-8", newline="\n") as destination_file:
|
||||
destination_file.write(
|
||||
f"""\
|
||||
/* THIS FILE IS GENERATED. EDITS WILL BE LOST. */
|
||||
|
||||
#pragma once
|
||||
|
||||
inline constexpr int {constant_prefix}_count = {len(icon_names)};
|
||||
inline constexpr const char *{constant_prefix}_sources[] = {{
|
||||
{icon_sources_str}
|
||||
}};
|
||||
|
||||
inline constexpr const char *{constant_prefix}_names[] = {{
|
||||
{icon_names_str}
|
||||
}};
|
||||
"""
|
||||
)
|
||||
Reference in New Issue
Block a user