Compare commits
68 Commits
3ddc2b3d97
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| ed381c4178 | |||
| 450f725cab | |||
| 93b58ea197 | |||
| 6c78ecbeb1 | |||
| 7c2f89b331 | |||
| c1be5385d3 | |||
| ecf1cfc29c | |||
| 184bc7fcdf | |||
| 1f6637408d | |||
| 9f499c933d | |||
| 88e33bdcc8 | |||
| 9e6f00eb60 | |||
| e7503ed92f | |||
| aee4e340dd | |||
| 520a0e1363 | |||
| 97f5bbfe00 | |||
| b64ce44d4e | |||
| 1acf00138a | |||
| 5dcb58f53c | |||
| a1bdc077b1 | |||
| 9553f9b662 | |||
| 9645856554 | |||
| a2abb58705 | |||
| 7f1dd245dc | |||
| 6d58105734 | |||
| d928d86014 | |||
| 0b76b06a1b | |||
| 6f09c3f7fe | |||
| fe8c3a4602 | |||
| b9026ec8da | |||
| 7b87828f06 | |||
| 8925b3f2f0 | |||
| f636feb4f1 | |||
| 1a9a80e37f | |||
| 18c8171847 | |||
| d546519180 | |||
| f208141b5b | |||
| 52d6c62f76 | |||
| 037e2554a1 | |||
| 8867576a2e | |||
| 788f63d092 | |||
| a35f2a699d | |||
| 80925965d4 | |||
| f2a45aa913 | |||
| 3565bbbc52 | |||
| 375492ef7b | |||
| dbcb50349d | |||
| 992a02db3e | |||
| e1eaf97961 | |||
| 086c73f058 | |||
| 339f5c8cd8 | |||
| 18c0a7099d | |||
| 540c7fbce8 | |||
| cbcd699ab0 | |||
| ff27caab4f | |||
| fe4c49d092 | |||
| 037c62bf12 | |||
| 494d766741 | |||
| 83f0c01e29 | |||
| 4ff739d625 | |||
| 7cd38604a7 | |||
| 733fca41ef | |||
| 55a8c54119 | |||
| 27754a56d7 | |||
| fcf9c6adcb | |||
| e6c0a60ea9 | |||
| 5252ba84c9 | |||
| 73041e994d |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@
|
||||
/docs/
|
||||
/bin/
|
||||
/lib/
|
||||
/doxy/README.md
|
||||
/doxy/Doxyfile.out
|
||||
|
||||
220
CMakeLists.txt
220
CMakeLists.txt
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -16,17 +16,45 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
cmake_minimum_required(VERSION 3.30)
|
||||
cmake_minimum_required(VERSION 3.28...3.31)
|
||||
|
||||
project(fennec)
|
||||
set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
macro(fennec_add_sources)
|
||||
list(APPEND FENNEC_SOURCES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_definitions)
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_link_libraries)
|
||||
list(APPEND FENNEC_LINK_LIBRARIES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_shared_libraries)
|
||||
list(APPEND FENNEC_SHARED_LIBRARIES ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_compile_options)
|
||||
list(APPEND FENNEC_PRIVATE_COMPILE_OPTIONS ${ARGN})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_add_link_options)
|
||||
list(APPEND FENNEC_PRIVATE_LINK_OPTIONS ${ARGN})
|
||||
endmacro()
|
||||
|
||||
# External dependencies should be loaded here
|
||||
|
||||
# CppTrace is a dependency of the project, added as a git submodule
|
||||
add_subdirectory(external/cpptrace)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_C_STANDARD 23)
|
||||
set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
add_custom_target(fennec-dependencies
|
||||
COMMAND ${CMAKE_COMMAND} -E echo "Running dependencies."
|
||||
COMMENT "Running dependencies."
|
||||
)
|
||||
|
||||
# include scripts
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/version.cmake")
|
||||
@@ -47,75 +75,125 @@ include_directories(${FENNEC_SOURCE_DIR}/include)
|
||||
# Metaprogramming is a dependency for generating various type info before compilation of the engine.
|
||||
add_subdirectory(metaprogramming)
|
||||
|
||||
|
||||
# Specify where to send libraries and executables
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
||||
|
||||
# add the test suite as a sub-project
|
||||
add_subdirectory(test)
|
||||
|
||||
add_library(fennec STATIC
|
||||
|
||||
# Core Files
|
||||
fennec_add_sources(
|
||||
# CORE =================================================================================================================
|
||||
include/fennec/core/engine.h source/core/engine.cpp
|
||||
include/fennec/core/event.h source/core/event.cpp
|
||||
include/fennec/core/logger.h source/core/logger.cpp
|
||||
|
||||
include/fennec/core/version.h
|
||||
include/fennec/core/system.h
|
||||
|
||||
|
||||
|
||||
# SCENE ================================================================================================================
|
||||
include/fennec/scene/scene.h
|
||||
include/fennec/scene/scene.h source/scene/scene.cpp
|
||||
include/fennec/scene/component.h
|
||||
include/fennec/scene/scene_node.h
|
||||
include/fennec/scene/node2d.h
|
||||
include/fennec/scene/forward.h
|
||||
|
||||
|
||||
|
||||
|
||||
# RENDERERS ============================================================================================================
|
||||
include/fennec/renderers/interface/forward.h
|
||||
|
||||
include/fennec/renderers/interface/gfxcontext.h
|
||||
include/fennec/renderers/interface/gfxsurface.h
|
||||
|
||||
|
||||
|
||||
# CONTAINERS ===========================================================================================================
|
||||
include/fennec/containers/containers.h
|
||||
|
||||
include/fennec/containers/array.h
|
||||
include/fennec/containers/bintree.h
|
||||
include/fennec/containers/bitfield.h
|
||||
include/fennec/containers/deque.h
|
||||
include/fennec/containers/dynarray.h
|
||||
include/fennec/containers/generic.h
|
||||
include/fennec/containers/graph.h
|
||||
include/fennec/containers/initializer_list.h
|
||||
include/fennec/containers/list.h
|
||||
include/fennec/containers/map.h
|
||||
include/fennec/containers/object_pool.h
|
||||
include/fennec/containers/optional.h
|
||||
include/fennec/containers/pair.h
|
||||
include/fennec/containers/priority_queue.h
|
||||
include/fennec/containers/rdtree.h
|
||||
include/fennec/containers/sequence.h
|
||||
include/fennec/containers/set.h
|
||||
include/fennec/containers/traversal.h
|
||||
include/fennec/containers/tuple.h
|
||||
include/fennec/containers/variant.h
|
||||
|
||||
|
||||
include/fennec/containers/detail/_tuple.h
|
||||
|
||||
|
||||
# LANG =================================================================================================================
|
||||
|
||||
# lang =================================================================================================================
|
||||
include/fennec/lang/lang.h
|
||||
include/fennec/lang/metaprogramming.h
|
||||
|
||||
|
||||
include/fennec/lang/bits.h
|
||||
include/fennec/lang/constants.h
|
||||
include/fennec/lang/conditional_types.h
|
||||
include/fennec/lang/declval.h
|
||||
include/fennec/lang/function.h
|
||||
include/fennec/lang/hashing.h
|
||||
include/fennec/lang/intrinsics.h
|
||||
include/fennec/lang/limits.h
|
||||
include/fennec/lang/numeric_transforms.h
|
||||
include/fennec/lang/const_sequences.h
|
||||
include/fennec/lang/startup.h
|
||||
include/fennec/lang/metasequences.h
|
||||
include/fennec/lang/ranges.h
|
||||
include/fennec/lang/static_constructor.h
|
||||
include/fennec/lang/type_identity.h
|
||||
include/fennec/lang/type_operators.h
|
||||
include/fennec/lang/type_sequences.h
|
||||
include/fennec/lang/type_traits.h
|
||||
include/fennec/lang/type_transforms.h
|
||||
include/fennec/lang/typeuuid.h
|
||||
include/fennec/lang/types.h
|
||||
include/fennec/lang/utility.h
|
||||
include/fennec/lang/integer.h
|
||||
|
||||
include/fennec/lang/assert.h source/lang/assert.cpp
|
||||
|
||||
include/fennec/lang/detail/_bits.h
|
||||
include/fennec/lang/detail/_declval.h
|
||||
include/fennec/lang/detail/_function.h
|
||||
include/fennec/lang/detail/_int.h
|
||||
include/fennec/lang/detail/_numeric_transforms.h
|
||||
include/fennec/lang/detail/_stdlib.h
|
||||
include/fennec/lang/detail/_type_traits.h
|
||||
include/fennec/lang/detail/_type_transforms.h
|
||||
include/fennec/lang/detail/_type_sequences.h
|
||||
include/fennec/lang/detail/_typeuuid.h
|
||||
|
||||
include/fennec/lang/assert.h source/lang/assert.cpp
|
||||
|
||||
# RTTI =================================================================================================================
|
||||
include/fennec/rtti/typeid.h
|
||||
include/fennec/rtti/type_data.h
|
||||
include/fennec/rtti/type.h
|
||||
include/fennec/rtti/enable.h
|
||||
include/fennec/rtti/forward.h
|
||||
include/fennec/rtti/typelist.h
|
||||
include/fennec/rtti/type_registry.h
|
||||
include/fennec/rtti/singleton.h
|
||||
|
||||
|
||||
include/fennec/rtti/detail/_constants.h
|
||||
include/fennec/rtti/detail/_this_t.h
|
||||
include/fennec/rtti/detail/_typeid.h
|
||||
include/fennec/rtti/detail/_type_name.h
|
||||
|
||||
|
||||
|
||||
# MEMORY ===============================================================================================================
|
||||
@@ -130,15 +208,13 @@ add_library(fennec STATIC
|
||||
|
||||
include/fennec/memory/detail/_ptr_traits.h
|
||||
|
||||
# CONCURRENCY ==========================================================================================================
|
||||
include/fennec/concurrency/thread.h
|
||||
include/fennec/concurrency/mutex.h
|
||||
include/fennec/concurrency/atomic.h
|
||||
|
||||
|
||||
# DEBUG ================================================================================================================
|
||||
source/debug/assert_impl.cpp
|
||||
|
||||
|
||||
|
||||
# MATH =================================================================================================================
|
||||
include/fennec/math/math.h
|
||||
|
||||
@@ -164,64 +240,107 @@ add_library(fennec STATIC
|
||||
include/fennec/math/ext/constants.h
|
||||
include/fennec/math/ext/primes.h
|
||||
include/fennec/math/ext/quaternion.h
|
||||
include/fennec/math/ext/rect.h
|
||||
include/fennec/math/ext/transform.h
|
||||
include/fennec/math/ext/trigonometric.h
|
||||
|
||||
|
||||
include/fennec/math/detail/_fwd.h
|
||||
include/fennec/math/detail/_forward.h
|
||||
include/fennec/math/detail/_math.h
|
||||
include/fennec/math/detail/_matrix.h
|
||||
include/fennec/math/detail/_types.h
|
||||
include/fennec/math/detail/_vector_traits.h
|
||||
|
||||
|
||||
# langproc ================================================================================================================
|
||||
|
||||
# Strings
|
||||
include/fennec/langproc/strings/cstring.h
|
||||
include/fennec/langproc/strings/locale.h
|
||||
include/fennec/langproc/strings/string.h
|
||||
# threading ============================================================================================================
|
||||
|
||||
include/fennec/langproc/strings/detail/_ctype.h
|
||||
include/fennec/threading/atomic.h
|
||||
include/fennec/threading/lock_guard.h
|
||||
include/fennec/threading/mpscq.h
|
||||
include/fennec/threading/mutex.h
|
||||
include/fennec/threading/thread.h
|
||||
|
||||
include/fennec/threading/detail/_thread.h
|
||||
|
||||
|
||||
|
||||
# string ===============================================================================================================
|
||||
include/fennec/string/locale.h
|
||||
include/fennec/string/cstring.h
|
||||
include/fennec/string/string.h
|
||||
|
||||
include/fennec/string/detail/_ctype.h
|
||||
|
||||
|
||||
|
||||
# format ===============================================================================================================
|
||||
include/fennec/format/format.h
|
||||
include/fennec/format/format_arg.h
|
||||
include/fennec/format/formatter.h
|
||||
include/fennec/format/charconv.h
|
||||
|
||||
include/fennec/format/detail/_format.h
|
||||
|
||||
source/format/charconv.cpp
|
||||
|
||||
|
||||
|
||||
# filesystem ===========================================================================================================
|
||||
include/fennec/filesystem/file.h source/filesystem/file.cpp
|
||||
include/fennec/filesystem/path.h source/filesystem/path.cpp
|
||||
|
||||
|
||||
|
||||
# interpret ============================================================================================================
|
||||
include/fennec/interpret/tokenizer.h
|
||||
|
||||
# Filesystem
|
||||
include/fennec/langproc/filesystem/file.h source/langproc/filesystem/file.cpp
|
||||
include/fennec/langproc/filesystem/path.h source/langproc/filesystem/path.cpp
|
||||
|
||||
|
||||
# PLATFORM =============================================================================================================
|
||||
|
||||
include/fennec/platform/interface/fwd.h
|
||||
include/fennec/platform/interface/forward.h
|
||||
include/fennec/platform/interface/display_server.h
|
||||
include/fennec/platform/interface/platform.h source/platform/interface/platform.cpp
|
||||
include/fennec/platform/interface/display.h source/platform/interface/display.cpp
|
||||
include/fennec/platform/interface/gfxcontext.h
|
||||
include/fennec/platform/interface/window.h source/platform/interface/window.cpp
|
||||
include/fennec/platform/window_manager.h source/platform/window_manager.cpp
|
||||
|
||||
|
||||
# EXTRA SOURCES ========================================================================================================
|
||||
|
||||
${FENNEC_EXTRA_SOURCES}
|
||||
include/fennec/containers/traversal.h
|
||||
include/fennec/containers/graph.h
|
||||
include/fennec/containers/deque.h
|
||||
include/fennec/containers/object_pool.h
|
||||
# GRAPHICS =============================================================================================================
|
||||
|
||||
)
|
||||
|
||||
add_dependencies(fennec metaprogramming)
|
||||
# add the test suite as a sub-project
|
||||
add_subdirectory(test)
|
||||
|
||||
target_compile_definitions(fennec PUBLIC
|
||||
${FENNEC_COMPILE_DEFINITIONS}
|
||||
add_library(fennec STATIC
|
||||
${FENNEC_SOURCES}
|
||||
include/fennec/rtti/this_t.h
|
||||
)
|
||||
|
||||
add_dependencies(fennec metaprogramming fennec-dependencies)
|
||||
|
||||
target_link_options(fennec PRIVATE ${FENNEC_PRIVATE_LINK_OPTIONS}) # Do not compile base fennec library with c++ stdlib
|
||||
target_compile_definitions(fennec PUBLIC ${FENNEC_COMPILE_DEFINITIONS})
|
||||
target_compile_options(fennec PRIVATE ${FENNEC_PRIVATE_COMPILE_OPTIONS})
|
||||
|
||||
# Do not compile base fennec library with c++ stdlib
|
||||
# fennec does not use the C++ stdlib because it is bloated, difficult to read, and implementation defined.
|
||||
# This implementation is designed to be as readable as possible, and expose information that would otherwise be obfuscated
|
||||
target_link_options(fennec PRIVATE ${FENNEC_PRIVATE_LINK_OPTIONS})
|
||||
|
||||
|
||||
|
||||
target_link_libraries(fennec PRIVATE
|
||||
cpptrace::cpptrace
|
||||
${FENNEC_LINK_LIBRARIES}
|
||||
${FENNEC_SHARED_LIBRARIES}
|
||||
|
||||
cpptrace::cpptrace
|
||||
)
|
||||
|
||||
foreach (_LIB IN ITEMS ${FENNEC_SHARED_LIBRARIES})
|
||||
add_custom_command(TARGET fennec POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_LINKER_FILE:${_LIB}> ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
|
||||
)
|
||||
endforeach ()
|
||||
|
||||
|
||||
# DOXYGEN ==============================================================================================================
|
||||
@@ -232,9 +351,10 @@ file(COPY logo DESTINATION docs/logo)
|
||||
if(DOXYGEN_FOUND)
|
||||
add_dependencies(fennec fennecdocs)
|
||||
set(DOXY_OUTPUT_DIR "${FENNEC_SOURCE_DIR}/docs")
|
||||
set(DOXY_EXAMPLES_DIR "${FENNEC_SOURCE_DIR}/examples")
|
||||
get_filename_component(DOXYGEN_PROJECT_NAME ${FENNEC_SOURCE_DIR} NAME) # Set Doxy Project name to the name of the root dir
|
||||
set(DOXYGEN_CONFIG_IN "${FENNEC_SOURCE_DIR}/doxy/Doxyfile.in") # Input config file with preprocessor arguments
|
||||
set(DOXYGEN_CONFIG_OUT "${FENNEC_SOURCE_DIR}/doxy/Doxyfile") # Generated config file from input
|
||||
set(DOXYGEN_CONFIG_OUT "${FENNEC_SOURCE_DIR}/doxy/Doxyfile.out") # Generated config file from input
|
||||
|
||||
configure_file(${DOXYGEN_CONFIG_IN} ${DOXYGEN_CONFIG_OUT} @ONLY) # Execute preprocessing step
|
||||
message("Doxygen Build Started.")
|
||||
@@ -243,7 +363,7 @@ if(DOXYGEN_FOUND)
|
||||
# Target for building docs
|
||||
add_custom_target(fennecdocs ALL
|
||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT}
|
||||
WORKING_DIRECTORY ${FENNEC_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${FENNEC_SOURCE_DIR}/include/
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${FENNEC_SOURCE_DIR}/logo/raster.png
|
||||
${DOXY_OUTPUT_DIR}/logo/raster.png
|
||||
COMMENT "Generating Doxygen Documentation"
|
||||
|
||||
138
README.md
138
README.md
@@ -8,17 +8,24 @@
|
||||
|
||||
<br><br>
|
||||
|
||||
## Table of Contents
|
||||
<a id="table-of-contents"></a>
|
||||
<h2>Table of Contents</h2>
|
||||
|
||||
* [Table of Contents](#table-of-contents)
|
||||
* [Introduction](#introduction)
|
||||
* [Coding Standards](#coding-standards)
|
||||
* [Building from Source](#building-from-source)
|
||||
* [Building from Terminal](#building-from-terminal)
|
||||
* [Git](#git) → `git`
|
||||
* [Debian](#debian) → `apt`
|
||||
* [Arch](#arch) → `pacman`
|
||||
* [Fedora](#fedora) → `dnf`
|
||||
* [Building on Windows](#building-on-windows)
|
||||
* [Running the Test Suite](#running-the-test-suite)
|
||||
* [Usage](#usage)
|
||||
* [Licensing](#licensing)
|
||||
* [Contribution](#contribution)
|
||||
|
||||
1. [Introduction](#introduction)
|
||||
1. [Coding Standards](#coding-standards)
|
||||
2. [Building from Source](#building-from-source)
|
||||
1. [Building from Terminal](#building-from-terminal)
|
||||
2. [Building on Windows](#building-on-windows)
|
||||
3. [Running the Test Suite](#running-the-test-suite)
|
||||
4. [Usage](#usage)
|
||||
5. [Contribution](#contribution)
|
||||
6. [Documentation](./documentation.html)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
@@ -43,9 +50,11 @@ Some main areas where the engine strays from the GNU standard includes the follo
|
||||
|
||||
* [Section 4.7, Standards for Graphical Interfaces](https://www.gnu.org/prep/standards/html_node/Graphical-Interfaces.html).
|
||||
fennec provides an implementation for X11, however it does not use the GTK toolkit.
|
||||
- [Section 6.1, GNU Manuals](https://www.gnu.org/prep/standards/html_node/GNU-Manuals.html)
|
||||
- [Section 5.1, Formatting Your Source Code](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||
fennec uses Allman (BSD) for namespaces, otherwise K&R.
|
||||
* [Section 6.1, GNU Manuals](https://www.gnu.org/prep/standards/html_node/GNU-Manuals.html)
|
||||
fennec does not use Texinfo and instead uses Doxygen. Otherwise, it follows the other standards of this section.
|
||||
* [Section 7, The Release Process](https://www.gnu.org/prep/standards/html_node/Managing-Releases.html)
|
||||
- [Section 7, The Release Process](https://www.gnu.org/prep/standards/html_node/Managing-Releases.html)
|
||||
fennec follows most of the conventions in this section, however the build system used is CMake and not
|
||||
Makefile. CMake, although overwhelming at first, is much more friendly to those who are learning build systems for the first time.
|
||||
|
||||
@@ -57,8 +66,8 @@ fennec Standards:
|
||||
`<APP_NAME>_`. Macros that wrap C-Style functions may use normal `snake_case`.
|
||||
|
||||
- Header Guards should be implemented using `#ifndef`, `#define`, and `#endif` for portability.
|
||||
The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`.
|
||||
I.E. the engine file `fennec/lang/utility.h` has the Header Guard `FENNEC_LANG_UTILITY_H`.
|
||||
The naming convention for Header Guards is as follows: `<APP_NAME>_<DIRECTORY_PATH>_<FILE_NAME>_H`,
|
||||
e.g. the engine file `fennec/lang/utility.h` has the Header Guard `FENNEC_LANG_UTILITY_H`.
|
||||
|
||||
* Helper Functions, in the case of classes, should be private.
|
||||
In the case of global functions, helpers should be placed in a similarly named file in a subdirectory and namespace
|
||||
@@ -69,10 +78,28 @@ fennec Standards:
|
||||
|
||||
* Most behaviours should be type independent. Specifically interactions with the core systems of the engine.
|
||||
|
||||
- Classes should have banners between each distinct group of definitions, with the access specifier on the following
|
||||
line. E.G.
|
||||
```c++
|
||||
class foo {
|
||||
// Definitions ============================
|
||||
public:
|
||||
using bar = int;
|
||||
|
||||
// Constructors & Destructor ==============
|
||||
public:
|
||||
foo();
|
||||
~foo();
|
||||
};
|
||||
```
|
||||
This helps significantly with readability, and it also serves to reduce bugs related to mistakes involving access
|
||||
specifiers. E.G. in the example above, bar is changed to private, the constructors are still labeled public in the
|
||||
case the person forgets to update the succeeding access specifiers.
|
||||
|
||||
<br><br>
|
||||
|
||||
<a id="f1"></a>
|
||||
<sup>[1]</sup> If we were to use the exception paradigm for all erroneous behaviour, we couldn't guarantee
|
||||
<sup>[[1]](#f1)</sup> If we were to use the exception paradigm for all erroneous behaviour, we couldn't guarantee
|
||||
that the state will not be corrupted when an exception is thrown. The behaviour afterward is undefined
|
||||
because of this, and we also don't really know when, how, or where that exception will be handled.
|
||||
The assertion paradigm is better at handling this because you are defining erroneous behaviour in the
|
||||
@@ -119,16 +146,24 @@ is also a viable IDE but involves some extra setup.
|
||||
| fennecdocs-clean | Cleans the generated html documentation files. |
|
||||
| fennec-test | Test suite for verifying engine functionality. |
|
||||
|
||||
<br>
|
||||
|
||||
<a id="dependencies"></a>
|
||||
### Dependencies
|
||||
|
||||
| Dependency | Notes |
|
||||
|-------------------|----------------------------------------------------------------------------------------------------------|
|
||||
|------------------------------|----------------------------------------------------------------------------------------------------------|
|
||||
| C/C++ Compiler | GCC/G++ is the compiler that fennec is designed around, however, Clang, MSVC, and MinGW may also be used |
|
||||
| CMake | The build manager used by the engine |
|
||||
| Volk<sup>[*](#opt)</sup> | The Vulkan loader Volk, includes necessary headers for Vulkan. |
|
||||
| A build system | Any build system will work, however, `build.sh` uses Ninja by default. |
|
||||
| A memory debugger | Any memory debugger will work, however, `test.sh` uses Valgrind by default. |
|
||||
| Doxygen | Doxygen is required for building the documentation for fennec. This is an optional dependency |
|
||||
| Graphviz | Graphviz is a required dependency for Doxygen |
|
||||
| Doxygen<sup>[*](#opt)</sup> | Doxygen is required for building the documentation for fennec. |
|
||||
| Graphviz<sup>[*](#opt)</sup> | Graphviz is a required dependency for Doxygen |
|
||||
|
||||
|
||||
<a id="opt"></a>
|
||||
<sup>[*](#opt)</sup> Optional Dependency
|
||||
|
||||
<br>
|
||||
|
||||
@@ -141,11 +176,57 @@ for more info.
|
||||
  By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
|
||||
build scripts to use another build manager, see the [CMake documentation for available generators](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html).
|
||||
|
||||
|
||||
  I will at no point provide official cross-compilation toolchains for fennec. However, I will provide tools for
|
||||
using specific toolchains for specific platforms that necessitate this. The primary examples would be Android and iOS.
|
||||
If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot. I recommend Dual Boot over WSL.
|
||||
|
||||
<a id="git"></a>
|
||||
#### Git
|
||||
|
||||
Install git dependencies with the following command:
|
||||
```shell
|
||||
git submodule update --force --init --recursive --remote
|
||||
```
|
||||
|
||||
<a id="debian"></a>
|
||||
#### Debian
|
||||
|
||||
On Debian-based distributions, you can install dependencies using the following command:
|
||||
```shell
|
||||
sudo apt install build-essential cmake ninja-build valgrind libvulkan-volk-dev vulkan-validationlayers
|
||||
```
|
||||
|
||||
for Doxygen run:
|
||||
```shell
|
||||
sudo apt install doxygen graphviz
|
||||
```
|
||||
|
||||
<a id="arch"></a>
|
||||
#### Arch
|
||||
|
||||
On Arch-based distributions, you can install dependencies using the following command:
|
||||
```shell
|
||||
sudo pacman -S base-devel cmake ninja valgrind vulkan-devel
|
||||
```
|
||||
|
||||
for Doxygen run:
|
||||
```shell
|
||||
sudo pacman -S doxygen graphviz
|
||||
```
|
||||
|
||||
<a id="fedora"></a>
|
||||
#### Fedora
|
||||
|
||||
On Fedora-based distributions, you can install dependencies using the following command:
|
||||
```shell
|
||||
sudo dnf install build-essential g++ cmake ninja-build valgrind vulkan-volk-devel vulkan-validation-layers
|
||||
```
|
||||
|
||||
for Doxygen run:
|
||||
```shell
|
||||
sudo dnf install doxygen graphviz
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
<a id="building-on-windows"></a>
|
||||
@@ -155,7 +236,7 @@ If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot.
|
||||
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require
|
||||
the build [dependencies](#dependencies) installed and configured to be available on the `PATH` environment variable.
|
||||
|
||||
Fore more details, [see this blog post](https://blogs.windows.com/windows-insider/2016/04/06/announcing-windows-10-insider-preview-build-14316/) for Windows Build 14316.
|
||||
For more details, [see this blog post](https://blogs.windows.com/windows-insider/2016/04/06/announcing-windows-10-insider-preview-build-14316/) for Windows Build 14316.
|
||||
|
||||
Otherwise, follow the sequence of commands provided in the bash script.
|
||||
|
||||
@@ -206,23 +287,26 @@ information displayed is correct.
|
||||
## Usage
|
||||
|
||||
|
||||
<a id="licensing"></a>
|
||||
### Licensing
|
||||
|
||||
The following content of this section is not legal advice, nor is it legally binding, and nor does it change the terms
|
||||
of the license. Please seek legal council if you have any concerns.
|
||||
|
||||
  fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from modifying
|
||||
fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially, however
|
||||
it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
|
||||
fennec however you wish according to the terms of the license, which does not bar you from commercializing software
|
||||
based on fennec.
|
||||
TLDR; You may license your game under whichever license you please. Any C++ code that is by definition a derivative work
|
||||
must be licensed under GPLv3 and freely available, everything else; assets, scripts, design documents, etc. may be under
|
||||
the license of your choosing and remain proprietary.
|
||||
|
||||
  fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from
|
||||
modifying fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially,
|
||||
however it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
|
||||
fennec however you wish according to the terms of the license, which does not bar you from commercializing software based
|
||||
on fennec.
|
||||
|
||||
  If you wish to protect your game files, assets and generated content do not constitute a covered work and may be
|
||||
copyrighted under a non-compliant license. Think of it in terms of using Blender to create a mesh for a game, then
|
||||
licensing that mesh under another license.
|
||||
|
||||
Any code that is linked against fennec or uses any part is by definition a covered work must be licensed under GPLv3.
|
||||
|
||||
  Later down the line, I plan on implementing scripts in a manner that allows the script itself to remain proprietary.
|
||||
The scripts will likely be trans-compiled to another language before being compiled to binary, but this is only an
|
||||
intermediate step and will be erased when no longer needed.
|
||||
|
||||
10
build.sh
10
build.sh
@@ -1,6 +1,6 @@
|
||||
## =====================================================================================================================
|
||||
## fennec, a free and open source game engine
|
||||
## Copyright © 2025 Medusa Slockbower
|
||||
## Copyright © 2025 - 2026 Medusa Slockbower
|
||||
##
|
||||
## This program is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU General Public License as published by
|
||||
@@ -38,7 +38,7 @@ Help()
|
||||
Debug()
|
||||
{
|
||||
mkdir -p build/debug
|
||||
cd ./build/debug
|
||||
cd ./build/debug || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -47,7 +47,7 @@ Debug()
|
||||
Release()
|
||||
{
|
||||
mkdir -p build/release
|
||||
cd ./build/release
|
||||
cd ./build/release || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -56,7 +56,7 @@ Release()
|
||||
RelWithDebInfo()
|
||||
{
|
||||
mkdir -p build/relwithdebinfo
|
||||
cd ./build/relwithdebinfo
|
||||
cd ./build/relwithdebinfo || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
@@ -65,7 +65,7 @@ RelWithDebInfo()
|
||||
MinSizeRel()
|
||||
{
|
||||
mkdir -p build/minsizerel
|
||||
cd ./build/minsizerel
|
||||
cd ./build/minsizerel || exit
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B .
|
||||
cmake --build . --target fennec
|
||||
cd ../..
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -18,6 +18,12 @@
|
||||
|
||||
# this script finds the compiler being used
|
||||
|
||||
message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}")
|
||||
|
||||
fennec_add_definitions(
|
||||
FENNEC_LONG_COMPILER_NAME="${CMAKE_CXX_COMPILER_ID} ${CMAKE_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR}"
|
||||
)
|
||||
|
||||
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||
set(FENNEC_COMPILER "GCC")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/gcc.cmake")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -18,8 +18,15 @@
|
||||
|
||||
# this script sets flags and variables for gnu and gnu-like compilers
|
||||
|
||||
add_compile_options("-mxsave" "-Wall" "-Wextra" "-pedantic" "-Werror")
|
||||
add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror" "-fms-extensions")
|
||||
|
||||
set(FENNEC_PRIVATE_LINK_OPTIONS "-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates")
|
||||
fennec_add_compile_options("-ffile-prefix-map=${FENNEC_SOURCE_DIR}=.")
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_COMPILER_GCC=1 FENNEC_NO_INLINE=[[gnu::noinline]])
|
||||
fennec_add_link_options("-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates" "-pthread")
|
||||
|
||||
fennec_add_definitions(
|
||||
_GLIBCXX_INCLUDE_NEXT_C_HEADERS=1
|
||||
FENNEC_COMPILER_GCC=1
|
||||
FENNEC_NO_INLINE=[[gnu::noinline]]
|
||||
FENNEC_FUNCTION_NAME=__PRETTY_FUNCTION__
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -23,13 +23,13 @@ macro(fennec_check_platform)
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/unix.cmake")
|
||||
|
||||
# compile definitions
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
fennec_add_definitions(
|
||||
FENNEC_PLATFORM_NAME="Linux"
|
||||
FENNEC_PLATFORM_LINUX=1
|
||||
)
|
||||
|
||||
# extra source files
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/linux/platform.h source/platform/linux/platform.cpp
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -16,37 +16,28 @@
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
if(FENNEC_GRAPHICS_WANT_EGL)
|
||||
find_package(OpenGL REQUIRED COMPONENTS EGL)
|
||||
find_package(GLEW REQUIRED)
|
||||
message(STATUS "EGL Requested")
|
||||
else()
|
||||
find_package(OpenGL)
|
||||
|
||||
if(FENNEC_GRAPHICS_WANT_EGL)
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/opengl/egl/fwd.h
|
||||
include/fennec/platform/opengl/egl/error.h
|
||||
include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp
|
||||
include/fennec/platform/opengl/egl/surface.h source/platform/opengl/egl/surface.cpp
|
||||
|
||||
include/fennec/platform/opengl/glad/egl.h source/platform/opengl/glad/egl.c
|
||||
)
|
||||
endif()
|
||||
|
||||
if(TARGET OpenGL::GL AND TARGET GLEW::GLEW)
|
||||
message(STATUS "Found OpenGL: ${OPENGL_gl_LIBRARY}")
|
||||
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::GL GLEW::GLEW)
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_OPENGL=1)
|
||||
if(TARGET OpenGL::GL)
|
||||
fennec_add_link_libraries(OpenGL::GL)
|
||||
fennec_add_definitions(FENNEC_GRAPHICS_OPENGL=1)
|
||||
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/opengl/glad/gl.h source/platform/opengl/glad/gl.c
|
||||
|
||||
include/fennec/renderers/opengl/glcontext.h source/renderers/opengl/glcontext.cpp
|
||||
)
|
||||
else()
|
||||
message(FATAL_ERROR "No Suitable OpenGL implementation found.")
|
||||
endif()
|
||||
|
||||
if(FENNEC_GRAPHICS_WANT_EGL)
|
||||
if(NOT TARGET OpenGL::EGL)
|
||||
message(FATAL_ERROR "EGL Library not found.")
|
||||
endif()
|
||||
|
||||
message(STATUS "Found EGL: ${OPENGL_egl_LIBRARY}")
|
||||
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::EGL)
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_EGL=1)
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
include/fennec/platform/opengl/lib/fwd.h
|
||||
include/fennec/platform/opengl/lib/enum.h
|
||||
include/fennec/platform/opengl/lib/buffer.h
|
||||
include/fennec/platform/opengl/lib/texture.h
|
||||
include/fennec/platform/opengl/lib/vertex_array.h
|
||||
|
||||
include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp
|
||||
)
|
||||
endif()
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -22,13 +22,14 @@ message(STATUS "OS: ${CMAKE_SYSTEM_NAME}")
|
||||
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/default_user.cmake")
|
||||
|
||||
# Graphics APIs
|
||||
macro(fennec_init_graphics)
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/opengl.cmake")
|
||||
endmacro()
|
||||
|
||||
# Check for Linux
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||
set(FENNEC_PLATFORM "Linux")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/linux.cmake")
|
||||
endif ()
|
||||
|
||||
# Graphics APIs
|
||||
macro(fennec_init_graphics)
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/opengl.cmake")
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/vulkan.cmake")
|
||||
endmacro()
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -19,11 +19,11 @@
|
||||
# generic unix functionality
|
||||
|
||||
# compile definitions
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
fennec_add_definitions(
|
||||
FENNEC_PLATFORM_UNIX=1
|
||||
)
|
||||
|
||||
# extra source files
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/unix/platform.h source/platform/unix/platform.cpp
|
||||
)
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
40
cmake/vulkan.cmake
Normal file
40
cmake/vulkan.cmake
Normal file
@@ -0,0 +1,40 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
# ======================================================================================================================
|
||||
|
||||
find_package(Vulkan COMPONENTS glslang volk)
|
||||
|
||||
if(FENNEC_GRAPHICS_WANT_MOLTENVK)
|
||||
find_package(Vulkan COMPONENTS MoltenVK)
|
||||
endif()
|
||||
|
||||
if( TARGET Vulkan::Headers AND TARGET Vulkan::volk # Base Headers and Meta-Loader
|
||||
AND TARGET Vulkan::glslang # GLSL Compilation
|
||||
AND (NOT FENNEC_GRAPHICS_WANT_MOLTENVK OR TARGET Vulkan::MoltenVK)
|
||||
)
|
||||
fennec_add_link_libraries(Vulkan::volk Vulkan::glslang)
|
||||
fennec_add_definitions(FENNEC_GRAPHICS_VULKAN=1)
|
||||
|
||||
fennec_add_sources(
|
||||
include/fennec/renderers/vulkan/lib/app_info.h
|
||||
include/fennec/renderers/vulkan/lib/instance.h
|
||||
|
||||
include/fennec/renderers/vulkan/vkcontext.h include/fennec/renderers/vulkan/vkcontext.cpp
|
||||
)
|
||||
else()
|
||||
message(WARNING "No Suitable Vulkan implementation found.")
|
||||
endif()
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -19,6 +19,34 @@
|
||||
# https://gist.github.com/mariobadr/acc3c8adf4b4e722705be38c3deac59a
|
||||
# this script finds libwayland and dependencies
|
||||
|
||||
# some of this code is based on SDL3's use of wayland-scanner
|
||||
|
||||
function(fennec_wayland_get_protocol)
|
||||
set( _OPTIONS_ARGS )
|
||||
set( _ONE_VALUE_ARGS )
|
||||
set( _MULTI_VALUE_ARGS NAMES PATHS )
|
||||
|
||||
cmake_parse_arguments(_FINDPROTOCOLS "${_OPTIONS_ARGS}" "${_ONE_VALUE_ARGS}" "${_MULTI_VALUE_ARGS}" ${ARGN} )
|
||||
|
||||
find_file(_FINDPROTOCOLS_TEMP NAMES ${_FINDPROTOCOLS_NAMES} PATHS ${_FINDPROTOCOLS_PATHS})
|
||||
file(COPY ${_FINDPROTOCOLS_TEMP} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
|
||||
endfunction()
|
||||
|
||||
macro(fennec_wayland_get_header _SCANNER _XML _FILE)
|
||||
set(_WAYLAND_PROT_H_CODE "${WAYLAND_HEADERS_DIR}/${_FILE}-client-protocols.h")
|
||||
set(_WAYLAND_PROT_C_CODE "${WAYLAND_SOURCES_DIR}/${_FILE}-client.c")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_SCANNER} client-header "${_XML}" "${_WAYLAND_PROT_H_CODE}"
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND ${_SCANNER} private-code "${_XML}" "${_WAYLAND_PROT_C_CODE}"
|
||||
)
|
||||
|
||||
fennec_add_sources(${_WAYLAND_PROT_C_CODE} ${_WAYLAND_PROT_H_CODE})
|
||||
endmacro()
|
||||
|
||||
macro(fennec_check_wayland)
|
||||
set(WAYLAND_CLIENT_FOUND 0)
|
||||
|
||||
@@ -30,6 +58,7 @@ macro(fennec_check_wayland)
|
||||
WAYLAND_CLIENT_LIBRARY
|
||||
NAMES wayland-client libwayland-client
|
||||
)
|
||||
find_program(WAYLAND_SCANNER NAMES wayland-scanner)
|
||||
|
||||
# EGL is required
|
||||
find_path(
|
||||
@@ -41,13 +70,31 @@ macro(fennec_check_wayland)
|
||||
NAMES wayland-egl libwayland-egl
|
||||
)
|
||||
|
||||
if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY)
|
||||
|
||||
if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY AND WAYLAND_SCANNER)
|
||||
AND (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARY))
|
||||
message(STATUS "Found Wayland: ${WAYLAND_CLIENT_LIBRARY}")
|
||||
|
||||
set(WAYLAND_PROTOCOLS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/protocols)
|
||||
set(WAYLAND_HEADERS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/headers)
|
||||
set(WAYLAND_SOURCES_DIR ${FENNEC_SOURCE_DIR}/source/platform/linux/wayland/lib/sources)
|
||||
|
||||
# Search for base protocol xml
|
||||
fennec_wayland_get_protocol(NAMES "wayland.xml" PATHS "/usr/share/wayland" "/usr/share/wayland-protocols")
|
||||
fennec_wayland_get_protocol(NAMES "xdg-shell.xml" PATHS "/usr/share/wayland/stable/xdg-shell" "/usr/share/wayland-protocols/stable/xdg-shell")
|
||||
|
||||
# include sub-dependencies
|
||||
include("${FENNEC_SOURCE_DIR}/cmake/xkb.cmake")
|
||||
fennec_check_xkb()
|
||||
|
||||
# generate protocols, based on SDL3
|
||||
file(GLOB WAYLAND_PROTOCOLS_XML RELATIVE "${WAYLAND_PROTOCOLS_DIR}" "${WAYLAND_PROTOCOLS_DIR}/*.xml")
|
||||
foreach(_XML IN LISTS WAYLAND_PROTOCOLS_XML)
|
||||
get_filename_component(_FILE ${_XML} NAME_WLE)
|
||||
fennec_wayland_get_header("${WAYLAND_SCANNER}" "${WAYLAND_PROTOCOLS_DIR}/${_XML}" "${_FILE}")
|
||||
endforeach()
|
||||
|
||||
# Add sources and libraries
|
||||
get_filename_component(
|
||||
WAYLAND_CLIENT_LIBRARY
|
||||
${WAYLAND_CLIENT_LIBRARY}
|
||||
@@ -63,22 +110,60 @@ macro(fennec_check_wayland)
|
||||
set(WAYLAND_EGL_FOUND 1)
|
||||
set(FENNEC_GRAPHICS_WANT_EGL 1)
|
||||
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
fennec_add_sources(
|
||||
# Dynamic Library Files
|
||||
include/fennec/platform/linux/wayland/lib/fwd.h
|
||||
include/fennec/platform/linux/wayland/lib/sym.h
|
||||
include/fennec/platform/linux/wayland/lib/wayland-client.h
|
||||
include/fennec/platform/linux/wayland/lib/wayland-util.h
|
||||
include/fennec/platform/linux/wayland/lib/wayland.h
|
||||
include/fennec/platform/linux/wayland/lib/loader.h source/platform/linux/wayland/lib/loader.cpp
|
||||
|
||||
# Fennec Files
|
||||
include/fennec/platform/linux/wayland/display.h source/platform/linux/wayland/display.cpp
|
||||
include/fennec/platform/linux/wayland/fwd.h
|
||||
include/fennec/platform/linux/wayland/server.h source/platform/linux/wayland/server.cpp
|
||||
include/fennec/platform/linux/wayland/window.h source/platform/linux/wayland/window.cpp
|
||||
|
||||
# EGL
|
||||
include/fennec/platform/linux/wayland/egl/context.h source/platform/linux/wayland/egl/context.cpp
|
||||
include/fennec/platform/linux/wayland/egl/surface.h source/platform/linux/wayland/egl/surface.cpp
|
||||
)
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
fennec_add_definitions(
|
||||
FENNEC_HAS_WAYLAND=1
|
||||
FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}"
|
||||
FENNEC_LIB_WAYLAND_EGL="${WAYLAND_EGL_LIBRARY}"
|
||||
VK_USE_PLATFORM_WAYLAND_KHR=1
|
||||
)
|
||||
|
||||
|
||||
# find libdecor
|
||||
find_path(
|
||||
LIBDECOR_INCLUDE_DIR
|
||||
PATH_SUFFIXES libdecor libdecor-0
|
||||
NAMES libdecor.h libdecor.h
|
||||
)
|
||||
find_library(
|
||||
LIBDECOR_LIBRARY
|
||||
PATH_SUFFIXES libdecor libdecor-0
|
||||
NAMES libdecor.so libdecor-0.so
|
||||
)
|
||||
|
||||
if(LIBDECOR_INCLUDE_DIR AND LIBDECOR_LIBRARY)
|
||||
message(STATUS "Found libdecor: ${LIBDECOR_LIBRARY}")
|
||||
|
||||
fennec_add_definitions(
|
||||
FENNEC_HAS_LIBDECOR=1
|
||||
FENNEC_LIB_LIBDECOR="${LIBDECOR_LIBRARY}"
|
||||
)
|
||||
|
||||
include_directories(
|
||||
${LIBDECOR_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
fennec_add_sources(
|
||||
include/fennec/platform/linux/wayland/libdecor/sym.h
|
||||
include/fennec/platform/linux/wayland/libdecor/libdecor.h
|
||||
include/fennec/platform/linux/wayland/libdecor/loader.h source/platform/linux/wayland/libdecor/loader.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
endmacro()
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -41,17 +41,16 @@ macro(fennec_check_xkb)
|
||||
|
||||
set(XKB_FOUND 1)
|
||||
|
||||
list(APPEND FENNEC_EXTRA_SOURCES
|
||||
fennec_add_sources(
|
||||
# Dynamic Library Files
|
||||
include/fennec/platform/linux/xkb/lib/fwd.h
|
||||
include/fennec/platform/linux/xkb/lib/sym.h
|
||||
include/fennec/platform/linux/xkb/lib/xkbcommon.h
|
||||
include/fennec/platform/linux/xkb/lib/xkb.h
|
||||
include/fennec/platform/linux/xkb/lib/loader.h source/platform/linux/xkb/lib/loader.cpp
|
||||
|
||||
# Fennec files
|
||||
)
|
||||
|
||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
||||
fennec_add_definitions(
|
||||
FENNEC_HAS_XKB=1
|
||||
FENNEC_LIB_XKB="${XKB_LIBRARY}"
|
||||
)
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<includes visible="$SHOW_HEADERFILE"/>
|
||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||
<collaborationgraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestedclasses visible="yes" title=""/>
|
||||
<publictypes title=""/>
|
||||
@@ -83,7 +84,6 @@
|
||||
<related title="" subtitle=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
@@ -105,6 +105,7 @@
|
||||
<!-- Layout definition for a namespace page -->
|
||||
<namespace>
|
||||
<briefdescription visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestednamespaces visible="yes" title=""/>
|
||||
<constantgroups visible="yes" title=""/>
|
||||
@@ -121,7 +122,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<typedefs title=""/>
|
||||
@@ -150,6 +150,7 @@
|
||||
<includegraph visible="yes"/>
|
||||
<includedbygraph visible="yes"/>
|
||||
<sourcelink visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<interfaces visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
@@ -167,7 +168,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<inlineclasses title=""/>
|
||||
<defines title=""/>
|
||||
@@ -185,6 +185,7 @@
|
||||
<group>
|
||||
<briefdescription visible="yes"/>
|
||||
<groupgraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<nestedgroups visible="yes" title=""/>
|
||||
<modules visible="yes" title=""/>
|
||||
@@ -210,7 +211,6 @@
|
||||
<friends title=""/>
|
||||
<membergroups visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdef>
|
||||
<pagedocs/>
|
||||
<inlineclasses title=""/>
|
||||
@@ -237,6 +237,7 @@
|
||||
<module>
|
||||
<briefdescription visible="yes"/>
|
||||
<exportedmodules visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<concepts visible="yes" title=""/>
|
||||
<classes visible="yes" title=""/>
|
||||
@@ -246,7 +247,6 @@
|
||||
<variables title=""/>
|
||||
<membergroups title=""/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
@@ -256,10 +256,10 @@
|
||||
<directory>
|
||||
<briefdescription visible="yes"/>
|
||||
<directorygraph visible="yes"/>
|
||||
<detaileddescription title=""/>
|
||||
<memberdecl>
|
||||
<dirs visible="yes"/>
|
||||
<files visible="yes"/>
|
||||
</memberdecl>
|
||||
<detaileddescription title=""/>
|
||||
</directory>
|
||||
</doxygenlayout>
|
||||
|
||||
2869
doxy/Doxyfile
2869
doxy/Doxyfile
File diff suppressed because it is too large
Load Diff
@@ -260,7 +260,7 @@ INHERIT_DOCS = YES
|
||||
# of the file/class/namespace that contains it.
|
||||
# The default value is: NO.
|
||||
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
SEPARATE_MEMBER_PAGES = YES
|
||||
|
||||
# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
|
||||
# uses this value to replace tabs by spaces in code fragments.
|
||||
@@ -582,7 +582,7 @@ RESOLVE_UNNAMED_PARAMS = YES
|
||||
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_MEMBERS = YES
|
||||
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
|
||||
@@ -591,7 +591,7 @@ HIDE_UNDOC_MEMBERS = YES
|
||||
# if EXTRACT_ALL is enabled.
|
||||
# The default value is: NO.
|
||||
|
||||
HIDE_UNDOC_CLASSES = YES
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
|
||||
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
|
||||
# declarations. If set to NO, these declarations will be included in the
|
||||
@@ -701,7 +701,7 @@ SORT_BRIEF_DOCS = NO
|
||||
# detailed member documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_MEMBERS_CTORS_1ST = YES
|
||||
|
||||
# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
|
||||
# of group names into alphabetical order. If set to NO the group names will
|
||||
@@ -943,8 +943,7 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = "@PROJECT_SOURCE_DIR@/include" \
|
||||
"@PROJECT_SOURCE_DIR@/source" \
|
||||
INPUT = "@PROJECT_SOURCE_DIR@/include/" \
|
||||
"@PROJECT_SOURCE_DIR@/README.md"
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
@@ -1049,7 +1048,10 @@ RECURSIVE = YES
|
||||
# Note that relative paths are relative to the directory from which doxygen is
|
||||
# run.
|
||||
|
||||
EXCLUDE =
|
||||
EXCLUDE = "@PROJECT_SOURCE_DIR@/include/fennec/platform/linux/wayland/lib" \
|
||||
"@PROJECT_SOURCE_DIR@/include/fennec/platform/linux/wayland/libdecor" \
|
||||
"@PROJECT_SOURCE_DIR@/include/fennec/platform/linux/xkb/lib" \
|
||||
"@PROJECT_SOURCE_DIR@/include/fennec/platform/opengl/glad"
|
||||
|
||||
# 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
|
||||
@@ -1099,7 +1101,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = "@PROJECT_SOURCE_DIR@/doxy/static"
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@@ -1981,7 +1983,7 @@ GENERATE_LATEX = NO
|
||||
# The default directory is: latex.
|
||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
||||
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_OUTPUT = ./latex
|
||||
|
||||
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||
# invoked.
|
||||
@@ -2402,7 +2404,7 @@ MACRO_EXPANSION = YES
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
EXPAND_ONLY_PREDEF = YES
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
|
||||
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
|
||||
# INCLUDE_PATH will be searched if a #include is found.
|
||||
@@ -2417,7 +2419,7 @@ SEARCH_INCLUDES = YES
|
||||
# RECURSIVE has no effect here.
|
||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
||||
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_PATH = "@PROJECT_SOURCE_DIR@/include"
|
||||
|
||||
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
|
||||
# patterns (like *.h and *.hpp) to filter out the header-files in the
|
||||
@@ -2435,8 +2437,7 @@ INCLUDE_FILE_PATTERNS =
|
||||
# recursively expanded use the := operator instead of the = operator.
|
||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||
|
||||
PREDEFINED = "FENNEC_SCALAR_TEMPLATE=" \
|
||||
"FENNEC_VECTOR_TEMPLATE="
|
||||
PREDEFINED = "FENNEC_DOXYGEN="
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
||||
# tag can be used to specify a list of macro names that should be expanded. The
|
||||
@@ -2511,7 +2512,7 @@ EXTERNAL_PAGES = YES
|
||||
# and usage relations if the target is undocumented or is not a class.
|
||||
# The default value is: YES.
|
||||
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HIDE_UNDOC_RELATIONS = NO
|
||||
|
||||
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
|
||||
# available from the path. This tool is part of Graphviz (see:
|
||||
@@ -2664,7 +2665,7 @@ TEMPLATE_RELATIONS = NO
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDE_GRAPH = NO
|
||||
|
||||
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
|
||||
# set to YES then doxygen will generate a graph for each documented file showing
|
||||
@@ -2676,7 +2677,7 @@ INCLUDE_GRAPH = YES
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag HAVE_DOT is set to YES.
|
||||
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = NO
|
||||
|
||||
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
|
||||
# dependency graph for every global function or class method.
|
||||
|
||||
@@ -287,3 +287,8 @@ html.dark-mode {
|
||||
td.odd_c {
|
||||
background-color: var(--odd-color)
|
||||
}
|
||||
|
||||
a + h2.groupheader {
|
||||
display:none;
|
||||
}
|
||||
|
||||
|
||||
71
doxy/retrieve-emojis.py
Normal file
71
doxy/retrieve-emojis.py
Normal file
@@ -0,0 +1,71 @@
|
||||
# script to download the emoticons from GitHub and to produce a table for
|
||||
# inclusion in Doxygen. Works with python 2.7+ and python 3.x
|
||||
import json
|
||||
import os
|
||||
import argparse
|
||||
import re
|
||||
try:
|
||||
import urllib.request as urlrequest
|
||||
except ImportError:
|
||||
import urllib as urlrequest
|
||||
|
||||
unicode_re = re.compile(r'.*?/unicode/(.*?).png\?.*')
|
||||
|
||||
def get_emojis():
|
||||
response = urlrequest.urlopen('https://api.github.com/emojis')
|
||||
raw_data = response.read()
|
||||
return json.loads(raw_data)
|
||||
|
||||
def download_images(dir_name, silent):
|
||||
if not os.path.exists(dir_name):
|
||||
os.makedirs(dir_name)
|
||||
json_data = get_emojis()
|
||||
num_items = len(json_data)
|
||||
cur_item=0
|
||||
for image,url in sorted(json_data.items()):
|
||||
image_name = image+'.png'
|
||||
cur_item=cur_item+1
|
||||
if url.find('/unicode/')==-1 or not os.path.isfile(dir_name+'/'+image_name):
|
||||
success = True
|
||||
with open(dir_name+'/'+image_name,'wb') as file:
|
||||
if not silent:
|
||||
print('%s/%s: fetching %s' % (cur_item,num_items,image_name))
|
||||
try:
|
||||
file.write(urlrequest.urlopen(url).read())
|
||||
except:
|
||||
print('Unable to fetch %s' % (image_name))
|
||||
success = False
|
||||
if not success:
|
||||
os.remove(dir_name+'/'+image_name)
|
||||
else:
|
||||
if not silent:
|
||||
print('%s/%s: skipping %s' % (cur_item,num_items,image_name))
|
||||
|
||||
def produce_table():
|
||||
json_data = get_emojis()
|
||||
lines = []
|
||||
for image,url in sorted(json_data.items()):
|
||||
match = unicode_re.match(url)
|
||||
if match:
|
||||
unicodes = match.group(1).split('-')
|
||||
unicodes_html = ''.join(["&#x"+x+";" for x in unicodes])
|
||||
image_str = "\":"+image+":\","
|
||||
unicode_str = "\""+unicodes_html+"\""
|
||||
lines.append(' { %-42s %-38s }' % (image_str,unicode_str))
|
||||
out_str = ',\n'.join(lines)
|
||||
print("{")
|
||||
print(out_str)
|
||||
print("};")
|
||||
|
||||
if __name__=="__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-d','--dir',help='directory to place images in')
|
||||
parser.add_argument('-t','--table',help='generate code fragment',action='store_true')
|
||||
parser.add_argument('-s','--silent',help='silent mode',action='store_true')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.table:
|
||||
produce_table()
|
||||
if args.dir:
|
||||
download_images(args.dir, args.silent)
|
||||
4
doxy/static/graphs/containers/rdtree.svg
Normal file
4
doxy/static/graphs/containers/rdtree.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 60 KiB |
67
examples/assert.cpp
Normal file
67
examples/assert.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// =====================================================================================================================
|
||||
// I release this example code into the public domain
|
||||
// =====================================================================================================================
|
||||
|
||||
// This file contains code that tests the efficiency of the assert macro scheme in
|
||||
// fennec engine. This is purely looking at the branching aspect and not the private
|
||||
// assert definition. The code only uses passing values to see any performance overhead
|
||||
// from the conditional
|
||||
|
||||
// This code is based on the example code that cppreference provides at
|
||||
// https://en.cppreference.com/w/cpp/language/attributes/likely
|
||||
|
||||
// To my surprise, the difference between them is negligible.
|
||||
// Even when n is a crazy number like one billion, there isn't a conclusive difference.
|
||||
// I checked that it isn't the lambdas, they are optimized out.
|
||||
|
||||
// In debug mode, the results are to be expected; release < experimental < control
|
||||
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#define assert_c(expression) \
|
||||
if(not(expression)) { \
|
||||
std::abort(); \
|
||||
}
|
||||
|
||||
#define assert_e(expression) \
|
||||
if(not(expression)) [[unlikely]] { \
|
||||
std::abort(); \
|
||||
}
|
||||
|
||||
#define assert_r(expression) (static_cast<void> (0))
|
||||
|
||||
volatile int sink{}; // ensures a side effect
|
||||
|
||||
int main() {
|
||||
auto benchmark = [](auto fun, auto rem) {
|
||||
srand(0);
|
||||
|
||||
const auto start = std::chrono::high_resolution_clock::now();
|
||||
for (auto size{1ULL}; size != 10'000'000ULL; ++size)
|
||||
sink = fun(rand());
|
||||
const std::chrono::duration<double> diff =
|
||||
std::chrono::high_resolution_clock::now() - start;
|
||||
|
||||
std::cout << "Time: " << std::fixed << std::setprecision(6) << diff.count()
|
||||
<< " sec " << rem << std::endl;
|
||||
};
|
||||
|
||||
benchmark([](int x) {
|
||||
assert_c(0 <= x && x <= RAND_MAX);
|
||||
return x;
|
||||
}, "control");
|
||||
|
||||
benchmark([](int x) {
|
||||
assert_r(0 <= x && x <= RAND_MAX);
|
||||
return x;
|
||||
}, "release");
|
||||
|
||||
benchmark([](int x) {
|
||||
assert_e(0 <= x && x <= RAND_MAX);
|
||||
return x;
|
||||
}, "experimental");
|
||||
}
|
||||
2
external/cpptrace
vendored
2
external/cpptrace
vendored
Submodule external/cpptrace updated: 9133b90a99...787d8af6f6
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -20,6 +20,7 @@
|
||||
# GDB CODE =============================================================================================================
|
||||
|
||||
import gdb
|
||||
import gdb.printing
|
||||
|
||||
from . import containers
|
||||
from . import strings
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -371,7 +371,7 @@ class RDTreePrinter:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}, {}]'.format(node, i)
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
@@ -389,6 +389,200 @@ class RDTreePrinter:
|
||||
return self.Iterator(self.tree, 0, self.capacity)
|
||||
|
||||
|
||||
# PRIORITY QUEUE =======================================================================================================
|
||||
|
||||
class PriorityQueuePrinter:
|
||||
"""Print a fennec::rdtree"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, tree, node, capacity):
|
||||
self.tree = tree
|
||||
self.capacity = capacity
|
||||
self.visit = deque()
|
||||
self.skip = True
|
||||
|
||||
self.visit.append((node, 0, 0, node))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
start = self.visit[0][3]
|
||||
self.visit.popleft()
|
||||
|
||||
if node == start and not self.skip:
|
||||
return self.__next__()
|
||||
|
||||
self.skip = False
|
||||
|
||||
value = self.tree[node]['_val']['key']
|
||||
|
||||
nnext = self.tree[node]['_val']['next']
|
||||
child = self.tree[node]['_val']['child']
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
|
||||
if nnext < self.capacity:
|
||||
self.visit.appendleft((nnext, i + 1, depth, start))
|
||||
|
||||
if child < self.capacity:
|
||||
self.visit.appendleft((child, 0, depth + 1, child))
|
||||
self.skip = True
|
||||
|
||||
# ┌ ─ ├ └
|
||||
|
||||
if nnext != 18446744073709551615:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.tree = val['_table']['_table']['_alloc']['_data']
|
||||
self.size = val['_table']['_size']
|
||||
self.capacity = val['_table']['_table']['_alloc']['_capacity']
|
||||
self.min = val['_min']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.tree, self.min, self.capacity)
|
||||
|
||||
|
||||
# BINTREE ==============================================================================================================
|
||||
|
||||
class BinTreePrinter:
|
||||
"""Print a fennec::bintree"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, tree, node, capacity):
|
||||
self.tree = tree
|
||||
self.capacity = capacity
|
||||
self.visit = deque()
|
||||
|
||||
if capacity > 0:
|
||||
self.visit.append((node, 0, 0))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
parent = self.tree[node]['parent']
|
||||
self.visit.popleft()
|
||||
|
||||
value = self.tree[node]['value']
|
||||
left = self.tree[node]['child'][0]
|
||||
right = self.tree[node]['child'][1]
|
||||
|
||||
if right < self.capacity:
|
||||
self.visit.appendleft((right, 1, depth + 1))
|
||||
if left < self.capacity:
|
||||
self.visit.appendleft((left, 0, depth + 1))
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
if i == 0 and parent != 18446744073709551615 and self.tree[parent]['right'] != 18446744073709551615:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.tree = val['_table']['_data']
|
||||
self.size = val['_size']
|
||||
self.root = val['_root']
|
||||
self.capacity = val['_table']['_capacity']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
return self.Iterator(self.tree, self.root, self.capacity)
|
||||
|
||||
|
||||
# SEQUENCE =============================================================================================================
|
||||
|
||||
class SequencePrinter:
|
||||
"""Print a fennec::sequence"""
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, node):
|
||||
self.visit = deque()
|
||||
|
||||
if node is not None:
|
||||
self.visit.append((node, 0, 0))
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if len(self.visit) == 0:
|
||||
raise StopIteration
|
||||
|
||||
node = self.visit[0][0]
|
||||
i = self.visit[0][1]
|
||||
depth = self.visit[0][2]
|
||||
self.visit.popleft()
|
||||
|
||||
value = node['key']
|
||||
left = node['child'][0]
|
||||
right = node['child'][1]
|
||||
print("it: ", node, " ", left, " ", right);
|
||||
|
||||
if right != 0:
|
||||
self.visit.appendleft((right, 1, depth + 1))
|
||||
if left != 0:
|
||||
self.visit.appendleft((left, 0, depth + 1))
|
||||
|
||||
index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
|
||||
if i == 0:
|
||||
index += '├'
|
||||
else:
|
||||
index += '└'
|
||||
|
||||
index += '─'
|
||||
index += '[{}]'.format(node)
|
||||
return index, value
|
||||
|
||||
|
||||
def __init__(self, val):
|
||||
self.size = val['_size']
|
||||
self.root = val['_root']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
return "{ empty }"
|
||||
return "{ size = " + str(self.size) + " }"
|
||||
|
||||
def children(self):
|
||||
print("root: ", self.root)
|
||||
return self.Iterator(self.root)
|
||||
|
||||
|
||||
# Graph ================================================================================================================
|
||||
|
||||
class GraphPrinter:
|
||||
@@ -396,10 +590,10 @@ class GraphPrinter:
|
||||
|
||||
class Iterator:
|
||||
def __init__(self, val):
|
||||
self.node_pool = val['_node_pool']['_table']['_alloc']['_data']
|
||||
self.max_nodes = val['_node_pool']['_table']['_alloc']['_capacity']
|
||||
self.conn_map = val['_conn_map']['_alloc']['_data']
|
||||
self.max_conn = val['_conn_map']['_size']
|
||||
self.node_pool = val['_vertex_pool']['_table']['_alloc']['_data']
|
||||
self.max_nodes = val['_vertex_pool']['_table']['_alloc']['_capacity']
|
||||
self.conn_map = val['_edge_map']['_alloc']['_data']
|
||||
self.max_conn = val['_edge_map']['_size']
|
||||
self.index = 0
|
||||
|
||||
def __iter__(self):
|
||||
@@ -447,7 +641,7 @@ class GraphPrinter:
|
||||
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.size = val['_node_pool']['_size']
|
||||
self.size = val['_vertex_pool']['_size']
|
||||
|
||||
def to_string(self):
|
||||
if self.size == 0:
|
||||
@@ -475,6 +669,9 @@ def register_printers():
|
||||
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
|
||||
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
|
||||
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
|
||||
pp.add_printer('fennec::bintree', '^fennec::bintree<.*>$', BinTreePrinter)
|
||||
pp.add_printer('fennec::sequence', '^fennec::sequence<.*>$', SequencePrinter)
|
||||
pp.add_printer('fennec::priority_queue', '^fennec::priority_queue<.*>$', PriorityQueuePrinter)
|
||||
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
||||
return pp
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -24,7 +24,7 @@ from . import utility
|
||||
class VectorPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.base = val['data']['elements']
|
||||
self.base = val['data']['data']
|
||||
self.len = self.base.type.range()[1] + 1
|
||||
|
||||
def to_string(self):
|
||||
@@ -51,7 +51,7 @@ class VectorPrinter:
|
||||
class QuaternionPrinter:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
self.base = val['data']['elements']
|
||||
self.base = val['data']['data']
|
||||
|
||||
def to_string(self):
|
||||
res = ("< "
|
||||
@@ -72,7 +72,7 @@ class QuaternionPrinter:
|
||||
# VECTOR =================================================================================================================
|
||||
class MatrixPrinter:
|
||||
def __init__(self, val):
|
||||
self.columns = val['data']['elements']
|
||||
self.columns = val['data']['data']
|
||||
self.num_columns = self.columns.type.range()[1] + 1
|
||||
self.num_rows = val.type.template_argument(1)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
@@ -27,7 +27,7 @@ class CStringPrinter:
|
||||
return 'string'
|
||||
|
||||
def to_string(self):
|
||||
value = "\"" + self.val['_str'].string('', 'replace', self.val['_size'] - 1) + "\""
|
||||
value = "\"" + self.val['_str'].string('', 'replace', self.val['_size']) + "\""
|
||||
return value
|
||||
|
||||
def display_hint(self):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ======================================================================================================================
|
||||
# fennec, a free and open source game engine
|
||||
# Copyright © 2025 Medusa Slockbower
|
||||
# Copyright © 2025 - 2026 Medusa Slockbower
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file array.h
|
||||
/// \brief statically allocated array wrapper
|
||||
/// \file fennec/containers/array.h
|
||||
/// \brief A header containing the definition for a static/stack allocated array
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -34,102 +34,252 @@
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/assert.h>
|
||||
#include <fennec/lang/metasequences.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for fixed size arrays
|
||||
/// \brief Data Structure that defines a compile-time allocated array
|
||||
///
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:--------:|:-----------------------:|
|
||||
/// | stable | \emoji heavy_check_mark |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam ValueT value type
|
||||
/// \tparam ElemV number of elements
|
||||
template<typename ValueT, size_t ElemV>
|
||||
struct array
|
||||
{
|
||||
/// \tparam N number of elements
|
||||
template<typename ValueT, size_t N>
|
||||
struct array {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = ValueT; //!< Alias for \f$ValueT\f$
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Member Variables
|
||||
/// @{
|
||||
|
||||
value_t data[N]; //!< Backing c-style array handle
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief backing c-style array handle
|
||||
ValueT elements[ElemV];
|
||||
/// \brief Returns the number of elements in the array.
|
||||
/// \returns \f$N\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
[[nodiscard]] constexpr size_t size() const { return N; }
|
||||
|
||||
///
|
||||
/// \brief Returns \f$true\f$ when the array is empty
|
||||
/// \returns \f$ElemV == 0\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
[[nodiscard]] constexpr bool_t is_empty() const { return N == 0; }
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Element Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \copydetails array::operator[](size_t) const
|
||||
constexpr ValueT& operator[](size_t i) {
|
||||
assertd(i < ElemV, "Array Out of Bounds");
|
||||
return elements[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief access specified element
|
||||
/// \details Returns a reference to the element at \c i
|
||||
/// \param i index of the element to return
|
||||
/// \return reference to the requested element
|
||||
///
|
||||
/// \par Time-Complexity
|
||||
/// Constant
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \par Space-Complexity
|
||||
/// Constant
|
||||
constexpr const ValueT& operator[](size_t i) const {
|
||||
assertd(i < ElemV, "Array Out of Bounds");
|
||||
return elements[i];
|
||||
constexpr value_t& operator[](size_t i) {
|
||||
assertd(i < N, "Array Out of Bounds");
|
||||
return data[i];
|
||||
}
|
||||
|
||||
constexpr ValueT* begin() { return elements; }
|
||||
constexpr ValueT* end() { return elements + ElemV; }
|
||||
///
|
||||
/// \brief Indexed access
|
||||
/// \details Returns a reference to the element at \c i
|
||||
/// \param i index of the element to return
|
||||
/// \return reference to the requested element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& operator[](size_t i) const {
|
||||
assertd(i < N, "Array Out of Bounds");
|
||||
return data[i];
|
||||
}
|
||||
|
||||
constexpr const ValueT* begin() const { return elements; }
|
||||
constexpr const ValueT* end() const { return elements + ElemV; }
|
||||
///
|
||||
/// \returns A reference to \f$data[0]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& front() {
|
||||
return data[0];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access the first element
|
||||
/// \returns A const-qualified reference to \f$data[0]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& front() const {
|
||||
return data[0];
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A reference to \f$data[N - 1]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& back() {
|
||||
return data[N - 1];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access the last element
|
||||
/// \returns A const-qualified reference to \f$data[N - 1]\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& back() const {
|
||||
return data[N - 1];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
/// \name Capacity
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements in the array
|
||||
/// \returns the number of elements in the array
|
||||
[[nodiscard]] constexpr size_t size() const { return ElemV; }
|
||||
|
||||
///
|
||||
/// \brief returns **true** when the array is empty
|
||||
/// \return \f$ElemV == 0\f$
|
||||
[[nodiscard]] constexpr bool_t empty() const { return ElemV == 0; }
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief
|
||||
/// \brief Checks if all elements in the arrays are equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
friend constexpr bool_t operator==(const array& lhs, const array& rhs) {
|
||||
return array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
|
||||
return array::_compare(lhs, rhs, make_index_metasequence<N>{});
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Checks if any element in the arrays is not equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
friend constexpr bool_t operator!=(const array& lhs, const array& rhs) {
|
||||
return not array::_compare(lhs, rhs, make_index_sequence<ElemV>{});
|
||||
return not array::_compare(lhs, rhs, make_index_metasequence<N>{});
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns A pointer to the first element of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t* begin() {
|
||||
return data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns A const-qualified pointer to the first element of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t* begin() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A pointer to one after the end of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t* end() {
|
||||
return data + N;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns A const-qualified pointer to one after the end of the array
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t* end() const {
|
||||
return data + N;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<size_t...i>
|
||||
static bool _compare(const array& lhs, const array& rhs, const_index_sequence<i...>) {
|
||||
return ((lhs[i] == rhs[i]) && ...);
|
||||
static bool _compare(const array& lhs, const array& rhs, index_metasequence<i...>) {
|
||||
return ((lhs[i] == rhs[i]) and ...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1268
include/fennec/containers/bintree.h
Normal file
1268
include/fennec/containers/bintree.h
Normal file
File diff suppressed because it is too large
Load Diff
323
include/fennec/containers/bitfield.h
Normal file
323
include/fennec/containers/bitfield.h
Normal file
@@ -0,0 +1,323 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/bitfield.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_BITFIELD_H
|
||||
#define FENNEC_CONTAINERS_BITFIELD_H
|
||||
|
||||
#include <fennec/containers/array.h>
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Bitfield Container with basic Bit Ops
|
||||
///
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam N The number of bits in the bitfield
|
||||
template<size_t N>
|
||||
struct bitfield {
|
||||
|
||||
// Constants ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t bits = N; //!< The number of bits in the bitfield
|
||||
static constexpr size_t bytes = (N + 7) / 8; //!< The number of bytes that hold the bitfield
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default constructor.
|
||||
/// \details Initializes all bits with \f$0\f$.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr bitfield()
|
||||
: _bytes() {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Boolean array constructor.
|
||||
/// \param arr An array of boolean values resembling each bit.
|
||||
/// \details Initializes each bit with the respective boolean value in \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
explicit constexpr bitfield(const bool (&arr)[N])
|
||||
: _bytes() {
|
||||
for (size_t i = 0; i < arr; ++i) {
|
||||
this->store(i, arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Index array constructor.
|
||||
/// \param arr An array of indices.
|
||||
/// \details Sets the bits of each index provided in \f$arr\f$.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t I>
|
||||
explicit constexpr bitfield(const size_t (&arr)[I])
|
||||
: _bytes() {
|
||||
for (size_t i : arr) {
|
||||
this->set(i);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \param args A set of indices.
|
||||
/// \details This substitution assumes \f$ArgsT\ldots\f$ can be taken as an array of indices. <br>
|
||||
/// Sets the bits of each index provided in \f$args\ldots\f$.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr bitfield(ArgsT&&...args)
|
||||
: _bytes() {
|
||||
(this->store(fennec::forward<ArgsT>(args), true), ...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Variadic array constructor
|
||||
/// \param args A set of boolean values.
|
||||
/// \details This substitution assumes \f$ArgsT\ldots\f$ can be taken as an array of booleans. <br>
|
||||
/// Initializes each bit with the respective boolean in \f$args\ldots\f$. <br>
|
||||
/// Does not necessitate the number of arguments be equal to the number of bits.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT> requires((is_bool_v<ArgsT> or is_convertible_v<ArgsT, bool>) and ...)
|
||||
constexpr bitfield(ArgsT&&...args)
|
||||
: _bytes() {
|
||||
size_t i = 0;
|
||||
(this->store(i++, fennec::forward<ArgsT>(args)), ...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param bf bitfield to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
bitfield(const bitfield& bf)
|
||||
: _bytes(bf._bytes) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param bf bitfield to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bitfield(bitfield&& bf) noexcept
|
||||
: _bytes(bf._bytes) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
constexpr ~bitfield() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param bf bitfield to copy
|
||||
/// \returns a reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
bitfield& operator=(const bitfield& bf) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param bf bitfield to move
|
||||
/// \returns a reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bitfield& operator=(bitfield&& bf) noexcept = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access & Modifiers ==================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access & Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief test a bit
|
||||
/// \param i the index of the bit
|
||||
/// \returns the value stored in the bit as a boolean
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bool test(size_t i) const {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
return _bytes[b] & (1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief set a bit
|
||||
/// \param i the index of the bit
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void set(size_t i) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
_bytes[b] |= (1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief clear a bit
|
||||
/// \param i the index of the bit
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void clear(size_t i) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
_bytes[b] &= ~(1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief toggle a bit
|
||||
/// \param i the index of the bit
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void toggle(size_t i) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
_bytes[b] ^= (1 << o);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief store \f$v\f$ in bit \f$i\f$
|
||||
/// \param i the index of the bit
|
||||
/// \param v the value to store
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void store(size_t i, bool v) {
|
||||
assertd(i < bits, "Index out of Bounds!");
|
||||
size_t b = i / 8;
|
||||
size_t o = i % 8;
|
||||
(_bytes[b] &= ~((1 << o))) |= ((v << o));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief not operator
|
||||
/// \returns a bitfield containing the bit-wise inverse
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
bitfield operator~() const {
|
||||
bitfield res = *this;
|
||||
res._inv_helper(make_index_metasequence_t<bytes>{});
|
||||
return res;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
array<uint8_t, bytes> _bytes;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<size_t...I>
|
||||
void _inv_helper(index_metasequence<I...>) {
|
||||
((_bytes[I] = ~_bytes[I]), ...);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_BITFIELD_H
|
||||
112
include/fennec/containers/containers.h
Normal file
112
include/fennec/containers/containers.h
Normal file
@@ -0,0 +1,112 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/containers.h
|
||||
/// \brief fennec containers library main header
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_CONTAINERS_H
|
||||
#define FENNEC_CONTAINERS_CONTAINERS_H
|
||||
|
||||
///
|
||||
/// \page fennec_containers Containers Library
|
||||
///
|
||||
/// \brief The fennec Containers Library.
|
||||
/// Includes various data structures, those specified in the C++ Standard Library, and custom data structures
|
||||
/// that fennec uses.
|
||||
///
|
||||
/// \code #include <fennec/containers/containers.h> \endcode
|
||||
///
|
||||
/// \section fennec_containers_container_section_properties Container Properties
|
||||
///
|
||||
/// | Property | Meaning |
|
||||
/// |:----------------|:-----------------------------------------------------------------------------------------------|
|
||||
/// | **stable** | Any pointer reference to an element remains constant for the lifetime of the container. |
|
||||
/// | **dynamic** | Memory for this container is allocated on the heap. |
|
||||
/// | **homogeneous** | The types of all elements are either identical, or inherit the same base type. |
|
||||
/// | **distinct** | Elements are guaranteed to be unique in their value. |
|
||||
/// | **ordered** | Elements are guaranteed to be in order, such that for any index \f$i\f$, \f$E_i < E_{i + 1}\f$ |
|
||||
/// | **space** | The amount of memory allocated with respect to the number of elements, in big-O notation. |
|
||||
/// | **linear** | Each element is sequential in terms of access. |
|
||||
/// | **access** | The runtime of the access operators and functions, in big-O notation. |
|
||||
/// | **find** | The runtime of finding an element in the container, in big-O notation. |
|
||||
/// | **insertion** | The runtime of inserting an element in the container, in big-O notation. |
|
||||
/// | **deletion** | The runtime of erasing an element in the container, in big-O notation. |
|
||||
/// | **space** | The space complexity of the container. |
|
||||
///
|
||||
///
|
||||
/// \section fennec_containers_section_cppstdlib C++ Standard Template Library
|
||||
///
|
||||
/// | Symbol | Implemented | Passed |
|
||||
/// |:----------------------------------------------------------------------------|:-----------:|:------:|
|
||||
/// | \ref fennec::generic "fennec::generic" `std::any` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::array "fennec::array" | ✅ | ✅ |
|
||||
/// | \ref fennec::bitfield "fennec::bitfield" `std::bitset` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::deque "fennec::deque" | 🚧 | 🚧 |
|
||||
/// | \ref fennec::dynarray "fennec::dynarray" `std::vector` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::list "fennec::list" | ✅ | ✅ |
|
||||
/// | \ref fennec::map "fennec::map" `std::unordered_map` | ✅ | ✅ |
|
||||
/// | \ref fennec::map_sequence "fennec::map_sequence" `std::map` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multiset "fennec::multiset" `std::unordered_multiset` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multisequence "fennec::multisequence" `std::multiset` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multimap "fennec::multimap" `std::unordered_multimap` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::multimap_sequence "fennec::multimap_sequence" `std::multimap` | ⛔ | ⛔ |
|
||||
/// | \ref fennec::optional "fennec::optional" | ✅ | ✅ |
|
||||
/// | \ref fennec::pair "fennec::pair" | ✅ | ✅ |
|
||||
/// | \ref fennec::sequence "fennec::sequence" `std::set` | 🚧 | 🚧 |
|
||||
/// | \ref fennec::set "fennec::set" `std::unordered_set` | ✅ | ✅ |
|
||||
/// | \ref fennec::tuple "fennec::tuple" | 🚧 | 🚧 |
|
||||
/// | \ref fennec::variant "fennec::variant" | 🚧 | 🚧 |
|
||||
///
|
||||
///
|
||||
/// \section fennec_containers_section_fennec fennec
|
||||
///
|
||||
/// | Symbol | Implemented | Passed |
|
||||
/// |:-------------------------|:-----------:|:------:|
|
||||
/// | \ref fennec::graph | 🚧 | 🚧 |
|
||||
/// | \ref fennec::object_pool | 🚧 | 🚧 |
|
||||
/// | \ref fennec::rdtree | ✅ | ✅ |
|
||||
///
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
#include <fennec/containers/traversal.h>
|
||||
|
||||
#include <fennec/containers/array.h>
|
||||
#include <fennec/containers/deque.h>
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/graph.h>
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/map.h>
|
||||
#include <fennec/containers/object_pool.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/pair.h>
|
||||
#include <fennec/containers/rdtree.h>
|
||||
#include <fennec/containers/set.h>
|
||||
#include <fennec/containers/tuple.h>
|
||||
|
||||
#endif // FENNEC_CONTAINERS_CONTAINERS_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/deque.h
|
||||
/// \brief A header containing the definition for a double-ended queue
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DEQUE_H
|
||||
#define FENNEC_CONTAINERS_DEQUE_H
|
||||
|
||||
@@ -26,31 +38,63 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief Data structure defining a double-ended queue.
|
||||
///
|
||||
/// \details
|
||||
/// This behaves the similar to fennec::list, however it does not allow arbitrary access, insertion, or deletion.
|
||||
/// It is one of the few data structures in this library that is stable, i.e. pointers to elements do not change.
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct deque {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using elem_t = TypeT;
|
||||
|
||||
struct node {
|
||||
elem_t value;
|
||||
node *prev, *next;
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
template<typename...ArgsT>
|
||||
node(node* prev, node* next, ArgsT&&...args)
|
||||
: value(fennec::forward<ArgsT>(args)...)
|
||||
, prev(prev), next(next) {
|
||||
}
|
||||
using value_t = TypeT; //!< Alias for the value type
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>; //!< The underlying allocator type
|
||||
using elem_t = node*; //!< The underlying element type
|
||||
|
||||
~node() = default;
|
||||
};
|
||||
/// @}
|
||||
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
||||
|
||||
class iterator;
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
deque()
|
||||
: _alloc()
|
||||
, _first(nullptr)
|
||||
@@ -58,6 +102,46 @@ public:
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Constructor, initializes an empty deque with the specified allocator
|
||||
/// \param alloc the allocator to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
deque(const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _first(nullptr)
|
||||
, _last(nullptr)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param deque the deque to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
deque(const deque& deque)
|
||||
: _alloc(deque._alloc)
|
||||
, _first(nullptr)
|
||||
, _last(nullptr)
|
||||
, _size(0) {
|
||||
const elem_t node = deque._first;
|
||||
while (node) {
|
||||
this->push_back(node->value);
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Deque Move Constructor
|
||||
/// \param deque the deque to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
deque(deque&& deque) noexcept
|
||||
: _alloc(deque._alloc)
|
||||
, _first(deque._first)
|
||||
@@ -67,69 +151,184 @@ public:
|
||||
deque._last = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, calls deque::clear
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
~deque() {
|
||||
clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
bool empty() const {
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when the deque is empty, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
///
|
||||
/// \returns the number of elements in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
elem_t& front() {
|
||||
assert(not empty(), "Attempted to access an empty deque.");
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns a reference to the first element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
value_t& front() {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _first->value;
|
||||
}
|
||||
|
||||
elem_t& back() {
|
||||
assert(not empty(), "Attempted to access an empty deque.");
|
||||
///
|
||||
/// \returns a const-qualified reference to the first element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const value_t& front() const {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _first->value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a reference to the last element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
value_t& back() {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _last->value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a const-qualified reference to the last element in the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const value_t& back() const {
|
||||
assert(not is_empty(), "Attempted to access an empty deque.");
|
||||
return _last->value;
|
||||
}
|
||||
|
||||
// Insertion ===========================================================================================================
|
||||
/// @}
|
||||
|
||||
void push_front(elem_t&& elem) {
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Push Front Move, moves a value to the front of the deque
|
||||
/// \param elem the value to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_front(value_t&& elem) {
|
||||
this->_push_front(elem);
|
||||
}
|
||||
|
||||
void push_front(const elem_t& elem) {
|
||||
///
|
||||
/// \brief Push Front Copy, copies a value to the front of the deque
|
||||
/// \param elem the value to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_front(const value_t& elem) {
|
||||
this->_push_front(elem);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Front, constructs a new value at the front of the deque
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments used to construct the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
void emplace_front(ArgsT&&...args) {
|
||||
this->_push_front(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
void push_back(elem_t&& elem) {
|
||||
|
||||
///
|
||||
/// \brief Push Back Move, moves a value to the back of the deque
|
||||
/// \param elem the value to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_back(value_t&& elem) {
|
||||
this->_push_back(elem);
|
||||
}
|
||||
|
||||
void push_back(const elem_t& elem) {
|
||||
///
|
||||
/// \brief Push Back Copy, copies a value to the back of the deque
|
||||
/// \param elem the value to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void push_back(const value_t& elem) {
|
||||
this->_push_back(elem);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Back, constructs a new value at the back of the deque
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments used to construct the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
void emplace_back(ArgsT&&...args) {
|
||||
this->_push_back(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
|
||||
// Deletion ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Clears the contents of the deque
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
void clear() {
|
||||
node* it = _first;
|
||||
elem_t it = _first;
|
||||
while (it) {
|
||||
node* next = it->next;
|
||||
elem_t next = it->next;
|
||||
fennec::destruct(it);
|
||||
_alloc.deallocate(it);
|
||||
it = next;
|
||||
@@ -139,11 +338,17 @@ public:
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase the First Element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void pop_front() {
|
||||
if (_first == nullptr) {
|
||||
return;
|
||||
}
|
||||
node* next = _first->next;
|
||||
elem_t next = _first->next;
|
||||
fennec::destruct(_first);
|
||||
_alloc.deallocate(_first);
|
||||
_first = next;
|
||||
@@ -151,11 +356,17 @@ public:
|
||||
--_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase the Last Element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void pop_back() {
|
||||
if (_last == nullptr) {
|
||||
return;
|
||||
}
|
||||
node* prev = _last->prev;
|
||||
elem_t prev = _last->prev;
|
||||
fennec::destruct(_last);
|
||||
_alloc.deallocate(_last);
|
||||
_last = prev;
|
||||
@@ -163,14 +374,28 @@ public:
|
||||
--_size;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
|
||||
/*
|
||||
* TODO: Decide whether you should be able to iterate over a deque
|
||||
*/
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
alloc_t _alloc;
|
||||
node *_first, *_last;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<typename...ArgsT>
|
||||
void _push_front(ArgsT&&...args) {
|
||||
node* next = _first;
|
||||
elem_t next = _first;
|
||||
_first = _alloc.allocate(1);
|
||||
fennec::construct(_first, nullptr, next, fennec::forward<ArgsT>(args)...);
|
||||
if (next) {
|
||||
@@ -183,7 +408,7 @@ private:
|
||||
|
||||
template<typename...ArgsT>
|
||||
void _push_back(ArgsT&&...args) {
|
||||
node* prev = _last;
|
||||
elem_t prev = _last;
|
||||
_last = _alloc.allocate(1);
|
||||
fennec::construct(_last, prev, nullptr, fennec::forward<ArgsT>(args)...);
|
||||
if (prev) {
|
||||
@@ -193,6 +418,22 @@ private:
|
||||
}
|
||||
++_size;
|
||||
}
|
||||
|
||||
|
||||
// Private Definitions =================================================================================================
|
||||
private:
|
||||
struct node {
|
||||
value_t value;
|
||||
node *prev, *next;
|
||||
|
||||
template<typename...ArgsT>
|
||||
node(node* prev, node* next, ArgsT&&...args)
|
||||
: value(fennec::forward<ArgsT>(args)...)
|
||||
, prev(prev), next(next) {
|
||||
}
|
||||
|
||||
~node() = default;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -18,20 +18,23 @@
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
#define FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||
#include <fennec/lang/const_sequences.h>
|
||||
#include <fennec/lang/metasequences.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
|
||||
template <std::size_t I, typename T>
|
||||
template <size_t I, typename T>
|
||||
struct _tuple_leaf
|
||||
{
|
||||
template <typename ArgT>
|
||||
_tuple_leaf(ArgT&& arg) : value(fennec::forward<ArgT>(arg)) {}
|
||||
constexpr _tuple_leaf(ArgT&& arg) : value(fennec::forward<ArgT>(arg)) {}
|
||||
|
||||
~_tuple_leaf() = default;
|
||||
constexpr ~_tuple_leaf() = default;
|
||||
|
||||
constexpr _tuple_leaf& operator=(const _tuple_leaf&) = default;
|
||||
constexpr _tuple_leaf& operator=(_tuple_leaf&&) noexcept = default;
|
||||
|
||||
T value;
|
||||
};
|
||||
@@ -40,12 +43,17 @@ template <typename, typename...>
|
||||
struct _tuple;
|
||||
|
||||
template <size_t...IndicesV, typename...TypesT>
|
||||
struct _tuple<const_index_sequence<IndicesV...>, TypesT...> : _tuple_leaf<IndicesV, TypesT>...
|
||||
struct _tuple<index_metasequence<IndicesV...>, TypesT...> : _tuple_leaf<IndicesV, TypesT>...
|
||||
{
|
||||
template <typename...ArgsT>
|
||||
_tuple(ArgsT&&... args) : _tuple_leaf<IndicesV, TypesT>(fennec::forward<ArgsT>(args))... {}
|
||||
constexpr _tuple(ArgsT&&... args)
|
||||
: _tuple_leaf<IndicesV, TypesT>(fennec::forward<ArgsT>(args))... {
|
||||
}
|
||||
|
||||
~_tuple() = default;
|
||||
constexpr _tuple& operator=(const _tuple&) = default;
|
||||
constexpr _tuple& operator=(_tuple&&) noexcept = default;
|
||||
|
||||
constexpr ~_tuple() = default;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,20 +17,21 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file dynarray.h
|
||||
/// \brief dynamically allocated array wrapper
|
||||
/// \file fennec/containers/dynarray.h
|
||||
/// \brief A header containing the definition for a dynamically allocated array
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_DYNARRAY_H
|
||||
#define FENNEC_CONTAINERS_DYNARRAY_H
|
||||
|
||||
#include <fennec/containers/initializer_list.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
#include <fennec/memory/new.h>
|
||||
@@ -40,219 +41,490 @@ namespace fennec
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for dynamically sized arrays
|
||||
/// \brief Wrapper for dynamically sized and allocated arrays
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |-----------|:----------:|
|
||||
/// | stable | \emoji x |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(N)\f$ |
|
||||
/// | deletion | \f$O(N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// This structure prefers shallow moves and deep copies.
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||
class dynarray {
|
||||
public:
|
||||
struct dynarray {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
using element_t = TypeT;
|
||||
using alloc_t = Alloc;
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< Alias for the value type
|
||||
using alloc_t = Alloc; //!< Alias for the allocator type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty allocation.
|
||||
constexpr dynarray() : _alloc(8), _size(0) {}
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr dynarray()
|
||||
: _alloc(8)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Constructor, initialize empty allocation with allocator instance.
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
|
||||
constexpr dynarray(const alloc_t& alloc)
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
|
||||
/// data.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
explicit constexpr dynarray(const alloc_t& alloc)
|
||||
: _alloc(8, alloc)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Move Constructor, initialize empty allocation with allocator instance.
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
|
||||
constexpr dynarray(alloc_t&& alloc) noexcept
|
||||
: _alloc(8, alloc)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
/// \brief Sized Allocation, initializes a dynarray with \f$n\f$ elements using the default constructor.
|
||||
/// \param n The number of elements.
|
||||
///
|
||||
/// \brief Sized Allocation, create an allocation of size `n` elements, initialized with the default constructor.
|
||||
constexpr dynarray(size_t n)
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
explicit constexpr dynarray(size_t n)
|
||||
: _alloc(n)
|
||||
, _size(n)
|
||||
{
|
||||
element_t* addr = _alloc.data();
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator `alloc` and `n` elements using the default constructor.
|
||||
/// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator \f$alloc\f$ and \f$n\f$ elements
|
||||
/// using the default constructor.
|
||||
/// \param n The number of elements
|
||||
/// \param alloc The allocator object to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(size_t n, const alloc_t& alloc)
|
||||
: _alloc(n, alloc)
|
||||
, _size(n) {
|
||||
element_t* addr = _alloc.data();
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation Alloc Move Constructor, initializes a dynarray with allocator `alloc` and `n` elements using the default constructor.
|
||||
/// \param n The number of elements
|
||||
/// \param alloc The allocator object to copy
|
||||
constexpr dynarray(size_t n, alloc_t&& alloc)
|
||||
: _alloc(n, alloc)
|
||||
, _size(n) {
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Sized Allocation Copy Constructor, Create an allocation of size \f$n\f$ elements, with each element
|
||||
/// constructed using the copy constructor
|
||||
/// \param n the number of elements
|
||||
/// \param val the value to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
/// \brief Sized Allocation Copy Constructor, Create an allocation of size `n` elements, with each element constructed using the copy constructor
|
||||
/// \brief n the number of elements
|
||||
constexpr dynarray(size_t n, const TypeT& val)
|
||||
: _alloc(n)
|
||||
, _size(n) {
|
||||
element_t* addr = _alloc.data();
|
||||
value_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
||||
// behaviour when moving to multiple elements
|
||||
constexpr dynarray(size_t n, TypeT&&) = delete;
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam ArgsT A sequence of argument types
|
||||
/// \param n The number of objects to create
|
||||
/// \param args The arguments to create each object with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr dynarray(size_t n, ArgsT&&...args) {
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr, fennec::forward<ArgsT>(args)...);
|
||||
constexpr explicit dynarray(size_t n, ArgsT&&...args)
|
||||
: _alloc(n)
|
||||
, _size(n) {
|
||||
for(; n > 0; --n) {
|
||||
fennec::construct(&_alloc[n], fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Copy Constructor
|
||||
/// \tparam N The length of the array, automatically deduced
|
||||
/// \param arr The array to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray(const TypeT (&arr)[N])
|
||||
: _alloc(N)
|
||||
, _size(N) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
_alloc[i] = arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Move Constructor
|
||||
/// \tparam N The length of the array, automatically deduced
|
||||
/// \param arr The array to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray(TypeT (&&arr)[N])
|
||||
: _alloc(N)
|
||||
, _size(N) {
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
_alloc[i] = fennec::move(arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Conversion Constructor, copies elements of conv as this \f$value_t\f$
|
||||
/// \tparam OTypeT The other value type
|
||||
/// \tparam OAlloc The other allocator type
|
||||
/// \param conv The dynarray to convert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename OTypeT, class OAlloc>
|
||||
constexpr dynarray(const dynarray<OTypeT, OAlloc>& conv)
|
||||
: _alloc(conv.size())
|
||||
, _size(conv.size()) {
|
||||
size_t i = 0;
|
||||
for (const auto& it : conv) {
|
||||
fennec::construct(&_alloc[i++], it);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Initializer List Constructor
|
||||
/// \param l List of elements to initialize with
|
||||
/// \param alloc An allocator object to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(initializer_list<value_t> l, const alloc_t& alloc = alloc_t())
|
||||
: _alloc(l.size(), alloc)
|
||||
, _size(l.size()) {
|
||||
size_t i = 0;
|
||||
for (auto& it : l) {
|
||||
fennec::construct(&_alloc[i++], fennec::move(it));
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, uses the copy constructor to copy each element
|
||||
/// \param arr the dynarray to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray(const dynarray& arr)
|
||||
: _alloc(arr._size)
|
||||
, _size(arr._size) {
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], arr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of the allocation
|
||||
/// \param arr the dynarray to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr dynarray(dynarray&& arr) noexcept
|
||||
: _alloc(fennec::move(arr._alloc))
|
||||
, _size(arr._size) {
|
||||
arr._size = 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~dynarray() {
|
||||
element_t* addr = _alloc.data();
|
||||
value_t* addr = _alloc.data();
|
||||
if (addr == nullptr) return;
|
||||
for(int n = _size; n > 0; --n, ++addr) {
|
||||
fennec::destruct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
private:
|
||||
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
||||
// behaviour when moving to multiple elements
|
||||
constexpr dynarray(size_t n, TypeT&& val) = delete;
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param arr the array to copy
|
||||
/// \returns A dynarray after having copied each element of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr dynarray& operator=(const dynarray& arr) {
|
||||
this->clear();
|
||||
_alloc.reallocate(_size = arr._size);
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param arr the array to move
|
||||
/// \returns A dynarray after having taken ownership of the contents of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr dynarray& operator=(dynarray&& arr) noexcept {
|
||||
this->clear();
|
||||
_alloc = fennec::move(arr._alloc);
|
||||
_size = arr._size;
|
||||
arr._size = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Copy Assignment Operator
|
||||
/// \tparam N the length of the array
|
||||
/// \param arr the array to copy
|
||||
/// \returns A dynarray after having copied each element of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray& operator=(const TypeT (&arr)[N]) {
|
||||
this->clear();
|
||||
_alloc.reallocate(_size = N);
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Copy Assignment Operator
|
||||
/// \tparam N the length of the array
|
||||
/// \param arr the array to copy
|
||||
/// \returns A dynarray after having moved each element of \f$arr\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<size_t N>
|
||||
constexpr dynarray& operator=(TypeT (&&arr)[N]) {
|
||||
this->clear();
|
||||
_alloc.reallocate(_size = N);
|
||||
for (size_t i = 0; i < _size; ++i) {
|
||||
fennec::construct(&_alloc[i], fennec::move(arr[i]));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The size of the dynarray in elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The current capacity, in elements, of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns True when there are no elements active, otherwise false
|
||||
constexpr bool empty() const {
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Element Access ======================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i The index to access
|
||||
/// \returns A reference to the element at index `i`
|
||||
constexpr TypeT& operator[](int i) {
|
||||
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
|
||||
return _alloc.data()[i];
|
||||
/// \returns A reference to the element at index \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT& operator[](size_t i) {
|
||||
assertd(i < _size, "Array Out of Bounds");
|
||||
return _alloc[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator (const)
|
||||
/// \param i The index to access
|
||||
/// \returns A const qualified reference to the element at index `i`
|
||||
constexpr const TypeT& operator[](int i) const {
|
||||
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
|
||||
return _alloc.data()[i];
|
||||
/// \returns A const qualified reference to the element at index \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT& operator[](size_t i) const {
|
||||
assertd(i < _size, "Array Out of Bounds");
|
||||
return _alloc[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Reference to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT& front() {
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified reference to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT& front() const {
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A reference to the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT& back() {
|
||||
return this->operator[](size() - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified reference to the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT& back() const {
|
||||
return this->operator[](size() - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief "Iterator" Begin Function
|
||||
/// \returns A pointer to the first element in the dynarray
|
||||
constexpr TypeT* begin() { return _alloc.data(); }
|
||||
/// \returns A pointer to the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT* data() {
|
||||
return _alloc.data();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief "Iterator" End Function
|
||||
/// \return A pointer to the address after the last element in the dynarray
|
||||
constexpr TypeT* end() { return begin() + _size; }
|
||||
|
||||
/// \returns A pointer to the underlying allocation
|
||||
///
|
||||
/// \brief Const "Iterator" Begin Function
|
||||
/// \returns A const qualified pointer to the first element in the dynarray
|
||||
constexpr const TypeT* begin() const { return _alloc; }
|
||||
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \brief Const "Iterator" End Function
|
||||
/// \return A const qualified pointer to the address after the last element in the dynarray
|
||||
constexpr const TypeT* end() const { return begin() + _size; }
|
||||
constexpr const TypeT* data() const {
|
||||
return _alloc.data();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param val the value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void insert(size_t i, TypeT&& val) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
@@ -277,6 +549,10 @@ public:
|
||||
/// \brief Copy Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param val the value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void insert(size_t i, const TypeT& val) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
@@ -303,6 +579,10 @@ public:
|
||||
/// \param i index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(size_t i, ArgsT&&...args) {
|
||||
|
||||
@@ -327,6 +607,10 @@ public:
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param val Value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void push_back(const TypeT& val) {
|
||||
dynarray::insert(_size, val);
|
||||
}
|
||||
@@ -334,6 +618,10 @@ public:
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param val Value to initialize with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void push_back(TypeT&& val) {
|
||||
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
||||
}
|
||||
@@ -342,6 +630,10 @@ public:
|
||||
/// \brief Emplace Back
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace_back(ArgsT...args) {
|
||||
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
|
||||
@@ -349,6 +641,10 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Erase last element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void pop_back() {
|
||||
fennec::destruct(&_alloc[--_size]);
|
||||
}
|
||||
@@ -356,21 +652,135 @@ public:
|
||||
///
|
||||
/// \brief Resize the dynarray, invoking the default constructor for all new elements
|
||||
/// \param n The new size in elements
|
||||
///
|
||||
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void resize(size_t n) {
|
||||
_alloc.creallocate(n);
|
||||
_reduce(n);
|
||||
_alloc.reallocate(n);
|
||||
|
||||
while (_size < n) {
|
||||
emplace_back();
|
||||
}
|
||||
for (size_t i = _size; i < n; ++i) {
|
||||
fennec::construct(&_alloc[i]);
|
||||
}
|
||||
|
||||
_size = n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Resize the dynarray, invoking the copy constructor for all new elements
|
||||
/// \param n The new size in elements
|
||||
/// \param val The value to fill with
|
||||
///
|
||||
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void resize(size_t n, const TypeT& val) {
|
||||
_reduce(n);
|
||||
_alloc.reallocate(n);
|
||||
|
||||
for (size_t i = _size; i < n; ++i) {
|
||||
fennec::construct(&_alloc[i], val);
|
||||
}
|
||||
|
||||
_size = n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Reserve the array, allocating new space without initialization
|
||||
/// \param n The new capacity in elements
|
||||
///
|
||||
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void reserve(size_t n) {
|
||||
_reduce(n);
|
||||
_alloc.reallocate(n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
_reduce(0);
|
||||
_alloc.deallocate();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns A pointer to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT* begin() { return _alloc; }
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns A const qualified pointer to the first element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT* begin() const { return _alloc; }
|
||||
|
||||
|
||||
///
|
||||
/// \return A pointer to the address after the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr TypeT* end() { return begin() + _size; }
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \return A const qualified pointer to the address after the last element in the dynarray
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const TypeT* end() const { return begin() + _size; }
|
||||
|
||||
/// @}
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
private:
|
||||
allocation<value_t, alloc_t> _alloc;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
// helper to double the capacity of the allocation
|
||||
constexpr void _grow() {
|
||||
_alloc.creallocate(_alloc.capacity() * 2);
|
||||
_alloc.reallocate(_alloc.capacity() * 2);
|
||||
}
|
||||
|
||||
allocation<element_t, alloc_t> _alloc;
|
||||
size_t _size;
|
||||
// helper to destruct elements past n
|
||||
constexpr void _reduce(size_t n) {
|
||||
while (_size > n) {
|
||||
fennec::destruct(&_alloc[--_size]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
363
include/fennec/containers/generic.h
Normal file
363
include/fennec/containers/generic.h
Normal file
@@ -0,0 +1,363 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/generic.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_GENERIC_H
|
||||
#define FENNEC_CONTAINERS_GENERIC_H
|
||||
|
||||
#include <fennec/memory/allocator.h>
|
||||
#include <fennec/rtti/type.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief A struct capable of holding a single object of any type
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
struct generic {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
|
||||
// based on GCC
|
||||
enum op_ : uint8_t {
|
||||
op_clone,
|
||||
op_destroy,
|
||||
op_type,
|
||||
};
|
||||
|
||||
using manager_t = void* (*)(uint8_t, void*);
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic()
|
||||
: _handle(nullptr)
|
||||
, _manage(nullptr) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param gen The generic object to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic(const generic& gen)
|
||||
: _handle(nullptr)
|
||||
, _manage(gen._manage) {
|
||||
if (_manage) {
|
||||
_handle = _manage(op_clone, gen._handle);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
/// \param gen The generic object to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic(generic&& gen)
|
||||
: _handle(gen._handle)
|
||||
, _manage(gen._manage) {
|
||||
gen._handle = nullptr;
|
||||
gen._manage = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Value Constructor
|
||||
/// \tparam T The type of the value
|
||||
/// \param x The value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
generic(T&& x)
|
||||
: _handle(new T(fennec::forward<T>(x)))
|
||||
, _manage(_manage_impl<T>) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam T The type to construct
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT>
|
||||
generic(type_identity<T>, ArgsT&&...args)
|
||||
: _handle(new T(fennec::forward<ArgsT>(args)...))
|
||||
, _manage(_manage_impl<T>) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
~generic() {
|
||||
reset();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief runtime type acquisition
|
||||
/// \returns a runtime type struct referencing the held type
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
type type() const {
|
||||
return *static_cast<fennec::type*>(_manage(op_type, nullptr));
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if there is a held value, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
bool has_value() const {
|
||||
return _handle != nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param gen the generic to copy
|
||||
/// \returns a reference to self after copying the contents of \f$gen\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic& operator=(const generic& gen) {
|
||||
if (this == &gen) { // self-assignment case
|
||||
return *this;
|
||||
}
|
||||
|
||||
reset();
|
||||
_manage = gen._manage;
|
||||
_handle = _manage(op_clone, gen._handle);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param gen the generic to move
|
||||
/// \returns a reference to self after swapping contents with \f$gen\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
generic& operator=(generic&& gen) noexcept {
|
||||
swap(gen);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief value assignment
|
||||
/// \tparam T the type of the value
|
||||
/// \param x the value to assign
|
||||
/// \returns a reference to self after having assigned \f$x\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
generic& operator=(T&& x) {
|
||||
reset();
|
||||
_handle = new T(fennec::forward<T>(x));
|
||||
_manage = _manage_impl<T>();
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief emplace value
|
||||
///
|
||||
/// \details constructs a new value of type \f$T\f$ using \f$args\ldots\f$
|
||||
/// \tparam T the type to construct
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT>
|
||||
void emplace(ArgsT&&...args) {
|
||||
reset();
|
||||
_handle = new T(fennec::forward<ArgsT>(args)...);
|
||||
_manage = _manage_impl<T>;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief reset value
|
||||
/// \details clears the held value using the appropriate destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void reset() {
|
||||
if (_manage) {
|
||||
_handle = _manage(op_destroy, _handle);
|
||||
_manage = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ 11 Swap Specification
|
||||
/// \param gen the generic to swap with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void swap(generic& gen) noexcept {
|
||||
fennec::swap(_handle, gen._handle);
|
||||
fennec::swap(_manage, gen._manage);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Casting =============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Casting
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief cast value
|
||||
///
|
||||
/// \details equivalent to \f$reinterpret_cast\f$
|
||||
/// \tparam T The type to cast to
|
||||
/// \returns The contents of generic after having cast to \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename U = remove_cvref_t<T>>
|
||||
T cast() {
|
||||
return static_cast<T>(*static_cast<U*>(_handle));
|
||||
}
|
||||
|
||||
///
|
||||
/// \details equivalent to \f$reinterpret_cast\f$
|
||||
/// \tparam T The type to cast to
|
||||
/// \returns The contents of generic after having cast to \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename U = remove_cvref_t<T>>
|
||||
T cast() const {
|
||||
return static_cast<T>(*static_cast<U*>(_handle));
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
void* _handle;
|
||||
manager_t _manage;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<typename T>
|
||||
static void* _manage_impl(uint8_t op, void* hnd) {
|
||||
static fennec::type t = type::get<T>();
|
||||
T* ptr = hnd;
|
||||
|
||||
switch (op) {
|
||||
case op_clone:
|
||||
return new T(*ptr);
|
||||
case op_destroy:
|
||||
delete ptr;
|
||||
return nullptr;
|
||||
case op_type:
|
||||
return &t;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_GENERIC_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/graph.h
|
||||
/// \brief A header containing the definition for a graph of vertices connected by edges
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_GRAPH_H
|
||||
#define FENNEC_CONTAINERS_GRAPH_H
|
||||
|
||||
@@ -28,144 +40,607 @@
|
||||
/*
|
||||
* With the directed tree we were able to cheat a little, the structure has more rules to it which allows
|
||||
* tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign
|
||||
* properties or rules to connections which can simply be an extension to this graph.
|
||||
* properties or rules to edges which can simply be an extension to this graph.
|
||||
*
|
||||
* The most effective way to do this is to have a dynarray of lists, however this results in double the
|
||||
* memory being used. This can also result in two connection objects being created.
|
||||
* memory being used. This can also result in two edge objects being created.
|
||||
*
|
||||
* There is no nice way to avoid the problem of mapping pins to connections
|
||||
* There is no nice way to avoid the problem of mapping vertices to edges
|
||||
*/
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
template<typename NodeT, typename ConnectionT = nullptr_t>
|
||||
///
|
||||
/// \brief Graph Data Structure, describes sets of arbitrarily connected vertices
|
||||
///
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:--------------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(M)\f$ |
|
||||
/// | space | \f$O(N + M)\f$ |
|
||||
///
|
||||
/// Graphs contain vertices and edges. Graphs are either directed
|
||||
/// or undirected. This structure allows the creation of both directed and undirected edges. As
|
||||
/// far as what that means; a directed graph means that edges have direction, where there are edges
|
||||
/// that are "to" and "from," rather than "between" which is used in undirected graphs.
|
||||
///
|
||||
/// An undirected graph is connected if there is a path between every pair of vertices in the graph.
|
||||
///
|
||||
/// A directed graph is weakly connected if replacing all of its directed edges with undirected edges would
|
||||
/// produce a connected graph. We will call this "disjointed"
|
||||
///
|
||||
/// A directed graph is semi-connected if there is a directed path p for \f$u\f$ → \f$v\f$ *or* \f$v\f$ → \f$u\f$ for every
|
||||
/// pair of vertices \f$[u, v]\f$. We will call this "unilateral"
|
||||
///
|
||||
/// A directed graph is strongly-connected if there is a directed path p for \f$u\f$ → \f$v\f$ *and* \f$v\f$ → \f$u\f$ for every pair
|
||||
/// of vertices \f$[u, v]\f$. We will call this "connected"
|
||||
///
|
||||
/// \tparam VertexT The type associated with each vertex
|
||||
/// \tparam EdgeT The type associated with each edge
|
||||
template<typename VertexT, typename EdgeT = empty_t>
|
||||
struct graph {
|
||||
public:
|
||||
using weight_t = ConnectionT;
|
||||
using node_t = NodeT;
|
||||
using conn_map_t = dynarray<map<size_t, size_t>>;
|
||||
using node_pool_t = object_pool<node_t>;
|
||||
using conn_pool_t = object_pool<weight_t>;
|
||||
// Definitions =========================================================================================================
|
||||
|
||||
static constexpr size_t npos = -1;
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using edge_t = EdgeT; //!< Alias for the edge type
|
||||
using vertex_t = VertexT; //!< Alias for the vertex type
|
||||
using vertex_pool_t = object_pool<vertex_t>; //!< Alias for a pool of vertices
|
||||
using edge_map_t = dynarray<map<size_t, size_t>>; //!< Alias for edge mapping
|
||||
using edge_pool_t = object_pool<edge_t>; //!< Alias for a pool of edges
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t npos = -1; //!< Constant for a non-existent vertex
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty graph
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr graph() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N + M)\f$
|
||||
///
|
||||
constexpr ~graph() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param g The graph to copy
|
||||
/// \returns A reference to this after assigning g
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N + M)\f$
|
||||
///
|
||||
constexpr graph& operator=(const graph& g) = default;
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param g The graph to copy
|
||||
/// \returns A reference to this after assigning g
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr graph& operator=(graph&& g) = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
constexpr size_t num_nodes() const {
|
||||
return _node_pool.size();
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of vertices in the graph
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t num_vertices() const {
|
||||
return _vertex_pool.size();
|
||||
}
|
||||
|
||||
constexpr size_t num_connections() const {
|
||||
return _conn_pool.size();
|
||||
///
|
||||
/// \returns The number of edges in the graph
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t num_edges() const {
|
||||
return _edge_pool.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the vertex pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _node_pool.capacity();
|
||||
return _vertex_pool.capacity();
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
return num_nodes() == 0;
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no vertices in the graph, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return num_vertices() == 0;
|
||||
}
|
||||
|
||||
|
||||
// Nodes ===============================================================================================================
|
||||
|
||||
constexpr size_t insert(node_t&& node) {
|
||||
return this->_insert(fennec::forward<node_t>(node));
|
||||
///
|
||||
/// \brief Checks if there exists an edge \f$e\f$ that starts from \f$a\f$ and ends at \f$b\f$
|
||||
/// \param a The first vertex
|
||||
/// \param b The second vertex
|
||||
/// \returns \f$true\f$ if the edge exists, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool exists(size_t a, size_t b) const {
|
||||
return _edge_map[a][b] != nullptr;
|
||||
}
|
||||
|
||||
constexpr size_t insert(const node_t& node) {
|
||||
return this->_insert(node);
|
||||
///
|
||||
/// \brief Checks if there exists an edge \f$e0\f$ that starts from \f$a\f$ and ends at \f$b\f$ and \f$e1\f$ that starts from \f$b\f$
|
||||
/// and ends at \f$a\f$
|
||||
/// \param a The first vertex
|
||||
/// \param b The second vertex
|
||||
/// \returns \f$true\f$ if both edges exist, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_symmetric(size_t a, size_t b) const {
|
||||
return exists(a, b) and exists(b, a);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Checks if there exists an edge \f$e\f$ between \f$a\f$ and \f$b\f$
|
||||
/// \param a The first vertex
|
||||
/// \param b The second vertex
|
||||
/// \returns \f$true\f$ if both edges exist, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_undirected(size_t a, size_t b) const {
|
||||
const auto* e0 = _edge_map[a][b];
|
||||
const auto* e1 = _edge_map[b][a];
|
||||
if (not (e0 != nullptr && e1 != nullptr)) {
|
||||
return false;
|
||||
}
|
||||
return *e0 == *e1;
|
||||
}
|
||||
|
||||
// TODO: connected, disjoint, unilateral, get_component
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief vertex Access Operator
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A reference to the value stored in the vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr vertex_t& operator[](size_t vertex) {
|
||||
return _vertex_pool[vertex];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief vertex Const Access Operator
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A reference to the value stored in the vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const vertex_t& operator[](size_t vertex) const {
|
||||
return _vertex_pool[vertex];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief edge Access Operator
|
||||
/// \param a The id of the first vertex
|
||||
/// \param b The id of the second vertex
|
||||
/// \returns A pointer to the value stored in the edge, \f$nullptr\f$ if not found
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr edge_t* operator[](size_t a, size_t b) {
|
||||
if (is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
edge_t* it = _edge_map[a][b];
|
||||
if (it) {
|
||||
return _edge_pool[*it];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief edge Const Access Operator
|
||||
/// \param a The id of the first vertex
|
||||
/// \param b The id of the second vertex
|
||||
/// \returns A const-qualified pointer to the value stored in the edge, \f$nullptr\f$ if not found
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const edge_t* operator[](size_t a, size_t b) const {
|
||||
if (is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
const edge_t* it = _edge_map[a][b];
|
||||
if (it) {
|
||||
return _edge_pool[*it];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to \f$x\ldots\f$
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ with edges from \f$vertex\f$ to \f$x\ldots\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> outgoing(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (const auto& it : _edge_map[vertex]) {
|
||||
res.push_back(it.first);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge from \f$x\ldots\f$
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ with edges from \f$x\ldots\f$ to \f$vertex\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> incoming(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (size_t n = 0; n < _edge_map.size(); ++n) {
|
||||
if (_edge_map[n][vertex]) {
|
||||
res.push_back(n);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to and from \f$x\ldots\f$
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ that have symmetric edges with \f$vertex\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> symmetric(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (const auto& it : _edge_map[vertex]) {
|
||||
if (_edge_map[it.first][vertex]) {
|
||||
res.push_back(it.first);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to and from \f$x\ldots\f$ and share the same value
|
||||
/// \details
|
||||
/// "Joined" edges may also be referred to as "undirected." A joined, or undirected, edge may be
|
||||
/// turned into a directed edge by changing the weight object associated with the edge, or by
|
||||
/// removing one of the sub-edges.
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A list containing all vertices \f$x\f$ that have symmetric edges with \f$vertex\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
list<size_t> undirected(size_t vertex) {
|
||||
list<size_t> res;
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return res;
|
||||
}
|
||||
for (const auto& it : _edge_map[vertex]) {
|
||||
const auto* at = _edge_map[it.first][vertex];
|
||||
if (at != nullptr && *at == it.second) {
|
||||
res.push_back(it.first);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Getter for the internal storage of mapped edges from this vertex.
|
||||
/// Use this when you want to iterate over edges that start from this vertex.
|
||||
/// \param vertex The id of the vertex
|
||||
/// \returns A pointer to a map containing edges mapped from this vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
const auto* edges(size_t vertex) {
|
||||
if (is_empty() || vertex >= _edge_map.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &_edge_map[vertex];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move a new vertex into the graph
|
||||
/// \param vertex The vertex to move into the graph
|
||||
/// \returns The id of the new vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(vertex_t&& vertex) {
|
||||
return this->_insert(fennec::forward<vertex_t>(vertex));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy a new vertex into the graph
|
||||
/// \param vertex The vertex to copy into the graph
|
||||
/// \returns The id of the new vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(const vertex_t& vertex) {
|
||||
return this->_insert(vertex);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Construct a new vertex in the graph
|
||||
/// \tparam ArgsT The types of the arguments
|
||||
/// \param args The arguments to construct the vertex with
|
||||
/// \returns The id of the new vertex
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(ArgsT&&...args) {
|
||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
constexpr void erase(size_t node) {
|
||||
disconnect(node);
|
||||
_node_pool.erase(node);
|
||||
}
|
||||
|
||||
constexpr node_t& operator[](size_t node) {
|
||||
return _node_pool[node];
|
||||
}
|
||||
|
||||
constexpr const node_t& operator[](size_t node) const {
|
||||
return _node_pool[node];
|
||||
}
|
||||
|
||||
// Connections =========================================================================================================
|
||||
|
||||
list<size_t> connections(size_t n) {
|
||||
list<size_t> conns;
|
||||
if (_conn_map.empty()) return conns;
|
||||
|
||||
for (auto it : _conn_map[n]) {
|
||||
conns.push_back(it.first);
|
||||
}
|
||||
return conns;
|
||||
///
|
||||
/// \brief Erase a vertex from the graph
|
||||
/// \param vertex The id of the vertex to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
constexpr void erase(size_t vertex) {
|
||||
cut(vertex);
|
||||
_vertex_pool.erase(vertex);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Form an edge from vertex \f$a\f$ to vertex \f$b\f$
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
/// \param args The arguments to construct the edge with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void connect(size_t a, size_t b, ArgsT&&...args) {
|
||||
if (_conn_map.size() < _node_pool.capacity()) {
|
||||
_conn_map.resize(_node_pool.capacity());
|
||||
constexpr void make_edge(size_t a, size_t b, ArgsT&&...args) {
|
||||
if (a == b) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = _conn_map[a][b];
|
||||
if (_edge_map.size() < _vertex_pool.capacity()) {
|
||||
_edge_map.resize(_vertex_pool.capacity());
|
||||
}
|
||||
|
||||
auto it = _edge_map[a][b];
|
||||
size_t conn;
|
||||
if (it != nullptr) {
|
||||
conn = *it;
|
||||
_conn_pool[conn] = node_t(fennec::forward<ArgsT>(args)...);
|
||||
_edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
_edge_map[a].emplace(b, conn);
|
||||
}
|
||||
|
||||
_conn_map[a].emplace(b, conn);
|
||||
_conn_map[b].emplace(a, conn);
|
||||
///
|
||||
/// \brief Form an undirected edge between vertex \f$a\f$ and vertex \f$b\f$
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
/// \param args The arguments to construct the edge with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void make_edge2(size_t a, size_t b, ArgsT&&...args) {
|
||||
if (a == b) {
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr void disconnect(size_t a, size_t b) {
|
||||
size_t c = *_conn_map[a][b];
|
||||
_conn_pool.erase(c);
|
||||
_conn_map[a].erase(b);
|
||||
_conn_map[b].erase(a);
|
||||
if (_edge_map.size() < _vertex_pool.capacity()) {
|
||||
_edge_map.resize(_vertex_pool.capacity());
|
||||
}
|
||||
|
||||
void disconnect(size_t n) {
|
||||
list<size_t> conns = connections(n);
|
||||
for (size_t conn : conns) {
|
||||
disconnect(n, conn);
|
||||
auto it = _edge_map[a][b];
|
||||
size_t conn;
|
||||
if (it != nullptr) {
|
||||
conn = *it;
|
||||
_edge_pool[conn] = vertex_t(fennec::forward<ArgsT>(args)...);
|
||||
} else {
|
||||
conn = _edge_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
_edge_map[a].emplace(b, conn);
|
||||
_edge_map[b].emplace(a, conn);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Disconnect an edge from vertex \f$a\f$ to vertex \f$b\f$
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void cut_edge(size_t a, size_t b) {
|
||||
|
||||
// Find the edge object
|
||||
const auto* it = _edge_map[a][b];
|
||||
if (not it) {
|
||||
return;
|
||||
}
|
||||
size_t c = *it;
|
||||
|
||||
// Check if undirected
|
||||
const auto* at = _edge_map[b][a];
|
||||
if (not at || *at != c) {
|
||||
_edge_pool.erase(c);
|
||||
}
|
||||
|
||||
// Erase the edge mapping
|
||||
_edge_map[a].erase(b);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Disconnect both directed edges between vertices \f$a\f$ and \f$b\f$
|
||||
/// \param a The first vertex id
|
||||
/// \param b The second vertex id
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void cut_edge2(size_t a, size_t b) {
|
||||
const auto* ita = _edge_map[a][b];
|
||||
const auto* itb = _edge_map[a][b];
|
||||
if (not (ita || itb)) {
|
||||
return;
|
||||
}
|
||||
if (ita) _edge_pool.erase(*ita);
|
||||
if (itb) _edge_pool.erase(*itb);
|
||||
_edge_map[a].erase(b);
|
||||
_edge_map[b].erase(a);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Break *all* edges connected to \f$n\f$
|
||||
/// \param n The vertex id
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(M)\f$
|
||||
///
|
||||
void cut(size_t n) {
|
||||
for (const auto it : outgoing(n)) {
|
||||
cut_edge(n, it);
|
||||
}
|
||||
for (const auto it : incoming(n)) {
|
||||
cut_edge(it, n);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr weight_t& operator[](size_t a, size_t b) {
|
||||
return _conn_pool[_conn_map[a][b]];
|
||||
///
|
||||
/// \brief Clear the graph, destructing all vertices and edges.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N + M)\f$
|
||||
///
|
||||
void clear() {
|
||||
_vertex_pool.clear();
|
||||
_edge_pool.clear();
|
||||
_edge_map.clear();
|
||||
}
|
||||
|
||||
constexpr const weight_t& operator[](size_t a, size_t b) const {
|
||||
return _conn_pool[_conn_map[a][b]];
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
// edges =========================================================================================================
|
||||
|
||||
|
||||
private:
|
||||
node_pool_t _node_pool;
|
||||
conn_pool_t _conn_pool;
|
||||
conn_map_t _conn_map;
|
||||
vertex_pool_t _vertex_pool;
|
||||
edge_pool_t _edge_pool;
|
||||
edge_map_t _edge_map;
|
||||
|
||||
template<typename...ArgsT>
|
||||
size_t _insert(ArgsT&&...args) {
|
||||
return _node_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
return _vertex_pool.emplace(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
63
include/fennec/containers/initializer_list.h
Normal file
63
include/fennec/containers/initializer_list.h
Normal file
@@ -0,0 +1,63 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/initializer_list.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_INITIALIZER_LIST_H
|
||||
#define FENNEC_CONTAINERS_INITIALIZER_LIST_H
|
||||
|
||||
// Since initializer lists are completely intertwined with the compiler, and this is part of the c++ standard
|
||||
// (specifically standard, and not the standard template library) we need to use std::initializer_list.
|
||||
// We can at least alias it for proper naming conventions.
|
||||
|
||||
#include <initializer_list>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
using std::initializer_list;
|
||||
|
||||
///
|
||||
/// \param inls the initializer list
|
||||
/// \returns A const qualified pointer to the first element in \f$inls\f$
|
||||
template<typename T>
|
||||
constexpr const T* begin(initializer_list<T> inls) noexcept {
|
||||
return inls.begin();
|
||||
}
|
||||
|
||||
///
|
||||
/// \param inls the initializer list
|
||||
/// \returns A const qualified pointer to one past the last element in \f$inls\f$
|
||||
template<typename T>
|
||||
constexpr const T* end(initializer_list<T> inls) noexcept {
|
||||
return inls.end();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_INITIALIZER_LIST_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file list.h
|
||||
/// \brief List of elements
|
||||
/// \file fennec/containers/list.h
|
||||
/// \brief A header containing the definition for a linked list of values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -43,78 +43,212 @@ namespace fennec
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for lists of elements
|
||||
/// \brief Data Structure defining lists of elements
|
||||
/// \details
|
||||
/// This data-structure behaves like a linked list, but does not use pointers. Instead, it is in-array. This creates the
|
||||
/// following properties:
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:----------|:-------------------------------------:|
|
||||
/// | stable | \emoji x |
|
||||
/// | access | \f$O(N)\f$ or \f$O(1)\f$ (front/back) |
|
||||
/// | insertion | \f$O(N)\f$ or \f$O(1)\f$ (iterator) |
|
||||
/// | deletion | \f$O(N)\f$ or \f$O(1)\f$ (iterator) |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(N)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(N)\f$ |
|
||||
/// | deletion | \f$O(N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \note Access, Insertion, and Deletion are \f$O(N)\f$ using the index pattern,
|
||||
/// using the iterator pattern yields \f$O(1)\f$ runtime.
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||
struct list {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using value_t = TypeT;
|
||||
using elem_t = node;
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
/// \brief Alias for the allocator type, rebound to list nodes
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<node>;
|
||||
|
||||
using value_t = TypeT; //!< Alias for the value type
|
||||
|
||||
static constexpr size_t npos = -1; //!< Constant representing a non-existant position
|
||||
|
||||
/// @}
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
private:
|
||||
using table_t = dynarray<node, alloc_t>;
|
||||
using freed_t = dynarray<size_t>;
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty list.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr list()
|
||||
: _table(), _freed(), _root(npos), _last(npos), _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies all elements in \f$l\f$ with optimized layout
|
||||
/// \param l The list to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr list(const list& l)
|
||||
: list() {
|
||||
for (const value_t& it : l) {
|
||||
this->push_back(it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of the list
|
||||
/// \param l The list to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr list(list&& l) noexcept
|
||||
: _table(fennec::move(l._table))
|
||||
, _freed(fennec::move(l._freed))
|
||||
, _root(l._root)
|
||||
, _last(l._last)
|
||||
, _size(l._size) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, destructs all elements then releases the allocation.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~list() {
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
_table[i].value = nullopt;
|
||||
clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param l the list to copy
|
||||
/// \returns \f$this\f$ after having copied all elements of \f$l\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr list& operator=(const list& l) {
|
||||
this->clear();
|
||||
for (const value_t& it : l) {
|
||||
this->push_back(it);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param l the list to copy
|
||||
/// \returns \f$this\f$ after having taken ownership over the contents of \f$l\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr list& operator=(list&& l) noexcept {
|
||||
this->clear();
|
||||
_table = fennec::move(l._table);
|
||||
_freed = fennec::move(l._freed);
|
||||
_root = l._root; _last = l._last;
|
||||
_size = l._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The size of the list in elements.
|
||||
constexpr size_t size() const { return _size; }
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the list in elements.
|
||||
constexpr size_t capacity() const { return _table.capacity(); }
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns `true` when the list is empty, `false` otherwise.
|
||||
constexpr bool empty() const { return _root == npos; }
|
||||
/// \returns \f$true\f$ when the list is empty, \f$false\f$ otherwise.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _root == npos;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i Index to access
|
||||
/// \returns A reference to the element at `i`
|
||||
/// \returns A reference to the element at \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr value_t& operator[](int i) {
|
||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||
size_t n = _walk(i);
|
||||
@@ -125,9 +259,11 @@ public:
|
||||
///
|
||||
/// \brief Const Array Access Operator
|
||||
/// \param i Index to access
|
||||
/// \returns A const-qualified reference to the element at `i`
|
||||
/// \returns A const-qualified reference to the element at \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr const value_t& operator[](int i) const {
|
||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||
size_t n = _walk(i);
|
||||
@@ -138,6 +274,10 @@ public:
|
||||
///
|
||||
/// \brief Access Front Element
|
||||
/// \returns A reference to the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& front() {
|
||||
return *_table[_root].value;
|
||||
}
|
||||
@@ -145,6 +285,10 @@ public:
|
||||
///
|
||||
/// \brief Const Access Front Element
|
||||
/// \returns A const-qualified reference to the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& front() const {
|
||||
return *_table[_root].value;
|
||||
}
|
||||
@@ -152,6 +296,10 @@ public:
|
||||
///
|
||||
/// \brief Access Back Element
|
||||
/// \returns A reference to the last element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& back() {
|
||||
return *_table[_last].value;
|
||||
}
|
||||
@@ -159,20 +307,33 @@ public:
|
||||
///
|
||||
/// \brief Const Access Back Element
|
||||
/// \returns A const-qualified reference to the last element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& back() const {
|
||||
return *_table[_last].value;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Insertions & Deletions ==============================================================================================
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param it Location to insert at
|
||||
/// \param x value to copy
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \details \f$O(1)\f$
|
||||
constexpr size_t insert(const iterator& it, const value_t& x) {
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(const iterator& it, const value_t& x) {
|
||||
return this->_insert(it._n, x);
|
||||
}
|
||||
|
||||
@@ -180,9 +341,12 @@ public:
|
||||
/// \brief Move Insertion
|
||||
/// \param it Location to insert at
|
||||
/// \param x value to move
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \details \f$O(1)\f$
|
||||
constexpr size_t insert(const iterator& it, value_t&& x) {
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(const iterator& it, value_t&& x) {
|
||||
return this->_insert(it._n, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
@@ -190,9 +354,12 @@ public:
|
||||
/// \brief Copy Insertion
|
||||
/// \param i Index to insert at
|
||||
/// \param x value to copy
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr size_t insert(size_t i, const value_t& x) {
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr iterator insert(size_t i, const value_t& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
@@ -203,9 +370,12 @@ public:
|
||||
/// \brief Move Insertion
|
||||
/// \param i Index to insert at
|
||||
/// \param x value to move
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr size_t insert(size_t i, value_t&& x) {
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr iterator insert(size_t i, value_t&& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
@@ -217,27 +387,55 @@ public:
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param i Index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(size_t i, ArgsT&&...args) {
|
||||
constexpr iterator emplace(size_t i, ArgsT&&...args) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param it Location to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace(const iterator& it, ArgsT&&...args) {
|
||||
return this->_insert(it._n, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Copy
|
||||
/// \param x Value to copy
|
||||
constexpr size_t push_front(const value_t& x) {
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_front(const value_t& x) {
|
||||
return this->_insert(_root, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Move
|
||||
/// \param x Value to move
|
||||
constexpr size_t push_front(value_t&& x) {
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_front(value_t&& x) {
|
||||
return this->_insert(_root, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
@@ -245,22 +443,37 @@ public:
|
||||
/// \brief Emplace Front
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace_front(ArgsT&&...args) {
|
||||
constexpr iterator emplace_front(ArgsT&&...args) {
|
||||
return this->_insert(_root, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param x Value to copy
|
||||
constexpr size_t push_back(const value_t& x) {
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_back(const value_t& x) {
|
||||
return this->_insert(npos, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param x Value to move
|
||||
constexpr size_t push_back(value_t&& x) {
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator push_back(value_t&& x) {
|
||||
return this->_insert(npos, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
@@ -268,14 +481,23 @@ public:
|
||||
/// \brief Emplace Back
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \returns The id of the inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace_back(ArgsT&&...args) {
|
||||
constexpr iterator emplace_back(ArgsT&&...args) {
|
||||
return this->_insert(npos, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase Element
|
||||
/// \param i Index to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void erase(size_t i) {
|
||||
assert(i < size(), "Index out of Bounds!");
|
||||
size_t n = _walk(i);
|
||||
@@ -285,59 +507,156 @@ public:
|
||||
///
|
||||
/// \brief Erase Element
|
||||
/// \param it Location to Erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(const iterator& it) {
|
||||
_erase(it._n);
|
||||
this->_erase(it._n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pop Front, erases first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void pop_front() {
|
||||
_erase(_root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pop Back, erases first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void pop_back() {
|
||||
_erase(_last);
|
||||
}
|
||||
|
||||
// ITERATOR ============================================================================================================
|
||||
///
|
||||
/// \brief Clears the list, destructing all elements in order
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
size_t i = _root;
|
||||
while (i != npos) {
|
||||
_table[i].value = nullopt;
|
||||
i = this->_next(i);
|
||||
}
|
||||
_table.clear();
|
||||
_root = npos;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Iterator Class
|
||||
/// \returns An iterator for the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator begin() {
|
||||
return iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns A const iterator for the first element in the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_iterator begin() const {
|
||||
return const_iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator for the end of the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const C++ Iterator Specification \f$end()\f$
|
||||
/// \returns A const iterator for the end of the list
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_iterator end() const {
|
||||
return const_iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief destructor
|
||||
~iterator() {
|
||||
_list = nullptr;
|
||||
}
|
||||
|
||||
// prefix operator
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param rhs the iterator to increment
|
||||
/// \returns \f$rhs\f$ after having moved to the next element in the list
|
||||
constexpr friend iterator& operator++(iterator& rhs) {
|
||||
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
|
||||
return rhs;
|
||||
}
|
||||
rhs._n = npos;
|
||||
rhs._n = rhs._list->_next(rhs._n);
|
||||
return rhs;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param lhs the iterator to increment
|
||||
/// \returns \f$lhs\f$ before having moved to the next element in the list
|
||||
constexpr friend iterator operator++(iterator& lhs, int) {
|
||||
iterator prev = lhs;
|
||||
++lhs;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
constexpr value_t& operator*() {
|
||||
return *(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
constexpr value_t* operator->() {
|
||||
return &*(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const iterator& it) {
|
||||
return _list == it._list and _n == it._n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const iterator& it) {
|
||||
return _list != it._list or _n != it._n;
|
||||
}
|
||||
@@ -357,11 +676,16 @@ public:
|
||||
/// \brief Iterator Class for Const Access
|
||||
class const_iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief destructor
|
||||
~const_iterator() {
|
||||
_list = nullptr;
|
||||
}
|
||||
|
||||
// prefix operator
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param rhs the iterator to increment
|
||||
/// \returns \f$rhs\f$ after having moved to the next element in the list
|
||||
constexpr friend const_iterator& operator++(const_iterator& rhs) {
|
||||
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
|
||||
return rhs;
|
||||
@@ -370,24 +694,42 @@ public:
|
||||
return rhs;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param lhs the iterator to increment
|
||||
/// \returns \f$lhs\f$ before having moved to the next element in the list
|
||||
constexpr friend const_iterator operator++(const_iterator& lhs, int) {
|
||||
const_iterator prev = lhs;
|
||||
++lhs;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
constexpr const value_t& operator*() {
|
||||
return *(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
constexpr const value_t* operator->() {
|
||||
return &*(_list->_table[_n].value);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const const_iterator& it) {
|
||||
return _list == it._list and _n == it._n;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const const_iterator& it) {
|
||||
return _list != it._list or _n != it._n;
|
||||
}
|
||||
@@ -403,39 +745,21 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \returns An iterator for the first element in the list
|
||||
constexpr iterator begin() {
|
||||
return iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator for the end of the list
|
||||
constexpr iterator end() {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const iterator for the first element in the list
|
||||
constexpr const_iterator begin() const {
|
||||
return const_iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const iterator for the end of the list
|
||||
constexpr const_iterator end() const {
|
||||
return const_iterator(this, npos);
|
||||
}
|
||||
/// @}
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
allocation<elem_t, alloc_t> _table;
|
||||
dynarray<size_t> _freed;
|
||||
table_t _table;
|
||||
freed_t _freed;
|
||||
size_t _root, _last, _size;
|
||||
|
||||
friend class iterator;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
constexpr void _expand() {
|
||||
_table.creallocate(fennec::max(_table.capacity(), size_t(1)) * 2);
|
||||
_table.resize(fennec::max(_table.size(), size_t(4)) * 2);
|
||||
}
|
||||
|
||||
struct node {
|
||||
@@ -448,18 +772,6 @@ private:
|
||||
, next(npos) {
|
||||
}
|
||||
|
||||
constexpr node(size_t p, size_t n)
|
||||
: value()
|
||||
, prev(p)
|
||||
, next(n) {
|
||||
}
|
||||
|
||||
constexpr node(size_t p, size_t n, value_t&& val)
|
||||
: value(fennec::forward<value_t>(val))
|
||||
, prev(p)
|
||||
, next(n) {
|
||||
}
|
||||
|
||||
constexpr ~node() = default;
|
||||
|
||||
constexpr void clear() {
|
||||
@@ -487,7 +799,7 @@ private:
|
||||
}
|
||||
|
||||
constexpr size_t _next_free() {
|
||||
if (not _freed.empty()) {
|
||||
if (not _freed.is_empty()) {
|
||||
size_t n = _freed.back();
|
||||
_freed.pop_back();
|
||||
return n;
|
||||
@@ -496,20 +808,20 @@ private:
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t _insert(size_t n, ArgsT&&...args) {
|
||||
constexpr iterator _insert(size_t n, ArgsT&&...args) {
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t i = _next_free();
|
||||
++_size;
|
||||
fennec::construct(&_table[i].value, fennec::forward<ArgsT>(args)...);
|
||||
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
|
||||
|
||||
if (_root == npos) {
|
||||
_table[i].prev = npos;
|
||||
_table[i].next = npos;
|
||||
_root = _last = i;
|
||||
return i;
|
||||
return iterator(this, i);
|
||||
}
|
||||
|
||||
if (n == npos) {
|
||||
@@ -517,20 +829,20 @@ private:
|
||||
_table[i].prev = _last;
|
||||
_table[i].next = npos;
|
||||
_last = i;
|
||||
return i;
|
||||
return iterator(this, i);
|
||||
}
|
||||
|
||||
_table[i].prev = _prev(n);
|
||||
_table[i].next = n;
|
||||
_table[n].prev = i;
|
||||
_root = n == _root ? i : _root;
|
||||
return i;
|
||||
return iterator(this, i);
|
||||
}
|
||||
|
||||
constexpr void _erase(size_t n) {
|
||||
if (n == npos) return;
|
||||
|
||||
fennec::destruct(&_table[n].value);
|
||||
_table[n].value = nullopt;
|
||||
_freed.push_back(n);
|
||||
--_size;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/map.h
|
||||
/// \brief A header containing the definition for a mapping of keys to values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_MAP_H
|
||||
#define FENNEC_CONTAINERS_MAP_H
|
||||
|
||||
@@ -44,7 +56,25 @@ namespace fennec
|
||||
*/
|
||||
|
||||
///
|
||||
/// \brief Map for Pairing Values to Keys
|
||||
/// \brief Data Structure defining a mapping of \f$key\f$ \f$KeyT\f$ to \f$value\f$ \f$ValueT\f$
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \note These runtimes are amortized, in theory the worst case is \f$O(N)\f$, but that is highly improbable.
|
||||
///
|
||||
/// \tparam KeyT The Key Type
|
||||
/// \tparam ValueT The Value Type
|
||||
/// \tparam Hash The Hash to Use
|
||||
@@ -54,32 +84,53 @@ struct map {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
struct key_hash;
|
||||
struct node_equals;
|
||||
using key_t = KeyT;
|
||||
using value_t = ValueT;
|
||||
using elem_t = pair<KeyT, ValueT>;
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>;
|
||||
using hash_t = Hash;
|
||||
using set_t = set<elem_t, key_hash, node_equals, alloc_t>;
|
||||
using iterator = set_t::iterator;
|
||||
|
||||
// We only want to hash the key
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
struct key_hash; //!< Hash for node keys
|
||||
struct key_equals; //!< Comparison for node keys
|
||||
using key_t = KeyT; //!< The key type
|
||||
using value_t = ValueT; //!< The value type
|
||||
using elem_t = pair<KeyT, ValueT>; //!< then node type
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>; //!< Rebinds the allocator type to nodes
|
||||
using hash_t = Hash; //!< The hash type
|
||||
using set_t = set<elem_t, key_hash, key_equals, alloc_t>; //!< The underlying set
|
||||
using iterator = set_t::iterator; //!< Iterator type
|
||||
|
||||
///
|
||||
/// \brief key hash helper
|
||||
struct key_hash : hash_t {
|
||||
///
|
||||
/// \brief C++ 11 Hash Specification \f$operator()\f$
|
||||
/// \param p the pair to hash
|
||||
/// \returns the hash of the key
|
||||
constexpr size_t operator()(const elem_t& p) const {
|
||||
return hash_t::operator()(p.first);
|
||||
}
|
||||
};
|
||||
|
||||
// We only want to compare the keys
|
||||
struct node_equals : equality<KeyT> {
|
||||
///
|
||||
/// \brief key comparison helper
|
||||
struct key_equals : equality<KeyT> {
|
||||
///
|
||||
/// \brief C++ 11 Compare Specification \f$operator()\f$
|
||||
/// \param a the first pair
|
||||
/// \param b the second pair
|
||||
/// \returns \f$true\f$ if the keys are equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const elem_t& a, const elem_t& b) const {
|
||||
return equality<KeyT>::operator()(a.first, b.first);
|
||||
}
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty map
|
||||
@@ -89,13 +140,50 @@ public:
|
||||
/// \brief Destructor, Destructs all elements and releases the allocation
|
||||
constexpr ~map() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The size of the set
|
||||
constexpr size_t size() const {
|
||||
return _set.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no elements in the set, \f$false\f$ otherwise
|
||||
constexpr size_t is_empty() const {
|
||||
return _set.size();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
constexpr size_t capacity() const {
|
||||
return _set.capacity();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Key Access Operator
|
||||
/// \param key Key value to access
|
||||
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
/// \returns A pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t* operator[](const KeyT& key) {
|
||||
auto it = _set.at(this->_find(key));
|
||||
return it ? &it->second : nullptr;
|
||||
@@ -104,7 +192,11 @@ public:
|
||||
///
|
||||
/// \brief Key Const Access Operator
|
||||
/// \param key Key value to access
|
||||
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
/// \returns A const-qualified pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t* operator[](const KeyT& key) const {
|
||||
auto it = _set.at(this->_find(key));
|
||||
return it ? &it->second : nullptr;
|
||||
@@ -112,9 +204,13 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Argument Key Access Operator
|
||||
/// \tparam ArgT Argument Type
|
||||
/// \param arg Argument to construct the key with
|
||||
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct the key with
|
||||
/// \returns A pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr value_t* operator[](ArgsT&&...args) {
|
||||
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
||||
@@ -125,26 +221,45 @@ public:
|
||||
/// \brief Argument Key Const Access Operator
|
||||
/// \tparam ArgsT Argument Type
|
||||
/// \param args Argument to construct the key with
|
||||
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
/// \returns A const-qualified pointer to the value associated with \f$key\f$, \f$nullptr\f$ if \f$key\f$ is not present.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr const value_t* operator[](ArgsT&&...args) const {
|
||||
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
||||
return it ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param pair a pair containing the key and its value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void insert(elem_t&& pair) {
|
||||
this->_insert(fennec::forward<elem_t>(pair));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param key key to insert
|
||||
/// \param args Arguments for constructing the key-value pair
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(const KeyT& key, ArgsT&&...args) {
|
||||
this->_insert(key, fennec::forward<ArgsT>(args)...);
|
||||
@@ -153,6 +268,10 @@ public:
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param args Arguments for constructing the key-value pair
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
@@ -161,6 +280,10 @@ public:
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(KeyT&& key) {
|
||||
_set.erase(this->_find(fennec::forward<KeyT>(key)));
|
||||
}
|
||||
@@ -168,6 +291,10 @@ public:
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(const KeyT& key) {
|
||||
_set.erase(this->_find(key));
|
||||
}
|
||||
@@ -176,28 +303,69 @@ public:
|
||||
/// \brief Argument Erase
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct a key to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void erase(ArgsT&&...args) {
|
||||
_set.erase(this->_find(fennec::forward<ArgsT>(args)...));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clears the map destructing all elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void clear() {
|
||||
_set.clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns an iterator at the start of the map
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator begin() {
|
||||
return _set.begin();
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns an iterator at the end of the map
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() {
|
||||
return _set.end();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
set_t _set;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
template<typename...ArgsT>
|
||||
set_t::iterator _find(ArgsT&&...args) {
|
||||
set_t::iterator _find(ArgsT&&...args) const {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
pair<KeyT, ValueT> val;
|
||||
@@ -205,7 +373,9 @@ private:
|
||||
~U() {
|
||||
fennec::destruct(&root);
|
||||
}
|
||||
} trick = { .root = { KeyT(fennec::forward<ArgsT>(args)...), 0 } };
|
||||
} trick = {
|
||||
.root = { KeyT(fennec::forward<ArgsT>(args)...), 0 }
|
||||
};
|
||||
return _set.find(trick.val);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,533 +0,0 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_SET_H
|
||||
#define FENNEC_CONTAINERS_SET_H
|
||||
|
||||
// https://programming.guide/robin-hood-hashing.html
|
||||
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/set.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/math/ext/primes.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
#include <fennec/lang/hashing.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for sets of elements
|
||||
/// \details
|
||||
/// This data-structure behaves like a set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:----------|:----------:|
|
||||
/// | stable | \emoji x |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
|
||||
struct set {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using hash_t = Hash;
|
||||
using equal_t = Equals;
|
||||
using elem_t = TypeT;
|
||||
|
||||
class iterator;
|
||||
class value_iterator;
|
||||
static constexpr size_t npos = -1;
|
||||
static constexpr double default_load = 0.8;
|
||||
|
||||
private:
|
||||
struct node {
|
||||
optional<elem_t> value;
|
||||
int psl;
|
||||
|
||||
constexpr node() = default;
|
||||
constexpr ~node() = default;
|
||||
};
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty set
|
||||
constexpr set()
|
||||
: _alloc()
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Constructor, initializes empty set with a hash
|
||||
constexpr set(const hash_t& hash)
|
||||
: _alloc()
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Constructor, initializes empty set with a hash
|
||||
constexpr set(hash_t&& hash) noexcept
|
||||
: _alloc()
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
|
||||
constexpr set(const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Move Constructor, initializes empty set with an allocator
|
||||
constexpr set(alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(const hash_t& hash, const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Alloc Move Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Alloc Move Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Set Copy Constructor
|
||||
/// \param set Set to copy
|
||||
constexpr set(const set& set)
|
||||
: _alloc(set._alloc)
|
||||
, _hash(set._hash)
|
||||
, _size(set._size)
|
||||
, _sumpsl(set._sumpsl)
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Set Move Constructor
|
||||
/// \param set Set to move
|
||||
constexpr set(set&& set) noexcept
|
||||
: _alloc(fennec::move(set._alloc))
|
||||
, _hash(fennec::move(set._hash))
|
||||
, _size(fennec::move(set._size))
|
||||
, _sumpsl(set._sumpsl)
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, destructs all elements and releases the allocation
|
||||
constexpr ~set() {
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
_alloc[i].value = nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \returns Size of the set in elements
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Capacity of the set in elements
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Find an Element
|
||||
/// \param val Value to find
|
||||
/// \returns An iterator at the location of the first instance of `value`
|
||||
constexpr iterator find(const elem_t& val, size_t c = 0) const {
|
||||
if (capacity() == 0) {
|
||||
return end();
|
||||
}
|
||||
size_t s = _hash(val) % capacity(); // Initial search index
|
||||
int psl = (_size != 0) ? _sumpsl / _size : 0; // Initial psl
|
||||
size_t i = (s + psl) % capacity(); // Median search
|
||||
size_t n = 0;
|
||||
|
||||
// Check the first element;
|
||||
if (_alloc[i].psl >= psl && _alloc[i].value) {
|
||||
if (*_alloc[i].value == val) {
|
||||
return iterator(this, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Loop while there is a value and its psl is greater than our probe
|
||||
while (c > 0) {
|
||||
++n;
|
||||
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
|
||||
size_t i1 = (i + n) % capacity();
|
||||
int p0 = psl - n, p1 = psl + n;
|
||||
bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
|
||||
|
||||
if (c0 && _alloc[i0].value) {
|
||||
if (*_alloc[i0].value == val) {
|
||||
if (c-- == 0) {
|
||||
return iterator(this, i0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (c1 && _alloc[i1].value) {
|
||||
if (*_alloc[i1].value == val) {
|
||||
if (c-- == 0) {
|
||||
return iterator(this, i1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (not(c0 or c1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Check if a set contains a value
|
||||
/// \param val Value to check
|
||||
/// \returns `true` if `val` can be found, `false` otherwise
|
||||
constexpr bool contains(const elem_t& val) const {
|
||||
return this->find(val) != end();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Access
|
||||
/// \param it Location to access
|
||||
/// \returns A pointer to the element, `nullptr` if not found.
|
||||
/// The value should not be changed in a manner that will change the hash of the element.
|
||||
constexpr elem_t* at(const iterator& it) {
|
||||
if (it == end()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (not _alloc[it._i].value) {
|
||||
return nullptr;
|
||||
}
|
||||
return &*_alloc[it._i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Const Access
|
||||
/// \param it Location to access
|
||||
/// \returns A const-qualified pointer to the element, `nullptr` if not found.
|
||||
constexpr const elem_t* at(const iterator& it) const {
|
||||
if (not _alloc[it._i].value) return nullptr;
|
||||
return &*_alloc[it._i].value;
|
||||
}
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param val Value to insert
|
||||
constexpr void insert(elem_t&& val) {
|
||||
this->_insert(fennec::forward<elem_t>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param val Value to insert
|
||||
constexpr void insert(const elem_t& val) {
|
||||
this->_insert(val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments to construct with
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param it Location to erase
|
||||
constexpr void erase(iterator it) {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) {
|
||||
return;
|
||||
} // These are separated due to compilers being inconsistent
|
||||
if (not _alloc[i].value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_alloc[i].value = nullopt;
|
||||
_sumpsl -= _alloc[i].psl;
|
||||
--_size;
|
||||
size_t p = i;
|
||||
while (_alloc[i = (i + 1) % capacity()].value) {
|
||||
if (_alloc[i].psl == 0) break;
|
||||
|
||||
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
|
||||
--_alloc[p].psl, --_sumpsl;
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param val Value to erase
|
||||
constexpr void erase(const elem_t& val) {
|
||||
this->erase(this->find(val));
|
||||
}
|
||||
|
||||
|
||||
// ITERATOR ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Class for Iterating the Set
|
||||
class iterator {
|
||||
public:
|
||||
constexpr ~iterator() {
|
||||
_set = nullptr;
|
||||
}
|
||||
|
||||
// prefix operator
|
||||
constexpr iterator& operator++() {
|
||||
while (++_i < _set->capacity()) {
|
||||
if (_set->_alloc[_i].value) {
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
_i = npos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr iterator operator++(int) {
|
||||
iterator prev = *this;
|
||||
this->operator++();
|
||||
return prev;
|
||||
}
|
||||
|
||||
constexpr const elem_t& operator*() const {
|
||||
return *_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr const elem_t* operator->() const {
|
||||
if (not _set->_alloc[_i].value) return nullptr;
|
||||
return &*_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const iterator& it) const {
|
||||
return _set == it._set and _i == it._i;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const iterator& it) const {
|
||||
return _set != it._set or _i != it._i;
|
||||
}
|
||||
|
||||
private:
|
||||
const set* _set;
|
||||
size_t _i;
|
||||
friend set;
|
||||
|
||||
constexpr iterator(const set* set, size_t i)
|
||||
: _set(set)
|
||||
, _i(i) {
|
||||
}
|
||||
};
|
||||
|
||||
class value_iterator {
|
||||
public:
|
||||
constexpr ~value_iterator() {
|
||||
_set = nullptr;
|
||||
}
|
||||
|
||||
// prefix operator
|
||||
constexpr value_iterator& operator++() {
|
||||
while (_psl <= _set->_alloc[_i].psl) {
|
||||
if (not _set->_alloc[_i].value) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
_i = npos;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr value_iterator operator++(int) {
|
||||
value_iterator prev = *this;
|
||||
this->operator++();
|
||||
return prev;
|
||||
}
|
||||
|
||||
constexpr const elem_t& operator*() const {
|
||||
return *_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr const elem_t* operator->() const {
|
||||
if (not _set->_alloc[_i].value) return nullptr;
|
||||
return &*_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const iterator& it) const {
|
||||
return _set == it._set and _i == it._i;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const iterator& it) const {
|
||||
return _set != it._set or _i != it._i;
|
||||
}
|
||||
|
||||
private:
|
||||
const set* _set;
|
||||
size_t _i;
|
||||
int _psl;
|
||||
elem_t _value;
|
||||
friend set;
|
||||
|
||||
constexpr value_iterator(const set* set, size_t i, int psl, const elem_t& value)
|
||||
: _set(set)
|
||||
, _i(i)
|
||||
, _value(value) {
|
||||
}
|
||||
};
|
||||
|
||||
constexpr iterator begin() const {
|
||||
iterator it(this, 0);
|
||||
if (not _alloc[it._i].value) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
constexpr iterator end() const {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
|
||||
// PRIVATE =============================================================================================================
|
||||
|
||||
private:
|
||||
constexpr void _expand() {
|
||||
set cpy; // Create a new set
|
||||
cpy._alloc.callocate(
|
||||
fennec::next_prime2(_alloc.capacity())
|
||||
);
|
||||
|
||||
// rehash
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
if (_alloc[i].value) {
|
||||
cpy.insert(fennec::move(*_alloc[i].value));
|
||||
}
|
||||
}
|
||||
|
||||
// Swap buffers
|
||||
fennec::swap(_alloc, cpy._alloc);
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr void _insert(ArgsT&&...args) {
|
||||
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
|
||||
_expand();
|
||||
}
|
||||
|
||||
elem_t value(fennec::forward<ArgsT>(args)...);
|
||||
size_t i = _hash(value) % capacity(); // Initial search index
|
||||
int psl = 0;
|
||||
while (_alloc[i].value) { // Search for empty cell
|
||||
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
|
||||
return;
|
||||
}
|
||||
if (psl > _alloc[i].psl) { // When psl is higher, swap
|
||||
_sumpsl += psl - _alloc[i].psl;
|
||||
fennec::swap(_alloc[i].psl, psl);
|
||||
fennec::swap(*_alloc[i].value, value);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
_alloc[i].value = fennec::move(value);
|
||||
_sumpsl += (_alloc[i].psl = psl);
|
||||
++_size;
|
||||
}
|
||||
|
||||
allocation<node, alloc_t> _alloc;
|
||||
hash_t _hash;
|
||||
equal_t _equal;
|
||||
size_t _size;
|
||||
size_t _sumpsl;
|
||||
double _load;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_SET_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,8 +16,21 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/object_pool.h
|
||||
/// \brief A header containing the definition for a pool of objects associated by ids
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||
#define FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
@@ -27,82 +40,485 @@ namespace fennec
|
||||
|
||||
///
|
||||
/// \brief Struct which holds a pool of objects associated with ids
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The value type
|
||||
/// \tparam AllocT The allocator type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct object_pool {
|
||||
public:
|
||||
using value_t = TypeT;
|
||||
using elem_t = optional<TypeT>;
|
||||
using table_t = dynarray<elem_t, AllocT>;
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< The held object type
|
||||
using elem_t = optional<value_t>; //!< The element type
|
||||
using table_t = dynarray<elem_t, AllocT>; //!< The underlying table type
|
||||
using freed_t = list<size_t, AllocT>; //!< The underlying free queue
|
||||
|
||||
/// @}
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr object_pool()
|
||||
: _size(0) {
|
||||
};
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs objects then releases the allocation.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~object_pool() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of active objects in the pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no objects in the pool, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Retrieve the next id \f$i\f$ that would be assigned to an object \f$o\f$ were it added to the object pool
|
||||
///
|
||||
/// \details This can be useful if there are constant members that need to be assigned at construction.
|
||||
/// \returns The id of the next inserted node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t next_id() const {
|
||||
size_t next = _size;
|
||||
if (not _freed.is_empty()) {
|
||||
next = _freed.front();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i id of the object
|
||||
/// \returns a reference to the object with id \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& operator[](size_t i) {
|
||||
assert(i < capacity(), "Index out of Bounds!");
|
||||
assert(_table[i], "Attempted to access Null Object.");
|
||||
assert(_table[i], "Attempted to access null object.")
|
||||
return *_table[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Const Access Operator
|
||||
/// \param i id of the object
|
||||
/// \returns a const-qualified reference to the object with id \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& operator[](size_t i) const {
|
||||
assert(i < capacity(), "Index out of Bounds!");
|
||||
assert(_table[i], "Attempted to access Null Object.");
|
||||
assert(_table[i], "Attempted to access null object.")
|
||||
return *_table[i];
|
||||
}
|
||||
|
||||
constexpr bool operator()(size_t i) const {
|
||||
assert(i < capacity(), "Index out of Bounds!");
|
||||
return _table[i];
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, inserts \f$x\f$ into the pool
|
||||
/// \param x the object to move
|
||||
/// \returns An integer corresponding to the id of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(value_t&& x) {
|
||||
return this->_insert(fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, inserts a copy of \f$x\f$ into the pool
|
||||
/// \param x the object to copy
|
||||
/// \returns An integer corresponding to the id of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(const value_t& x) {
|
||||
return this->_insert(x);
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief Emplacement, constructs a new object using \f$args\ldots\f$
|
||||
/// \param args The arguments to construct the new object with
|
||||
/// \returns An integer corresponding to the id of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(ArgsT&&...args) {
|
||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase an object from the pool
|
||||
/// \param i The id of the object
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(size_t i) {
|
||||
_table[i] = nullopt;
|
||||
_freed.push_back(i);
|
||||
--_size;
|
||||
}
|
||||
|
||||
constexpr size_t next_id() const {
|
||||
size_t next = _size;
|
||||
if (not _freed.empty()) {
|
||||
next = _freed.front();
|
||||
///
|
||||
/// \brief Clear the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
_table.clear();
|
||||
_freed.clear();
|
||||
_size = 0;
|
||||
}
|
||||
return next;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
|
||||
///
|
||||
/// \returns an iterator at the start of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
iterator begin() {
|
||||
return iterator(this, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns an iterator at the start of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const_iterator begin() const {
|
||||
return const_iterator(this, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns an iterator at the start of the end of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
iterator end() {
|
||||
return iterator(this, _size);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns an iterator at the start of the end of the object pool
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
const_iterator end() const {
|
||||
return const_iterator(this, _size);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param it the iterator to copy
|
||||
iterator(const iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param it the iterator to move
|
||||
iterator(iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief public destructor
|
||||
~iterator() = default;
|
||||
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param it the iterator to copy
|
||||
/// \returns a reference to self after having copied \f$it\f$
|
||||
iterator& operator=(const iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param it the iterator to move
|
||||
/// \returns a reference to self after having moved \f$it\f$
|
||||
iterator& operator=(iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \returns \f$it\f$ before having been incremented
|
||||
friend iterator operator++(iterator& it, int) {
|
||||
iterator ret = it;
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \returns \f$it\f$ after having moved to the next element
|
||||
friend iterator& operator++(iterator& it) {
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
value_t& operator*() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
value_t* operator->() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
bool operator==(const iterator& it) {
|
||||
return pool == it.pool and curr == it.curr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
bool operator!=(const iterator& it) {
|
||||
return pool != it.pool or curr != it.curr;
|
||||
}
|
||||
|
||||
private:
|
||||
dynarray<elem_t, AllocT> _table;
|
||||
list<size_t> _freed;
|
||||
object_pool* pool;
|
||||
size_t curr;
|
||||
|
||||
void _fix() {
|
||||
while (curr < pool->_size and not pool->_table[curr]) {
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
|
||||
iterator(object_pool* pool, size_t start)
|
||||
: pool(pool), curr(start) {
|
||||
_fix();
|
||||
}
|
||||
|
||||
friend struct object_pool;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$const_iterator\f$
|
||||
class const_iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param it the iterator to copy
|
||||
const_iterator(const const_iterator&it ) = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param it the iterator to move
|
||||
const_iterator(const_iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief public destructor
|
||||
~const_iterator() = default;
|
||||
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param it the iterator to copy
|
||||
/// \returns a reference to self after having copied \f$it\f$
|
||||
const_iterator& operator=(const const_iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param it the iterator to move
|
||||
/// \returns a reference to self after having moved \f$it\f$
|
||||
const_iterator& operator=(const_iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \returns \f$it\f$ before having been incremented
|
||||
friend const_iterator operator++(const_iterator& it, int) {
|
||||
const_iterator ret = it;
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return ret;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \returns \f$it\f$ after having moved to the next element
|
||||
friend const_iterator& operator++(const_iterator& it) {
|
||||
++it.curr;
|
||||
it._fix();
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
const value_t& operator*() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
const value_t* operator->() const {
|
||||
return *pool->_table[curr];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
bool operator==(const const_iterator& it) {
|
||||
return pool == it.pool and curr == it.curr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
bool operator!=(const const_iterator& it) {
|
||||
return pool != it.pool or curr != it.curr;
|
||||
}
|
||||
|
||||
private:
|
||||
const object_pool* pool;
|
||||
size_t curr;
|
||||
|
||||
void _fix() {
|
||||
while (curr < pool->_size and not pool->_table[curr]) {
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
|
||||
const_iterator(const object_pool* pool, size_t start)
|
||||
: pool(pool), curr(start) {
|
||||
_fix();
|
||||
}
|
||||
|
||||
friend struct object_pool;
|
||||
};
|
||||
|
||||
private:
|
||||
table_t _table;
|
||||
freed_t _freed;
|
||||
size_t _size;
|
||||
|
||||
size_t _next_free() {
|
||||
size_t next = _size;
|
||||
if (not _freed.empty()) {
|
||||
if (not _freed.is_empty()) {
|
||||
next = _freed.front();
|
||||
_freed.pop_front();
|
||||
}
|
||||
@@ -114,7 +530,7 @@ private:
|
||||
size_t _insert(ArgsT&&...args) {
|
||||
size_t i = _next_free();
|
||||
if (i >= _table.size()) {
|
||||
_table.emplace_back();
|
||||
_table.resize(fennec::max(_table.size() * 2, size_t(8)));
|
||||
}
|
||||
_table[i].emplace(fennec::forward<ArgsT>(args)...);
|
||||
return i;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/optional.h
|
||||
/// \brief A header containing the definition for a container with an optionally present variable
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_OPTIONAL_H
|
||||
#define FENNEC_CONTAINERS_OPTIONAL_H
|
||||
|
||||
@@ -26,30 +38,65 @@
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief struct to represent a \f$null\f$ `fennec::optional`
|
||||
struct nullopt_t {};
|
||||
|
||||
///
|
||||
/// \brief value representing a \f$null\f$ `fennec::optional`
|
||||
constexpr nullopt_t nullopt_v = {};
|
||||
|
||||
///
|
||||
/// \brief alias for representing a \f$null\f$ `fennec::optional`
|
||||
#define nullopt nullopt_v
|
||||
|
||||
///
|
||||
/// \brief Structure to hold an optional value.
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam T
|
||||
template<typename T>
|
||||
struct optional {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
using reference_t = T&;
|
||||
using pointer_t = T*;
|
||||
using const_reference_t = T&;
|
||||
using const_pointer_t = const T*;
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using reference_t = T&; //!< reference type
|
||||
using pointer_t = T*; //!< pointer type
|
||||
using const_reference_t = T&; //!< const reference type
|
||||
using const_pointer_t = const T*; //!< const pointer type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional()
|
||||
: _root(0)
|
||||
, _set(false) {
|
||||
@@ -57,6 +104,10 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(nullopt_t)
|
||||
: _root(0)
|
||||
, _set(false) {
|
||||
@@ -65,6 +116,10 @@ public:
|
||||
///
|
||||
/// \brief Type Copy Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(const T& val)
|
||||
: _val(val)
|
||||
, _set(true) {
|
||||
@@ -73,6 +128,10 @@ public:
|
||||
///
|
||||
/// \brief Type Move Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(T&& val)
|
||||
: _val(fennec::forward<T>(val))
|
||||
, _set(true) {
|
||||
@@ -81,6 +140,10 @@ public:
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param opt the optional to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(const optional& opt) requires is_copy_assignable_v<T>
|
||||
: optional() {
|
||||
_set = opt._set;
|
||||
@@ -92,6 +155,10 @@ public:
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
/// \param opt the optional to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional(optional&& opt) noexcept requires is_move_assignable_v<T>
|
||||
: optional() {
|
||||
_set = opt._set;
|
||||
@@ -101,12 +168,26 @@ public:
|
||||
opt = nullopt;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr optional(ArgsT&&...args)
|
||||
: _val(fennec::forward<ArgsT>(args)...)
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr ~optional() {
|
||||
if constexpr(is_fundamental_v<T>) {
|
||||
return;
|
||||
@@ -116,12 +197,50 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Implicit Boolean Check
|
||||
/// \returns \f$true\f$ when there is a value contained
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr operator bool() const {
|
||||
return _set;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there is no held value, \f$false\f$ otherwise.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return not _set;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment Operators ================================================================================================
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Null Assignment
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \brief Fundamental Type Assignment
|
||||
/// \param val The value to set with
|
||||
constexpr optional& operator=(nullopt_t) {
|
||||
if constexpr(not is_fundamental_v<T>) {
|
||||
if (_set) {
|
||||
@@ -136,6 +255,11 @@ public:
|
||||
///
|
||||
/// \brief Type Copy Assignment
|
||||
/// \param val The value to set with
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(const T& val) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||
if (_set) {
|
||||
_val = val;
|
||||
@@ -149,6 +273,11 @@ public:
|
||||
///
|
||||
/// \brief Type Move Assignment
|
||||
/// \param val The value to set with
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(T&& val) requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||
if (_set) {
|
||||
_val = fennec::forward<T>(val);
|
||||
@@ -162,6 +291,11 @@ public:
|
||||
///
|
||||
/// \brief Copy Assignment
|
||||
/// \param opt The optional to copy
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(const optional& opt) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||
if (_set != opt._set) {
|
||||
_set = opt._set;
|
||||
@@ -180,6 +314,11 @@ public:
|
||||
///
|
||||
/// \brief Move Assignment
|
||||
/// \param opt The optional to move
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr optional& operator=(optional&& opt) noexcept requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||
if (_set != opt._set) {
|
||||
_set = opt._set;
|
||||
@@ -195,9 +334,133 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns A pointer to the value, \f$nullptr\f$ if there is no value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pointer_t operator->() noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified pointer to the value, \f$nullptr\f$ if there is no value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_pointer_t operator->() const noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T& operator*() & noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const T& operator*() const& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T&& operator*() && noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const T&& operator*() const&& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment, Move overload
|
||||
/// \param val The object to take ownership of
|
||||
/// \returns A reference to the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T& emplace(T&& val) {
|
||||
if (_set) {
|
||||
_val = fennec::forward<T>(val);
|
||||
} else {
|
||||
fennec::construct(&_val, fennec::forward<T>(val));
|
||||
_set = true;
|
||||
}
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment, Copy overload
|
||||
/// \param val The object to copy
|
||||
/// \returns A reference to the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr T& emplace(const T& val) {
|
||||
if (_set) {
|
||||
_val = val;
|
||||
} else {
|
||||
fennec::construct(&_val, val);
|
||||
_set = true;
|
||||
}
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment
|
||||
/// \val The optional to move
|
||||
/// \param args The arguments to construct with
|
||||
/// \returns A reference to the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr T& emplace(ArgsT&&...args) {
|
||||
if (_set) {
|
||||
@@ -211,62 +474,15 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Reset the Optional
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
void reset() {
|
||||
this->operator=(nullopt);
|
||||
}
|
||||
|
||||
|
||||
// Operators ===========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Implicit Boolean Check, returns `true` when there is a value contained
|
||||
constexpr operator bool() const {
|
||||
return _set;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A pointer to the value, `nullptr` if there is no value
|
||||
constexpr pointer_t operator->() noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified pointer to the value, `nullptr` if there is no value
|
||||
constexpr const_pointer_t operator->() const noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
constexpr T& operator*() & noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
constexpr const T& operator*() const& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
constexpr T&& operator*() && noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
constexpr const T&& operator*() const&& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/pair.h
|
||||
/// \brief A header containing the definition for a container holding a pair of values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_PAIR_H
|
||||
#define FENNEC_CONTAINERS_PAIR_H
|
||||
|
||||
@@ -30,26 +42,70 @@ namespace fennec
|
||||
|
||||
///
|
||||
/// \brief Struct for holding a pair of values
|
||||
/// \tparam T0 The type of the first value
|
||||
/// \tparam T1 The type of the second value
|
||||
template<typename T0, typename T1>
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam TypeT0 The type of the first value
|
||||
/// \tparam TypeT1 The type of the second value
|
||||
template<typename TypeT0, typename TypeT1>
|
||||
struct pair {
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Member Variables
|
||||
/// @{
|
||||
|
||||
TypeT0 first; //!< The first value in the pair
|
||||
TypeT1 second; //!< The second value in the pair
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, invokes default constructor for both elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor, invokes destructor for both elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr ~pair() = default;
|
||||
|
||||
///
|
||||
/// \brief Pair Copy Constructor
|
||||
/// \param x Value to copy for the first element
|
||||
/// \param y Value to copy for the first element
|
||||
constexpr pair(const T0& x, const T1& y)
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(const TypeT0& x, const TypeT1& y)
|
||||
: first(x)
|
||||
, second(y) {
|
||||
}
|
||||
@@ -58,15 +114,23 @@ struct pair {
|
||||
/// \brief Pair Move Constructor
|
||||
/// \param x Value to move for the first element
|
||||
/// \param y Value to move for the first element
|
||||
constexpr pair(T0&& x, T1&& y) noexcept
|
||||
: first(fennec::forward<T0>(x))
|
||||
, second(fennec::forward<T1>(y)) {
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(TypeT0&& x, TypeT1&& y) noexcept
|
||||
: first(fennec::forward<TypeT0>(x))
|
||||
, second(fennec::forward<TypeT1>(y)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pair Implicit Constructor
|
||||
/// \param arg1 Value to initialize the first element
|
||||
/// \param arg2 Value to initialize the first element
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename Arg1T, typename Arg2T>
|
||||
constexpr pair(Arg1T&& arg1, Arg2T&& arg2)
|
||||
: first(fennec::forward<Arg1T>(arg1))
|
||||
@@ -75,27 +139,73 @@ struct pair {
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies both elements
|
||||
constexpr pair(const pair&) = default;
|
||||
/// \param pair The pair to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(const pair& pair)
|
||||
: first(fennec::copy(pair.first))
|
||||
, second(fennec::copy(pair.second)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, moves both elements
|
||||
constexpr pair(pair&&) noexcept = default;
|
||||
/// \param pair The pair to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair(pair&& pair) noexcept
|
||||
: first(fennec::move(pair.first))
|
||||
, second(fennec::move(pair.second)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment, copies both elements
|
||||
constexpr pair& operator=(const pair&) = default;
|
||||
/// \param pair The pair to copy
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair& operator=(const pair& pair) {
|
||||
first = fennec::copy(pair.first);
|
||||
second = fennec::copy(pair.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment, moves both elements
|
||||
constexpr pair& operator=(pair&&) noexcept = default;
|
||||
/// \param pair The pair to move
|
||||
/// \returns A reference to self
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr pair& operator=(pair&& pair) {
|
||||
first = fennec::move(pair.first);
|
||||
second = fennec::move(pair.second);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Equality Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns `true` when both elements of each pair are equal
|
||||
/// \returns \f$true\f$ when both elements of each pair are equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator==(const pair& p) const {
|
||||
return first == p.first and second == p.second;
|
||||
}
|
||||
@@ -103,7 +213,11 @@ struct pair {
|
||||
///
|
||||
/// \brief Inequality Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns `true` when either element of each pair are equal
|
||||
/// \returns \f$true\f$ when either element of each pair are equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator!=(const pair& p) const {
|
||||
return first != p.first or second != p.second;
|
||||
}
|
||||
@@ -111,8 +225,12 @@ struct pair {
|
||||
///
|
||||
/// \brief Less Than Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is less, or they are
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is less, or they are
|
||||
/// equal and the second element is less
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator<(const pair& p) const {
|
||||
return first < p.first or (first == p.first and second < p.second);
|
||||
}
|
||||
@@ -120,8 +238,12 @@ struct pair {
|
||||
///
|
||||
/// \brief Less Equal Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is less, or they are
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is less, or they are
|
||||
/// equal and the second element is less or equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator<=(const pair& p) const {
|
||||
return first < p.first or (first == p.first and second <= p.second);
|
||||
}
|
||||
@@ -129,8 +251,12 @@ struct pair {
|
||||
///
|
||||
/// \brief Greater Than Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is greater, or they are
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is greater, or they are
|
||||
/// equal and the second element is greater
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator>(const pair& p) const {
|
||||
return first > p.first or (first == p.first and second > p.second);
|
||||
}
|
||||
@@ -138,24 +264,33 @@ struct pair {
|
||||
///
|
||||
/// \brief Greater Equal Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is greater, or they are
|
||||
/// \returns lexical comparison of both elements, i.e. returns \f$true\f$ when the first element is greater, or they are
|
||||
/// equal and the second element is greater or equal
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool operator>=(const pair& p) const {
|
||||
return first > p.first or (first == p.first and second >= p.second);
|
||||
}
|
||||
|
||||
// Members =============================================================================================================
|
||||
|
||||
T0 first;
|
||||
T1 second;
|
||||
/// @}
|
||||
};
|
||||
|
||||
template<typename T0, typename T1>
|
||||
struct hash<pair<T0, T1>> : hash<T0>, hash<T1> {
|
||||
constexpr size_t operator()(const pair<T0, T1>& p) const {
|
||||
///
|
||||
/// \brief C++ 11 Hash Specification for `fennec::pair<TypeT0, TypeT1>`
|
||||
/// \tparam TypeT0 The first type of the pair
|
||||
/// \tparam TypeT1 The second type of the pair
|
||||
template<typename TypeT0, typename TypeT1>
|
||||
struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> {
|
||||
///
|
||||
/// \brief C++ 11 Hash Specification \f$operator()\f$
|
||||
/// \param p The pair to hash
|
||||
/// \returns a pairing of the hashes of both elements of \f$p\f$ using `fennec::pair_hash`
|
||||
constexpr size_t operator()(const pair<TypeT0, TypeT1>& p) const {
|
||||
return fennec::pair_hash( // pair the hashes of both elements
|
||||
hash<T0>::operator()(p.first),
|
||||
hash<T1>::operator()(p.second)
|
||||
hash<TypeT0>::operator()(p.first),
|
||||
hash<TypeT1>::operator()(p.second)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
289
include/fennec/containers/priority_queue.h
Normal file
289
include/fennec/containers/priority_queue.h
Normal file
@@ -0,0 +1,289 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/priority_queue.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_PRIORITY_QUEUE_H
|
||||
#define FENNEC_CONTAINERS_PRIORITY_QUEUE_H
|
||||
|
||||
#include <fennec/containers/object_pool.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
// Binary heaps are just kinda busted.
|
||||
// In-array binary heaps are one of the most efficient data structures for computers
|
||||
// -> Cache Locality
|
||||
// -> log(n) runtime
|
||||
// -> No auxiliary structures or constant runtimes
|
||||
//
|
||||
// I tried just about every heap under the sun
|
||||
// -> strict fibonacci heap, got blown out of the water by std::priority_queue
|
||||
// -> fibonacci heap, got blown out of the water by std::priority_queue
|
||||
// -> binomial heap, on-par with std::set, blown out of the water by std::priority_queue
|
||||
//
|
||||
// Then I relented and fell back to ye old binary heap
|
||||
// This implementation roughly matches gcc's std::priority_queue
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief a priority queue data structure implemented using a binary heap
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ✅ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam ValueT The value type
|
||||
/// \tparam CompareT The compare type, defaults to `fennec::less`
|
||||
/// \tparam AllocT The allocator type, defaults to `fennec::allocator`
|
||||
template<typename ValueT, class CompareT = less<ValueT>, class AllocT = allocator<ValueT>>
|
||||
struct priority_queue {
|
||||
|
||||
// Definitions & Constants =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = ValueT; //!< Alias for the value type
|
||||
using compare_t = CompareT; //!< Alias for the compare type
|
||||
using alloc_t = allocation<value_t, AllocT>; //!< The underlying allocation type
|
||||
|
||||
/// @}
|
||||
|
||||
static constexpr size_t npos = -1; //!< value representing a null node
|
||||
|
||||
private:
|
||||
constexpr size_t left(size_t n) const {
|
||||
n = n * 2 + 1;
|
||||
return n >= _size ? npos : n;
|
||||
}
|
||||
|
||||
constexpr size_t right(size_t n) const {
|
||||
n = n * 2 + 2;
|
||||
return n >= _size ? npos : n;
|
||||
}
|
||||
|
||||
constexpr size_t parent(size_t n) const {
|
||||
return n == 0 ? npos : (n - 1) / 2;
|
||||
}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief default constructor
|
||||
/// \details initializes an empty queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr priority_queue()
|
||||
: _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~priority_queue() {
|
||||
while (_size > 0) {
|
||||
--_size;
|
||||
fennec::destruct(&_table[_size]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \returns the size of the queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns the capacity of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if the queue holds no elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \returns the value at the front of the queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& front() const {
|
||||
return _table[0];
|
||||
}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief push a new key into the queue
|
||||
/// \param key the key to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void push(const value_t& key) {
|
||||
this->_insert(key);
|
||||
}
|
||||
|
||||
///
|
||||
/// \param key the key to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void push(value_t&& key) {
|
||||
this->_insert(fennec::forward<value_t>(key));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief emplace a new key into the queue
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pop the element at the front of the queue
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void pop() {
|
||||
fennec::swap(_table[0], _table[--_size]);
|
||||
fennec::destruct(&_table[_size]);
|
||||
_fix_erase(0);
|
||||
}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
compare_t _compare;
|
||||
alloc_t _table;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr void _insert(ArgsT&&...args) {
|
||||
if (_size == _table.capacity()) {
|
||||
_expand();
|
||||
}
|
||||
fennec::construct(&_table[_size], fennec::forward<ArgsT>(args)...);
|
||||
_fix_insert(_size++);
|
||||
}
|
||||
|
||||
constexpr void _expand() {
|
||||
_table.reallocate((_table.capacity() + 1) * 2 - 1);
|
||||
}
|
||||
|
||||
constexpr size_t _min(size_t a, size_t b) {
|
||||
if (a == npos) { return b; }
|
||||
if (b == npos) { return a; }
|
||||
return _compare(_table[a], _table[b]) ? a : b;
|
||||
}
|
||||
|
||||
void _fix_insert(size_t n) {
|
||||
size_t p = parent(n);
|
||||
while (p != npos && _compare(_table[n], _table[p])) {
|
||||
fennec::swap(_table[n], _table[p]);
|
||||
n = p;
|
||||
p = parent(n);
|
||||
}
|
||||
}
|
||||
|
||||
void _fix_erase(size_t n) {
|
||||
size_t c = _min(left(n), right(n));
|
||||
while (n != npos && c != npos && _compare(_table[c], _table[n])) {
|
||||
fennec::swap(_table[c], _table[n]);
|
||||
n = c;
|
||||
c = _min(left(n), right(n));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif // FENNEC_CONTAINERS_PRIORITY_QUEUE_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,12 +16,26 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/rdtree.h
|
||||
/// \brief A header containing the definition for a tree with a root and directed edges
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_RDTREE_H
|
||||
#define FENNEC_CONTAINERS_RDTREE_H
|
||||
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/traversal.h>
|
||||
#include <fennec/containers/pair.h>
|
||||
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
namespace fennec
|
||||
@@ -29,27 +43,63 @@ namespace fennec
|
||||
|
||||
///
|
||||
/// \brief Rooted-Directed Tree
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(N)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT Data type
|
||||
/// \tparam AllocT Allocator Type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct rdtree {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
protected:
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using value_t = TypeT;
|
||||
using alloc_t = typename allocator_traits<AllocT>::template rebind<node>;
|
||||
static constexpr size_t root = 0;
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
protected:
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< the held value type
|
||||
using alloc_t = typename allocator_traits<AllocT>::template rebind<node>; //!< the underlying allocator type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t root = 0; //!< the id of the root node
|
||||
static constexpr size_t npos = -1; //!< the id of a null node
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
struct node {
|
||||
optional<TypeT> value;
|
||||
value_t value;
|
||||
size_t parent, child, prev, next;
|
||||
size_t depth, num_children;
|
||||
|
||||
constexpr node()
|
||||
: value()
|
||||
, parent(npos), child(npos)
|
||||
, prev(npos), next(npos)
|
||||
, depth(0), num_children(0) {
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
|
||||
: value(fennec::forward<ArgsT>(args)...)
|
||||
@@ -62,34 +112,77 @@ protected:
|
||||
child = npos;
|
||||
prev = npos;
|
||||
next = npos;
|
||||
depth = npos;
|
||||
depth = 0;
|
||||
num_children = 0;
|
||||
}
|
||||
};
|
||||
|
||||
using table_t = allocation<node, alloc_t>;
|
||||
using freed_t = list<size_t>;
|
||||
|
||||
public:
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Root Constructor, constructs the root node of the tree
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct the root with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
explicit constexpr rdtree(ArgsT&&...args)
|
||||
: _table(), _freed(), _size(1) {
|
||||
_table.callocate(8);
|
||||
_table.reallocate(8);
|
||||
fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies the contents of \f$tree\f$
|
||||
/// \param tree the rdtree to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr rdtree(const rdtree& tree)
|
||||
: _table(tree._table), _freed(tree._freed), _size(tree._size) {
|
||||
// TODO: properly invoke copy constructor for all elements
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership over the contents of \f$tree\f$
|
||||
/// \param tree the rdtree to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr rdtree(rdtree&& tree) noexcept
|
||||
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
|
||||
/// \name Assignment Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment Operator
|
||||
/// \param rhs the rdtree to copy
|
||||
/// \returns \f$this\f$ after copying the contents of \f$rhs\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr rdtree& operator=(const rdtree& rhs) {
|
||||
// TODO: properly invoke copy constructor for all elements
|
||||
for (value_t* it : this->_table) {
|
||||
fennec::destruct(it);
|
||||
}
|
||||
@@ -99,6 +192,14 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Assignment Operator
|
||||
/// \param rhs the rdtree to move
|
||||
/// \returns \f$this\f$ after taking ownership over the contents of \f$rhs\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr rdtree& operator=(rdtree&& rhs) noexcept {
|
||||
for (value_t* it : _table) {
|
||||
fennec::destruct(it);
|
||||
@@ -109,27 +210,58 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of nodes in the tree
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The capacity of the underlying allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.capacity();
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no nodes in the tree, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The id of the parent node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t parent(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
return i == npos ? npos : _table[i].parent;
|
||||
@@ -137,9 +269,14 @@ public:
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \param n The index of the child relative to the parent
|
||||
/// \returns The id of the child node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t child(size_t i, size_t n = 0) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
if (i >= _table.capacity() && n != npos) return npos;
|
||||
size_t c = i == npos ? npos : _table[i].child;
|
||||
if (n != 0)
|
||||
return next(c, n == npos ? npos : n - 1);
|
||||
@@ -148,9 +285,14 @@ public:
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \param n The index of the child relative to the parent
|
||||
/// \returns The id of the next node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t next(size_t i, size_t n = 0) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
if (i >= _table.capacity() && n != npos) return npos;
|
||||
if (i == npos) {
|
||||
return npos;
|
||||
}
|
||||
@@ -172,7 +314,12 @@ public:
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \param n The index of the child relative to the parent
|
||||
/// \returns The id of the previous node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t prev(size_t i, size_t n = 0) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
if (i == npos) {
|
||||
@@ -196,7 +343,11 @@ public:
|
||||
|
||||
///
|
||||
/// \param i the node to start at
|
||||
/// \returns the left-most child of node `i`
|
||||
/// \returns the left-most child of node \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr size_t left_most(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
size_t n = i;
|
||||
@@ -213,7 +364,11 @@ public:
|
||||
|
||||
///
|
||||
/// \param i the node to start at
|
||||
/// \returns the right-most child of node `i`
|
||||
/// \returns the right-most child of node \f$i\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr size_t right_most(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
if ((i = child(i)) == npos) {
|
||||
@@ -234,6 +389,10 @@ public:
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The depth of the node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t depth(size_t i) const {
|
||||
if (i >= _table.capacity()) return npos;
|
||||
return i == npos ? npos : _table[i].depth;
|
||||
@@ -242,16 +401,24 @@ public:
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The number of children the node has
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t num_children(size_t i) const {
|
||||
if (i >= _table.capacity()) return 0;
|
||||
return i == npos ? 0 : _table[i].num_children;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The next node id were `insert` or `emplace` to be called
|
||||
constexpr size_t next_free() const {
|
||||
/// \returns The next node id were \f$insert\f$ or \f$emplace\f$ to be called
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t next_id() const {
|
||||
size_t i = _size;
|
||||
if (not _freed.empty()) {
|
||||
if (not _freed.is_empty()) {
|
||||
i = _freed.front();
|
||||
}
|
||||
return i;
|
||||
@@ -260,56 +427,104 @@ public:
|
||||
///
|
||||
/// \param i The id of the node to access
|
||||
/// \returns A reference to the value of the node wrapped in an optional
|
||||
constexpr value_t* operator[](size_t i) {
|
||||
auto& it = _table[i].value;
|
||||
if (it) {
|
||||
return &*_table[i].value;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr value_t& operator[](size_t i) {
|
||||
return _table[i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to access
|
||||
/// \returns A const-qualified reference to the value of the node wrapped in an optional
|
||||
constexpr const value_t* operator[](size_t i) const {
|
||||
const auto& it = _table[i].value;
|
||||
if (it) {
|
||||
return &*_table[i].value;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const value_t& operator[](size_t i) const {
|
||||
return _table[i].value;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent `parent`
|
||||
/// \param parent the parent node, if `npos` sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent
|
||||
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent, i.e. parent[0] == parent.child, parent[1] == parent.child.next
|
||||
/// \param val the value to insert
|
||||
/// \returns the index of the created node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
|
||||
return this->_insert(parent, next, val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent `parent`
|
||||
/// \param parent the parent node, if `npos` sets the value of the root node
|
||||
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent
|
||||
/// \param val the value to insert
|
||||
/// \returns the index of the created node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
|
||||
return this->_insert(parent, next, fennec::forward<value_t>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent `parent`
|
||||
/// \param parent the parent node, if `npos` sets the value of the root node
|
||||
/// \brief tree insertion, copies the contents of tree into self with the root at the specified node location
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent
|
||||
/// \param tree the tree to insert
|
||||
/// \returns the index of the inserted root
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t insert(size_t parent, size_t next, const rdtree& tree) {
|
||||
list<pair<size_t, size_t>> visit;
|
||||
visit.push_front({ root, parent });
|
||||
size_t res = npos;
|
||||
|
||||
while (not visit.is_empty()) {
|
||||
auto node = visit.front();
|
||||
visit.pop_front();
|
||||
|
||||
size_t p = this->_insert(node.second, node.second == parent ? next : npos, tree[node.first]);
|
||||
|
||||
res = (res == npos) ? p : res;
|
||||
|
||||
size_t c = tree.child(node.first, npos);
|
||||
while (c != npos) {
|
||||
visit.push_front({ c, p });
|
||||
c = tree._table[c].prev;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||
/// \param parent the parent node, if \f$npos\f$ sets the value of the root node
|
||||
/// \param next the next node, as an index relative to the parent, i.e. parent[0] == parent.child, parent[1] == parent.child.next
|
||||
/// \param args the args to construct the value to insert
|
||||
/// \returns the index of the created node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
|
||||
return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
|
||||
@@ -319,6 +534,10 @@ public:
|
||||
/// \brief Swap two nodes
|
||||
/// \param i0 The id of the first node
|
||||
/// \param i1 The id of the second node
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void swap(size_t i0, size_t i1) {
|
||||
assertf(i0 != root and i1 != root, "Cannot Swap With Root");
|
||||
|
||||
@@ -344,19 +563,146 @@ public:
|
||||
_erase(i);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Traversal ===========================================================================================================
|
||||
|
||||
struct pre_order {
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
/// \name Traversal
|
||||
/// @{
|
||||
|
||||
size_t operator()(const rdtree&, size_t start) {
|
||||
///
|
||||
/// \brief Traverse the tree using a specified order and visiting functor
|
||||
///
|
||||
/// \details
|
||||
/// The visitor should accept a reference to a value of type \f$TypeT\f$ and a \f$size_t\f$ which contains the node's id.
|
||||
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
|
||||
///
|
||||
/// \tparam OrderT The order with which to traverse the tree.
|
||||
/// \tparam VisitorT The visitor, should fulfill the signature \f$uint8_t visit(TypeT&, size_t)\f$
|
||||
/// \param visit The visiting object
|
||||
/// \param i The node to start at
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename OrderT, typename VisitorT>
|
||||
constexpr void traverse(VisitorT&& visit, size_t i = root) {
|
||||
OrderT order;
|
||||
i = order(*this, i);
|
||||
while (i != npos) {
|
||||
uint8_t mode = traversal_control_continue;
|
||||
if (_table[i].value) {
|
||||
mode = visit(_table[i].value, i);
|
||||
}
|
||||
if (mode == traversal_control_break) {
|
||||
break;
|
||||
}
|
||||
i = order[*this, i, mode];
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverse the tree using a specified order and visiting functor
|
||||
///
|
||||
/// \details
|
||||
/// The visitor should accept a reference to a value of type \f$TypeT\f$ and a \f$size_t\f$ which contains the node's id.
|
||||
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum
|
||||
///
|
||||
/// \tparam OrderT The order with which to traverse the tree.
|
||||
/// \tparam VisitorT The visitor, should fulfill the signature \f$uint8_t visit(TypeT&, size_t)\f$
|
||||
/// \param visit The visiting object
|
||||
/// \param i The node to start at
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename OrderT, typename VisitorT>
|
||||
constexpr void traverse(VisitorT&& visit, size_t i = root) const {
|
||||
OrderT order;
|
||||
i = order(*this, i);
|
||||
while (i != npos) {
|
||||
uint8_t mode = traversal_control_continue;
|
||||
if (_table[i].value) {
|
||||
mode = visit(_table[i].value, i);
|
||||
}
|
||||
if (mode == traversal_control_break) {
|
||||
break;
|
||||
}
|
||||
i = order[*this, i, mode];
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser pattern for breadth-first traversal
|
||||
struct breadth_first {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree&, size_t start) {
|
||||
head = start;
|
||||
return start;
|
||||
}
|
||||
|
||||
size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \param mode The mode specifying branch conditions
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
|
||||
size_t nxt = tree.next(node);
|
||||
size_t chd = tree.next(node);
|
||||
|
||||
if (nxt != npos && node != head) {
|
||||
visit.push_front(nxt);
|
||||
}
|
||||
|
||||
if (chd != npos && mode != traversal_control_jump_over) {
|
||||
visit.push_back(chd);
|
||||
}
|
||||
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
node = npos;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Traverser pattern for pre-order traversal
|
||||
struct pre_order {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree&, size_t start) {
|
||||
head = start;
|
||||
return start;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \param mode The mode specifying branch conditions
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
@@ -372,7 +718,7 @@ public:
|
||||
visit.push_front(chd);
|
||||
}
|
||||
|
||||
if (not visit.empty()) {
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
@@ -381,18 +727,32 @@ public:
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
struct in_order {
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
size_t operator()(const rdtree& tree, size_t start) {
|
||||
///
|
||||
/// \brief Traverser pattern for in-order traversal
|
||||
struct in_order {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree& tree, size_t start) {
|
||||
head = start;
|
||||
return tree.left_most(start);
|
||||
}
|
||||
|
||||
size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
@@ -410,7 +770,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (not visit.empty()) {
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
@@ -419,18 +779,32 @@ public:
|
||||
|
||||
return node;
|
||||
}
|
||||
};
|
||||
|
||||
struct post_order {
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
size_t operator()(const rdtree& tree, size_t start) {
|
||||
///
|
||||
/// \brief Traverser pattern for post-order traversal
|
||||
struct post_order {
|
||||
|
||||
///
|
||||
/// \brief Traversal Init
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param start The node in the tree to start at
|
||||
/// \returns The first index of the specified order
|
||||
constexpr size_t operator()(const rdtree& tree, size_t start) {
|
||||
head = start;
|
||||
return tree.left_most(start);
|
||||
}
|
||||
|
||||
size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
///
|
||||
/// \brief Traverser Step
|
||||
/// \param tree The tree we are operating on
|
||||
/// \param node The current node
|
||||
/// \returns The next index according to the traversal order
|
||||
constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) {
|
||||
if (node == npos) {
|
||||
return npos;
|
||||
}
|
||||
@@ -446,7 +820,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (not visit.empty()) {
|
||||
if (not visit.is_empty()) {
|
||||
node = visit.front();
|
||||
visit.pop_front();
|
||||
} else {
|
||||
@@ -455,34 +829,32 @@ public:
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private:
|
||||
list<size_t> visit;
|
||||
size_t head;
|
||||
};
|
||||
|
||||
template<typename OrderT, typename VisitorT>
|
||||
void traverse(VisitorT&& visit, size_t i = root) {
|
||||
OrderT order;
|
||||
i = order(*this, i);
|
||||
while (i != npos) {
|
||||
uint8_t mode = visit(*_table[i].value, i);
|
||||
if (mode == traversal_control_break) {
|
||||
break;
|
||||
}
|
||||
i = order[*this, i, mode];
|
||||
}
|
||||
}
|
||||
/// @}
|
||||
|
||||
|
||||
protected:
|
||||
allocation<node, alloc_t> _table;
|
||||
list<size_t> _freed;
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
table_t _table;
|
||||
freed_t _freed;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
void _expand() {
|
||||
_table.creallocate(_table.capacity() * 2);
|
||||
_table.reallocate(_table.capacity() * 2);
|
||||
}
|
||||
|
||||
size_t _next_free() {
|
||||
size_t next = _size;
|
||||
if (not _freed.empty()) {
|
||||
if (not _freed.is_empty()) {
|
||||
next = _freed.front();
|
||||
_freed.pop_front();
|
||||
}
|
||||
@@ -536,7 +908,7 @@ protected:
|
||||
constexpr void _erase(size_t i) {
|
||||
list<size_t> queue;
|
||||
queue.push_back(child(i));
|
||||
while (not queue.empty()) {
|
||||
while (not queue.is_empty()) {
|
||||
size_t n = queue.front(); queue.pop_front();
|
||||
if (n == npos) continue;
|
||||
queue.push_back(next(n));
|
||||
|
||||
927
include/fennec/containers/sequence.h
Normal file
927
include/fennec/containers/sequence.h
Normal file
@@ -0,0 +1,927 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/sequence.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_SEQUENCE_H
|
||||
#define FENNEC_CONTAINERS_SEQUENCE_H
|
||||
|
||||
#include <fennec/containers/pair.h>
|
||||
#include <fennec/containers/sequence.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
#include <fennec/memory/allocator.h>
|
||||
|
||||
// https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
|
||||
// https://www.geeksforgeeks.org/dsa/insertion-in-red-black-tree/
|
||||
// https://github.com/anandarao/Red-Black-Tree/blob/master/RBTree.cpp
|
||||
|
||||
// After rewriting the _fix_insert and _fix_erase functions the performance decreased significantly in the lower end
|
||||
// but now in the higher end it remains consistent. Something I was doing was disturbing both the rb-tree and bst tree
|
||||
// properties, now that is fixed. I'll see about optimizing more in the future.
|
||||
|
||||
// I realized that the way bintree is setup makes some insert calls O(n + log n) = O(n), so I switched to a pointer based model.
|
||||
// This increased performance overall maintaining O(log n).
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for ordered sets of elements, called sequences in mathematics
|
||||
/// \details
|
||||
/// This data-structure behaves like an ordered-set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:---------------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ✅ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(\log N)\f$ |
|
||||
/// | find | \f$O(\log N)\f$ |
|
||||
/// | insertion | \f$O(\log N)\f$ |
|
||||
/// | deletion | \f$O(\log N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
/// \tparam CompareT Function for comparing two values
|
||||
/// \tparam AllocT An allocator class
|
||||
template<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>>
|
||||
struct sequence {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct _node;
|
||||
|
||||
enum color_ : bool {
|
||||
black = false,
|
||||
red = true,
|
||||
};
|
||||
|
||||
enum dir_ : bool {
|
||||
dir_left = false,
|
||||
dir_right = true,
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using value_t = TypeT; //!< the value type
|
||||
using node_t = pair<TypeT, bool>; //!< the node type
|
||||
using alloc_t = allocator_traits<AllocT>::template rebind<_node>; //!< underlying alloc type
|
||||
using compare_t = CompareT; //!< comparison type
|
||||
|
||||
/// @}
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
private:
|
||||
using node = _node*;
|
||||
|
||||
struct _node {
|
||||
node parent;
|
||||
node child[2];
|
||||
value_t key;
|
||||
bool color;
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr _node(ArgsT&&...args)
|
||||
: parent(nullptr)
|
||||
, child { nullptr, nullptr }
|
||||
, key(fennec::forward<ArgsT>(args)...)
|
||||
, color(red) {
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr _node(node p, node l, node r, ArgsT&&...args)
|
||||
: parent(p)
|
||||
, child { l, r }
|
||||
, key(fennec::forward<ArgsT>(args)...)
|
||||
, color(red) {
|
||||
}
|
||||
|
||||
constexpr ~_node() {
|
||||
parent = nullptr;
|
||||
child[0] = nullptr;
|
||||
child[1] = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Member Access Helpers
|
||||
|
||||
constexpr value_t& _key(node n) {
|
||||
return n->key;
|
||||
}
|
||||
|
||||
constexpr bool& _color(node n) {
|
||||
return n->color;
|
||||
}
|
||||
|
||||
constexpr node& _parent(node n) {
|
||||
return n->parent;
|
||||
}
|
||||
|
||||
constexpr node& _child(node n, bool dir) {
|
||||
return n->child[dir];
|
||||
}
|
||||
|
||||
constexpr node& _left(node n) {
|
||||
return n->child[dir_left];
|
||||
}
|
||||
|
||||
constexpr node& _right(node n) {
|
||||
return n->child[dir_right];
|
||||
}
|
||||
|
||||
|
||||
// Safe Member Access Helpers
|
||||
|
||||
|
||||
constexpr const value_t& key(node n) const {
|
||||
return n->key;
|
||||
}
|
||||
|
||||
constexpr bool color(node n) {
|
||||
return n ? n->color : (bool)black;
|
||||
}
|
||||
|
||||
constexpr node parent(node n) {
|
||||
return n ? n->parent : nullptr;
|
||||
}
|
||||
|
||||
constexpr node child(node n, bool dir) {
|
||||
return n ? n->child[dir] : nullptr;
|
||||
}
|
||||
|
||||
constexpr node left(node n) {
|
||||
return n ? n->child[dir_left] : nullptr;
|
||||
}
|
||||
|
||||
constexpr node right(node n) {
|
||||
return n ? n->child[dir_right] : nullptr;
|
||||
}
|
||||
|
||||
constexpr node leftmost(node n) {
|
||||
if (n == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (this->_left(n)) {
|
||||
n = this->_left(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
constexpr node rightmost(node n) {
|
||||
if (n == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (this->_right(n)) {
|
||||
n = this->_right(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
// Constructors & Destructors ==========================================================================================
|
||||
public:
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr sequence()
|
||||
: _root(nullptr), _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, takes ownership of a sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr sequence(sequence&&) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies a sequence
|
||||
constexpr sequence(const sequence&) = default;
|
||||
// TODO: properly implement
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs elements *in-order*
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~sequence() {
|
||||
this->clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Search ==============================================================================================================
|
||||
public:
|
||||
/// \name Search
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Value Find Function, finds the iterator position for \f$val\f$, otherwise returns \f$end()\f$
|
||||
/// \param val The value to find
|
||||
/// \returns An iterator at the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr iterator find(const value_t& val) {
|
||||
node node = _root;
|
||||
while (node) {
|
||||
if (_compare(val, _key(node))) {
|
||||
node = _left(node);
|
||||
} else if (_compare(_key(node), val)) {
|
||||
node = _right(node);
|
||||
} else {
|
||||
return sequence::iterator(this, _root, node);
|
||||
}
|
||||
}
|
||||
return sequence::iterator(this, _root, node);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Value Contains Function, checks if the sequence contains a value
|
||||
/// \param val The value to find
|
||||
/// \returns \f$true\f$ if \f$val\f$ is in the sequence, \f$false\f$ otherwise
|
||||
bool contains(const value_t& val) {
|
||||
return find(val) != end();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The number of elements in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ when there are no elements in the sequence, \f$false\f$ otherwise.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion, moves \f$val\f$ into the sequence
|
||||
/// \param val The value to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void insert(value_t&& val) {
|
||||
node i = _insert_bst(fennec::forward<value_t>(val));
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion, inserts a copy of \f$val\f$ into the sequence
|
||||
/// \param val The value to insert
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void insert(const value_t& val) {
|
||||
node i = _insert_bst(val);
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplacement, constructs and adds a value into the sequence
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
node i = _insert_bst(fennec::forward<ArgsT>(args)...);
|
||||
_fix_insert(i);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase the specified value from the sequence
|
||||
/// \param val the value to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr void erase(const value_t& val) {
|
||||
_erase(find(val)._node);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructs all elements, *in-order*, contained in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
list<node> visit;
|
||||
for (iterator it = begin(); it != end(); ++it) {
|
||||
visit.push_back(it._node);
|
||||
}
|
||||
for (node n : visit) {
|
||||
this->_free_node(n);
|
||||
}
|
||||
_root = nullptr;
|
||||
_size = 0;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// Iterator ============================================================================================================
|
||||
|
||||
///
|
||||
/// \returns An iterator at the smallest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr iterator begin() {
|
||||
return sequence::iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns An iterator at the smallest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(\log N)\f$
|
||||
///
|
||||
constexpr const_iterator begin() const {
|
||||
return sequence::const_iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator after the largest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() {
|
||||
return sequence::iterator(this, _root, nullptr);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const C++ Iterator Specification \f$end()\f$
|
||||
/// \returns An iterator after the largest element in the sequence
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const_iterator end() const {
|
||||
return sequence::const_iterator(this, _root, nullptr);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ after having moved to the next element in the list
|
||||
friend iterator& operator++(iterator& it) {
|
||||
if (it._node == nullptr) {
|
||||
return it;
|
||||
}
|
||||
|
||||
node parent = it._seq->_parent(it._node);
|
||||
node pright = it._seq->right(parent);
|
||||
node next = it._seq->leftmost(it._seq->right(it._node));
|
||||
|
||||
if (it._node != pright && parent != nullptr) {
|
||||
it._visit.push_front(parent);
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
it._visit.push_front(next);
|
||||
}
|
||||
|
||||
if (not it._visit.is_empty()) {
|
||||
it._node = it._visit.front();
|
||||
it._visit.pop_front();
|
||||
} else {
|
||||
it._node = nullptr;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ before having moved to the next element in the list
|
||||
friend iterator operator++(iterator& it, int) {
|
||||
iterator prev = it;
|
||||
++it;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
const value_t& operator*() const {
|
||||
return _node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
const value_t* operator->() const {
|
||||
return &_node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr friend bool operator==(const iterator& lhs, const iterator& rhs) {
|
||||
return lhs._seq == rhs._seq and lhs._node == rhs._node;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr friend bool operator!=(const iterator& lhs, const iterator& rhs) {
|
||||
return lhs._seq != rhs._seq or lhs._node != rhs._node;
|
||||
}
|
||||
|
||||
private:
|
||||
sequence* _seq;
|
||||
node _head;
|
||||
node _node;
|
||||
list<node> _visit;
|
||||
|
||||
constexpr iterator(sequence* seq, node start)
|
||||
: _seq(seq)
|
||||
, _head(start)
|
||||
, _node(seq->leftmost(start)) {
|
||||
}
|
||||
|
||||
constexpr iterator(sequence* seq, node root, node start)
|
||||
: _seq(seq)
|
||||
, _head(root)
|
||||
, _node(start) {
|
||||
}
|
||||
|
||||
friend struct sequence;
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class const_iterator {
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ after having moved to the next element in the list
|
||||
friend const_iterator& operator++(const_iterator& it) {
|
||||
if (it._node == nullptr) {
|
||||
return it;
|
||||
}
|
||||
|
||||
node parent = it._seq->_parent(it._node);
|
||||
node pright = it._seq->right(parent);
|
||||
node next = it._seq->leftmost(it._seq->right(it._node));
|
||||
|
||||
if (it._node != pright && parent != nullptr) {
|
||||
it._visit.push_front(parent);
|
||||
}
|
||||
|
||||
if (next != nullptr) {
|
||||
it._visit.push_front(next);
|
||||
}
|
||||
|
||||
if (not it._visit.is_empty()) {
|
||||
it._node = it._visit.front();
|
||||
it._visit.pop_front();
|
||||
} else {
|
||||
it._node = nullptr;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ before having moved to the next element in the list
|
||||
friend const_iterator operator++(const_iterator& it, int) {
|
||||
const_iterator prev = it;
|
||||
++it;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
const value_t& operator*() const {
|
||||
return _node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
const value_t* operator->() const {
|
||||
return &_node->key;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr friend bool operator==(const const_iterator& lhs, const const_iterator& rhs) {
|
||||
return lhs._seq == rhs._seq and lhs._node == rhs._node;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param lhs the iterator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr friend bool operator!=(const const_iterator& lhs, const const_iterator& rhs) {
|
||||
return lhs._seq != rhs._seq or lhs._node != rhs._node;
|
||||
}
|
||||
|
||||
private:
|
||||
const sequence* _seq;
|
||||
node _head;
|
||||
node _node;
|
||||
list<node> _visit;
|
||||
|
||||
constexpr const_iterator(const sequence* seq, node start)
|
||||
: _seq(seq)
|
||||
, _head(start)
|
||||
, _node(seq->leftmost(start)) {
|
||||
}
|
||||
|
||||
constexpr const_iterator(const sequence* seq, node root, node start)
|
||||
: _seq(seq)
|
||||
, _head(root)
|
||||
, _node(start) {
|
||||
}
|
||||
|
||||
friend struct sequence;
|
||||
};
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
alloc_t _alloc;
|
||||
node _root;
|
||||
compare_t _compare;
|
||||
size_t _size;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr node _make_node(ArgsT&&...args) {
|
||||
node res = _alloc.allocate(1);
|
||||
fennec::construct(res, fennec::forward<ArgsT>(args)...);
|
||||
return res;
|
||||
}
|
||||
|
||||
constexpr void _free_node(node n) {
|
||||
fennec::destruct(n);
|
||||
_alloc.deallocate(n);
|
||||
}
|
||||
|
||||
constexpr node _rotate(node sub, bool dir) {
|
||||
if (sub == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
node sub_parent = _parent(sub);
|
||||
node new_root = _child(sub, not dir);
|
||||
node new_child = _child(new_root, dir);
|
||||
|
||||
_child(sub, not dir) = new_child;
|
||||
if (new_child != nullptr) {
|
||||
_parent(new_child) = sub;
|
||||
}
|
||||
_child(new_root, dir) = sub;
|
||||
|
||||
_parent(new_root) = sub_parent;
|
||||
_parent(sub) = new_root;
|
||||
if (sub_parent != nullptr) {
|
||||
_child(sub_parent, sub == _right(sub_parent)) = new_root;
|
||||
} else {
|
||||
_root = new_root;
|
||||
}
|
||||
return new_root;
|
||||
}
|
||||
|
||||
constexpr void _recolor(node n) {
|
||||
bool c = color(n) == black;
|
||||
if (n == _root) { // Only recolor if not the root node
|
||||
_color(n) = c;
|
||||
}
|
||||
_color(_left(n)) = !_color(_left(n)) ;
|
||||
_color(_right(n)) = !_color(_right(n));
|
||||
}
|
||||
|
||||
// run-of-the-mill bst insert
|
||||
template<typename...ArgsT>
|
||||
constexpr node _insert_bst(ArgsT&&...args) {
|
||||
node res = _make_node(fennec::forward<ArgsT>(args)...);
|
||||
|
||||
if (_root == nullptr) {
|
||||
++_size;
|
||||
_color(res) = black;
|
||||
return _root = res;
|
||||
}
|
||||
|
||||
node i = _root;
|
||||
node p = nullptr;
|
||||
bool d = dir_left;
|
||||
while (i != nullptr) {
|
||||
p = i;
|
||||
|
||||
if (_compare(_key(res), _key(i))) {
|
||||
i = _left(i);
|
||||
d = dir_left;
|
||||
} else if (_compare(_key(i), _key(res))) {
|
||||
i = _right(i);
|
||||
d = dir_right;
|
||||
} else {
|
||||
_free_node(res);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
++_size;
|
||||
_child(p, d) = res;
|
||||
_parent(res) = p;
|
||||
return res;
|
||||
}
|
||||
|
||||
// This makes some cheats given that the structure is modified only by internal functions
|
||||
// If such is the case, ONLY LL, LR, RL, and RR will show up
|
||||
// Then we just need to handle splitting a 4-node
|
||||
constexpr void _fix_insert(node n) {
|
||||
if (n == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
node p = _parent(n);
|
||||
while (n != _root && color(n) == red && color(p) == red) {
|
||||
node g = _parent(p);
|
||||
bool d = n == _right(p);
|
||||
bool r = p == _right(g);
|
||||
node u = _child(g, !r);
|
||||
|
||||
if (color(u) == red) {
|
||||
_recolor(g);
|
||||
n = g;
|
||||
p = _parent(n);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d != r) {
|
||||
_rotate(p, r);
|
||||
n = p;
|
||||
p = _parent(n);
|
||||
}
|
||||
|
||||
_rotate(g, not r);
|
||||
fennec::swap(_color(p), _color(g));
|
||||
n = p;
|
||||
p = _parent(n);
|
||||
}
|
||||
_color(_root) = black;
|
||||
}
|
||||
|
||||
constexpr void _swap_val(node a, node b) {
|
||||
fennec::swap(_key(a), _key(b));
|
||||
}
|
||||
|
||||
constexpr node _red_child(node x) {
|
||||
node l = _left(x);
|
||||
node r = _right(x);
|
||||
|
||||
if (color(l) == red) {
|
||||
return l;
|
||||
}
|
||||
|
||||
if (color(r) == red) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
constexpr void _fix_erase(node n) {
|
||||
if (n == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (n == _root) {
|
||||
_root = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
node o = n;
|
||||
node p = _parent(n);
|
||||
if (p == nullptr) {
|
||||
_root = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
bool d = n == _right(p);
|
||||
node c = _red_child(n);
|
||||
node s = nullptr;
|
||||
if (_color(n) == red || c != nullptr) {
|
||||
_child(p, d) = c;
|
||||
if (c != nullptr) {
|
||||
_parent(c) = p;
|
||||
}
|
||||
_color(c) = black;
|
||||
return;
|
||||
}
|
||||
|
||||
while (n != _root) {
|
||||
p = _parent(n);
|
||||
d = n == _right(p);
|
||||
s = _child(p, !d);
|
||||
|
||||
if (s == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (_color(s) == red) {
|
||||
_color(s) = black;
|
||||
_color(p) = red;
|
||||
_rotate(p, d);
|
||||
continue;
|
||||
}
|
||||
|
||||
node nc = _child(s, d);
|
||||
node nf = _child(s, !d);
|
||||
|
||||
if (color(nc) == black && color(nf) == black) {
|
||||
_color(s) = red;
|
||||
if (_color(p) == red) {
|
||||
_color(p) = black;
|
||||
break;
|
||||
}
|
||||
n = p;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (color(nf) == black) {
|
||||
_color(nc) = black;
|
||||
_color(s) = red;
|
||||
_rotate(s, !d);
|
||||
s = nc;
|
||||
nf = s;
|
||||
}
|
||||
_color(s) = _color(p);
|
||||
_color(p) = black;
|
||||
_color(nf) = black;
|
||||
_rotate(p, d);
|
||||
break;
|
||||
}
|
||||
|
||||
p = parent(o);
|
||||
if (p != nullptr) {
|
||||
if (o == _left(p)) {
|
||||
_left(p) = nullptr;
|
||||
} else {
|
||||
_right(p) = nullptr;
|
||||
}
|
||||
_color(_root) = black;
|
||||
} else {
|
||||
_root = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void _erase(node n) {
|
||||
if (n == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
node l = _left(n);
|
||||
node r = _right(n);
|
||||
|
||||
// 2 children
|
||||
if (l != nullptr && r != nullptr) {
|
||||
node s = leftmost(r);
|
||||
_swap_val(n, s);
|
||||
n = s;
|
||||
l = _left(n);
|
||||
r = _right(n);
|
||||
}
|
||||
|
||||
node p = _parent(n);
|
||||
bool d = n == right(p);
|
||||
node c = l != nullptr ? l : r;
|
||||
|
||||
// Single child
|
||||
if (c != nullptr) {
|
||||
_parent(c) = p;
|
||||
}
|
||||
|
||||
// Handles root cases
|
||||
if (p == nullptr) {
|
||||
_root = c;
|
||||
if (c == nullptr) {
|
||||
_free_node(n);
|
||||
--_size;
|
||||
return;
|
||||
} else {
|
||||
_color(c) = black;
|
||||
}
|
||||
}
|
||||
|
||||
// Single Child, Red, and Root cases
|
||||
if (p == nullptr || c != nullptr || _color(n) == red) {
|
||||
if (p != nullptr) {
|
||||
_child(p, d) = c;
|
||||
}
|
||||
_free_node(n);
|
||||
--_size;
|
||||
return;
|
||||
}
|
||||
|
||||
_fix_erase(n);
|
||||
_free_node(n);
|
||||
--_size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_SEQUENCE_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,12 +16,23 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/set.h
|
||||
/// \brief A header containing the definition for a set of unique values
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_SET_H
|
||||
#define FENNEC_CONTAINERS_SET_H
|
||||
|
||||
// https://programming.guide/robin-hood-hashing.html
|
||||
|
||||
#include <fennec/containers/multiset.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/containers/set.h>
|
||||
#include <fennec/lang/compare.h>
|
||||
@@ -39,44 +50,72 @@ namespace fennec
|
||||
/// This data-structure behaves like a set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:----------|:----------:|
|
||||
/// | stable | \emoji x |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ✅ |
|
||||
/// | distinct | ✅ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
|
||||
struct set {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using hash_t = Hash;
|
||||
using equal_t = Equals;
|
||||
using elem_t = TypeT;
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<node>; //!< the allocator type
|
||||
|
||||
using hash_t = Hash; //!< the hash type
|
||||
using equal_t = Equals; //!< the equality type
|
||||
using elem_t = TypeT; //!< the element type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr double default_load = 0.8; //!< the default load factor for reallocation
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
class iterator;
|
||||
static constexpr size_t npos = -1;
|
||||
static constexpr double default_load = 0.8;
|
||||
|
||||
private:
|
||||
struct node {
|
||||
optional<elem_t> value;
|
||||
int psl;
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
using table_t = dynarray<node, alloc_t>;
|
||||
|
||||
constexpr node() = default;
|
||||
constexpr ~node() = default;
|
||||
};
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty set
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set()
|
||||
: _alloc()
|
||||
: _table()
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
@@ -85,18 +124,13 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Constructor, initializes empty set with a hash
|
||||
constexpr set(const hash_t& hash)
|
||||
: _alloc()
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
/// \param hash the hash object
|
||||
///
|
||||
/// \brief Hash Move Constructor, initializes empty set with a hash
|
||||
constexpr set(hash_t&& hash) noexcept
|
||||
: _alloc()
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(const hash_t& hash)
|
||||
: _table()
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
@@ -105,18 +139,13 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
|
||||
constexpr set(const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
/// \param alloc the allocator object
|
||||
///
|
||||
/// \brief Alloc Move Constructor, initializes empty set with an allocator
|
||||
constexpr set(alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr set(const alloc_t& alloc)
|
||||
: _table(alloc)
|
||||
, _hash()
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
@@ -125,38 +154,14 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
/// \param hash the hash object
|
||||
/// \param alloc the allocator object
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(const hash_t& hash, const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Alloc Move Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Alloc Move Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
: _table(alloc)
|
||||
, _hash(hash)
|
||||
, _size(0)
|
||||
, _sumpsl(0)
|
||||
@@ -166,8 +171,12 @@ public:
|
||||
///
|
||||
/// \brief Set Copy Constructor
|
||||
/// \param set Set to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(const set& set)
|
||||
: _alloc(set._alloc)
|
||||
: _table(set._table)
|
||||
, _hash(set._hash)
|
||||
, _size(set._size)
|
||||
, _sumpsl(set._sumpsl)
|
||||
@@ -177,8 +186,12 @@ public:
|
||||
///
|
||||
/// \brief Set Move Constructor
|
||||
/// \param set Set to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr set(set&& set) noexcept
|
||||
: _alloc(fennec::move(set._alloc))
|
||||
: _table(fennec::move(set._table))
|
||||
, _hash(fennec::move(set._hash))
|
||||
, _size(fennec::move(set._size))
|
||||
, _sumpsl(set._sumpsl)
|
||||
@@ -187,34 +200,70 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Destructor, destructs all elements and releases the allocation
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr ~set() {
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
_alloc[i].value = nullopt;
|
||||
_table[i].value = nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns Size of the set in elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Capacity of the set in elements
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
/// \returns \f$true\f$ when the set is empty, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool is_empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Capacity of the set in elements
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr size_t capacity() const {
|
||||
return _table.size();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Find an Element
|
||||
/// \param val Value to find
|
||||
/// \returns An iterator at the location of the value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator find(const elem_t& val) const {
|
||||
if (capacity() == 0) {
|
||||
return end();
|
||||
@@ -225,8 +274,8 @@ public:
|
||||
size_t n = 0;
|
||||
|
||||
// Check the first element;
|
||||
if (_alloc[i].psl >= psl && _alloc[i].value) {
|
||||
if (*_alloc[i].value == val) {
|
||||
if (_table[i].psl >= psl && _table[i].value) {
|
||||
if (_equal(*_table[i].value, val)) {
|
||||
return iterator(this, i);
|
||||
}
|
||||
}
|
||||
@@ -237,16 +286,16 @@ public:
|
||||
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
|
||||
size_t i1 = (i + n) % capacity();
|
||||
int p0 = psl - n, p1 = psl + n;
|
||||
bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
|
||||
bool c0 = p0 >= 0 && _table[i0].psl >= p0, c1 = _table[i1].psl >= p1; // Check that we are in range
|
||||
|
||||
if (c0 && _alloc[i0].value) {
|
||||
if (*_alloc[i0].value == val) {
|
||||
if (c0 && _table[i0].value) {
|
||||
if (_equal(*_table[i0].value, val)) {
|
||||
return iterator(this, i0);
|
||||
}
|
||||
}
|
||||
|
||||
if (c1 && _alloc[i1].value) {
|
||||
if (*_alloc[i1].value == val) {
|
||||
if (c1 && _table[i1].value) {
|
||||
if (_equal(*_table[i1].value, val)) {
|
||||
return iterator(this, i1);
|
||||
}
|
||||
}
|
||||
@@ -262,7 +311,11 @@ public:
|
||||
///
|
||||
/// \brief Check if a set contains a value
|
||||
/// \param val Value to check
|
||||
/// \returns `true` if `val` can be found, `false` otherwise
|
||||
/// \returns \f$true\f$ if \f$val\f$ can be found, \f$false\f$ otherwise
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr bool contains(const elem_t& val) const {
|
||||
return this->find(val) != end();
|
||||
}
|
||||
@@ -270,73 +323,105 @@ public:
|
||||
///
|
||||
/// \brief Iterator Access
|
||||
/// \param it Location to access
|
||||
/// \returns A pointer to the element, `nullptr` if not found.
|
||||
/// \returns A pointer to the element, \f$nullptr\f$ if not found.
|
||||
/// The value should not be changed in a manner that will change the hash of the element.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr elem_t* at(const iterator& it) {
|
||||
if (it == end()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (not _alloc[it._i].value) {
|
||||
if (not _table[it._i].value) {
|
||||
return nullptr;
|
||||
}
|
||||
return &*_alloc[it._i].value;
|
||||
return &*_table[it._i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Const Access
|
||||
/// \param it Location to access
|
||||
/// \returns A const-qualified pointer to the element, `nullptr` if not found.
|
||||
/// \returns A const-qualified pointer to the element, \f$nullptr\f$ if not found.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr const elem_t* at(const iterator& it) const {
|
||||
if (not _alloc[it._i].value) return nullptr;
|
||||
return &*_alloc[it._i].value;
|
||||
if (not _table[it._i].value) return nullptr;
|
||||
return &*_table[it._i].value;
|
||||
}
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
/// @}
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param val Value to insert
|
||||
/// \returns An iterator at the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(elem_t&& val) {
|
||||
return fennec::move(this->_insert(fennec::forward<elem_t>(val)));
|
||||
return this->_insert(fennec::forward<elem_t>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param val Value to insert
|
||||
/// \returns An iterator at the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator insert(const elem_t& val) {
|
||||
return fennec::move(this->_insert(val));
|
||||
return this->_insert(val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments to construct with
|
||||
/// \returns An iterator at the held value
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator emplace(ArgsT&&...args) {
|
||||
return fennec::move(this->_insert(fennec::forward<ArgsT>(args)...));
|
||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param it Location to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(iterator it) {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) {
|
||||
return;
|
||||
} // These are separated due to compilers being inconsistent
|
||||
if (not _alloc[i].value) {
|
||||
if (not _table[i].value) {
|
||||
return;
|
||||
}
|
||||
|
||||
_alloc[i].value = nullopt;
|
||||
_sumpsl -= _alloc[i].psl;
|
||||
_table[i].value = nullopt;
|
||||
_sumpsl -= _table[i].psl;
|
||||
--_size;
|
||||
size_t p = i;
|
||||
while (_alloc[i = (i + 1) % capacity()].value) {
|
||||
if (_alloc[i].psl == 0) break;
|
||||
while (_table[i = (i + 1) % capacity()].value) {
|
||||
if (_table[i].psl == 0) break;
|
||||
|
||||
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
|
||||
--_alloc[p].psl, --_sumpsl;
|
||||
fennec::swap(_table[p].value, _table[i].value);
|
||||
--_table[p].psl, --_sumpsl;
|
||||
p = i;
|
||||
}
|
||||
}
|
||||
@@ -344,133 +429,207 @@ public:
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param val Value to erase
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr void erase(const elem_t& val) {
|
||||
this->erase(this->find(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Clear all elements from the set, destructing them
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
constexpr void clear() {
|
||||
_table.clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// ITERATOR ============================================================================================================
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns An iterator for all elements of the set in no particular order
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
/// \brief Class for Iterating the Set
|
||||
class iterator {
|
||||
public:
|
||||
constexpr iterator(const set* set, size_t i)
|
||||
: _set(set)
|
||||
, _i(i) {
|
||||
}
|
||||
|
||||
constexpr ~iterator() {
|
||||
_set = nullptr;
|
||||
}
|
||||
|
||||
// prefix operator
|
||||
constexpr friend iterator& operator++(iterator& rhs) {
|
||||
while (++rhs._i < rhs._set->capacity()) {
|
||||
if (rhs._set->_alloc[rhs._i].value) {
|
||||
return rhs;
|
||||
}
|
||||
}
|
||||
rhs._i = npos;
|
||||
return rhs;
|
||||
}
|
||||
|
||||
constexpr friend iterator operator++(iterator& lhs, int) {
|
||||
iterator prev = lhs;
|
||||
++lhs;
|
||||
return prev;
|
||||
}
|
||||
|
||||
constexpr const elem_t& operator*() const {
|
||||
return *_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr const elem_t* operator->() const {
|
||||
if (not _set->_alloc[_i].value) return nullptr;
|
||||
return &*_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const iterator& it) const {
|
||||
return _set == it._set and _i == it._i;
|
||||
}
|
||||
|
||||
constexpr bool operator!=(const iterator& it) const {
|
||||
return _set != it._set or _i != it._i;
|
||||
}
|
||||
|
||||
constexpr size_t index() const { return _i; }
|
||||
|
||||
private:
|
||||
const set* _set;
|
||||
size_t _i;
|
||||
friend set;
|
||||
};
|
||||
|
||||
constexpr iterator begin() const {
|
||||
iterator it(this, 0);
|
||||
if (not _alloc[it._i].value) {
|
||||
while (not _table[it._i].value) {
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns An iterator representing the end of the set
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
constexpr iterator end() const {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
// PRIVATE =============================================================================================================
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief destructor
|
||||
constexpr ~iterator() {
|
||||
_set = nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ after having moved to the next element in the list
|
||||
constexpr friend iterator& operator++(iterator& it) {
|
||||
while (++it._i < it._set->capacity()) {
|
||||
if (it._set->_table[it._i].value) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
it._i = npos;
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \param it the iterator to increment
|
||||
/// \returns \f$it\f$ before having moved to the next element in the list
|
||||
constexpr friend iterator operator++(iterator& it, int) {
|
||||
iterator prev = it;
|
||||
++it;
|
||||
return prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns a reference to the value pointed by the iterator
|
||||
constexpr const elem_t& operator*() const {
|
||||
return *_set->_table[_i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief pointer access operator
|
||||
/// \returns a pointer to the value pointed by the iterator
|
||||
constexpr const elem_t* operator->() const {
|
||||
if (not _set->_table[_i].value) return nullptr;
|
||||
return &*_set->_table[_i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const iterator& it) const {
|
||||
return _set == it._set and _i == it._i;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param it the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const iterator& it) const {
|
||||
return _set != it._set or _i != it._i;
|
||||
}
|
||||
|
||||
private:
|
||||
const set* _set;
|
||||
size_t _i;
|
||||
|
||||
constexpr iterator(const set* set, size_t i)
|
||||
: _set(set)
|
||||
, _i(i) {
|
||||
}
|
||||
|
||||
friend set;
|
||||
};
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
table_t _table;
|
||||
hash_t _hash;
|
||||
equal_t _equal;
|
||||
size_t _size;
|
||||
size_t _sumpsl;
|
||||
float _load;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
constexpr void _expand() {
|
||||
set cpy; // Create a new set
|
||||
cpy._alloc.callocate(
|
||||
fennec::next_prime2(_alloc.capacity())
|
||||
cpy._table.resize(
|
||||
fennec::next_prime2(_table.capacity())
|
||||
);
|
||||
|
||||
// rehash
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
if (_alloc[i].value) {
|
||||
cpy.insert(fennec::move(*_alloc[i].value));
|
||||
if (_table[i].value) {
|
||||
cpy.insert(fennec::move(*_table[i].value));
|
||||
}
|
||||
}
|
||||
|
||||
// Swap buffers
|
||||
fennec::swap(_alloc, cpy._alloc);
|
||||
fennec::swap(_table, cpy._table);
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr iterator _insert(ArgsT&&...args) {
|
||||
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
|
||||
if (_size == 0 or static_cast<float>(_size) / capacity() >= _load) { // expand when full
|
||||
_expand();
|
||||
}
|
||||
|
||||
elem_t value(fennec::forward<ArgsT>(args)...);
|
||||
size_t i = _hash(value) % capacity(); // Initial search index
|
||||
int psl = 0;
|
||||
while (_alloc[i].value) { // Search for empty cell
|
||||
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
|
||||
while (_table[i].value) { // Search for empty cell
|
||||
if (_equal(*_table[i].value, value)) { // Check to see if this element is already inserted
|
||||
return iterator(this, i);
|
||||
}
|
||||
if (psl > _alloc[i].psl) { // When psl is higher, swap
|
||||
_sumpsl += psl - _alloc[i].psl;
|
||||
fennec::swap(_alloc[i].psl, psl);
|
||||
fennec::swap(*_alloc[i].value, value);
|
||||
if (psl > _table[i].psl) { // When psl is higher, swap
|
||||
_sumpsl += psl - _table[i].psl;
|
||||
fennec::swap(_table[i].psl, psl);
|
||||
fennec::swap(*_table[i].value, value);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
_alloc[i].value = fennec::move(value);
|
||||
_sumpsl += (_alloc[i].psl = psl);
|
||||
_table[i].value = fennec::move(value);
|
||||
_sumpsl += (_table[i].psl = psl);
|
||||
++_size;
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
allocation<node, alloc_t> _alloc;
|
||||
hash_t _hash;
|
||||
equal_t _equal;
|
||||
size_t _size;
|
||||
size_t _sumpsl;
|
||||
double _load;
|
||||
|
||||
// Private Definitions =================================================================================================
|
||||
private:
|
||||
struct node {
|
||||
optional<elem_t> value;
|
||||
int psl;
|
||||
|
||||
constexpr node() = default;
|
||||
constexpr ~node() = default;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,16 +16,30 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/traversal.h
|
||||
/// \brief a header containing constants and utilities related to traversal
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_TRAVERSAL_H
|
||||
#define FENNEC_CONTAINERS_TRAVERSAL_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief A set of constants used in the traverser-visitor pattern
|
||||
enum traversal_control_ {
|
||||
traversal_control_continue = 0,
|
||||
traversal_control_jump_over,
|
||||
traversal_control_break
|
||||
traversal_control_continue = 0, //!< Continue to the next element
|
||||
traversal_control_break = 1, //!< Break the traversal loop
|
||||
traversal_control_jump_over = 2, //!< Jump over the next element
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,6 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/tuple.h
|
||||
/// \brief A header containing the definition for a container with multiple values of differing types
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_TUPLE_H
|
||||
#define FENNEC_CONTAINERS_TUPLE_H
|
||||
|
||||
@@ -29,10 +41,31 @@ namespace fennec
|
||||
|
||||
///
|
||||
/// \brief Tuple, holds a collection of values of different types
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ⛔ |
|
||||
/// | dynamic | ✅ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ✅ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | \f$O(1)\f$ |
|
||||
/// | insertion | ⛔ |
|
||||
/// | deletion | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypesT The types to store
|
||||
template<typename...TypesT> struct tuple;
|
||||
|
||||
|
||||
///
|
||||
/// \brief tuple get
|
||||
/// \tparam i the index
|
||||
/// \tparam TypesT the types held in the tuple
|
||||
/// \param x the tuple
|
||||
/// \returns the \f$i\f$th element of the tuple
|
||||
template<size_t i, typename...TypesT>
|
||||
constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
|
||||
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
|
||||
@@ -40,6 +73,11 @@ constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x
|
||||
return it.value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam i the index
|
||||
/// \tparam TypesT the types held in the tuple
|
||||
/// \param x the tuple
|
||||
/// \returns the \f$i\f$th element of the tuple
|
||||
template<size_t i, typename...TypesT>
|
||||
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const tuple<TypesT...>& x) {
|
||||
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
|
||||
@@ -49,28 +87,67 @@ constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const tuple<T
|
||||
|
||||
|
||||
template<typename ...TypesT>
|
||||
struct tuple : public detail::_tuple<make_index_sequence_t<sizeof...(TypesT)>, TypesT...>
|
||||
{
|
||||
using base_t = detail::_tuple<make_index_sequence_t<sizeof...(TypesT)>, TypesT...>;
|
||||
struct tuple : public detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...> {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Definitions
|
||||
/// @{
|
||||
|
||||
using base_t = detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...>; //!< the base type
|
||||
|
||||
template<size_t i>
|
||||
using elem_t = typename nth_element<i, TypesT...>::type;
|
||||
using elem_t = typename nth_element<i, TypesT...>::type; //!< helper for getting the \f$i\f$th element
|
||||
|
||||
static constexpr size_t size = sizeof...(TypesT); //!< the number of elements held by the tuple
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief tuple constructor
|
||||
/// \tparam ArgsT The element types
|
||||
/// \param args The arguments to initialize the tuple with
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
template<typename...ArgsT>
|
||||
tuple(ArgsT&&...args)
|
||||
: base_t(fennec::forward<ArgsT>(args)...) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param cpy the tuple to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
tuple(const tuple& cpy)
|
||||
: base_t(cpy) {
|
||||
}
|
||||
|
||||
tuple(tuple&& cpy)
|
||||
: base_t(cpy) {
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param mov the tuple to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(N)\f$
|
||||
///
|
||||
tuple(tuple&& mov)
|
||||
: base_t(fennec::forward<tuple>(mov)) {
|
||||
}
|
||||
};
|
||||
|
||||
// This is needed for
|
||||
///
|
||||
/// \brief Helper for deducing the tuple constructor
|
||||
/// \tparam TypesT the types of the tuple
|
||||
/// \returns A new tuple containing the passed values
|
||||
template<typename...TypesT>
|
||||
tuple(TypesT...) -> tuple<TypesT...>;
|
||||
|
||||
|
||||
385
include/fennec/containers/variant.h
Normal file
385
include/fennec/containers/variant.h
Normal file
@@ -0,0 +1,385 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/containers/variant.h
|
||||
/// \brief Contains the definition for a structure that holds a single value from multiple types
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CONTAINERS_VARIANT_H
|
||||
#define FENNEC_CONTAINERS_VARIANT_H
|
||||
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/lang/type_sequences.h>
|
||||
#include <fennec/math/ext/common.h>
|
||||
#include <fennec/rtti/type.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief A structure that represents a union between \f$TypesT\ldots\f$
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:-----------:|:----------:|
|
||||
/// | stable | ✅ |
|
||||
/// | dynamic | ⛔ |
|
||||
/// | homogeneous | ⛔ |
|
||||
/// | distinct | ⛔ |
|
||||
/// | ordered | ⛔ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | linear | ⛔ |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | find | ⛔ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam TypesT The types to hold in the variant
|
||||
template<typename...TypesT>
|
||||
struct variant {
|
||||
// Assertions ==========================================================================================================
|
||||
public:
|
||||
|
||||
static_assert(
|
||||
is_unique_v<TypesT...> and // No two types in TypesT... may be equivalent
|
||||
not (is_reference_v<TypesT> or ...) and // No type in TypesT... may be a reference
|
||||
not (is_array_v<TypesT> or ...) and // No type in TypesT... may be an array
|
||||
not (is_void_v<TypesT> or ...) // No type in TypesT... may be void
|
||||
);
|
||||
|
||||
|
||||
// Definitions & Constants =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
static constexpr size_t size = max_element_size_v<TypesT...>; //!< size of the variant in bytes
|
||||
static constexpr size_t nulltype = sizeof...(TypesT); //!< id for a null type
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, constructs the first type in \f$TypesT\ldots\f$ that is default constructible
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
variant()
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type(nulltype) {
|
||||
using construct_t = search_element_t<is_default_constructible, TypesT...>;
|
||||
this->_construct<construct_t>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Conversion Constructor, constructs the type in \f$TypesT\ldots\f$ that is identical to \f$T\f$
|
||||
/// or the first that is constructible with \f$T\f$
|
||||
/// \tparam T The type of the value
|
||||
/// \param t The value to forward
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
variant(T&& t)
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type() {
|
||||
using same_t = search_element_args<is_same, type_sequence<T>, TypesT...>::type;
|
||||
using convert_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>::type;
|
||||
using construct_t = conditional_t<is_void_v<same_t>, convert_t, same_t>;
|
||||
this->_construct<construct_t>(fennec::forward<T>(t));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor, constructs a type \f$T\f$ that is in \f$TypesT\ldots\f$ that is constructible with \f$ArgsT\ldots\f$
|
||||
/// \tparam ArgsT The arguments of the constructor
|
||||
/// \param args The argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT>
|
||||
variant(type_identity<T>, ArgsT&&...args)
|
||||
: _bytes{}
|
||||
, _handle(&_bytes)
|
||||
, _type(nulltype) {
|
||||
static_assert(contains_element_v<T, TypesT...>, "T must be in TypesT...");
|
||||
this->_construct<T>(fennec::forward<ArgsT>(args)...);
|
||||
_type = find_element_v<T>;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
/// \param v The variant to copy
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
variant(const variant& v)
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type(nulltype) {
|
||||
|
||||
if (v._type == nulltype) {
|
||||
return;
|
||||
}
|
||||
|
||||
((v._type == find_element_v<TypesT, TypesT...> ?
|
||||
this->_construct<TypesT>(v.get<TypesT>()) :
|
||||
((void)0)
|
||||
), ...);
|
||||
_type = v._type;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
/// \param v The variant to move
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
variant(variant&& v) noexcept
|
||||
: _bytes {}
|
||||
, _handle(&_bytes)
|
||||
, _type() {
|
||||
|
||||
if (v._type == nulltype) {
|
||||
return;
|
||||
}
|
||||
|
||||
((v._type == find_element_v<TypesT, TypesT...> ?
|
||||
this->_construct<TypesT>(fennec::move(v.get<TypesT>())) :
|
||||
((void)0)
|
||||
), ...);
|
||||
_type = v._type;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Destructor, if a type is held, destruct it.
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
~variant() {
|
||||
_clear();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment
|
||||
/// @{
|
||||
|
||||
|
||||
///
|
||||
/// \brief value assignment operator
|
||||
/// \tparam T The type to assign
|
||||
/// \param t the value to assign
|
||||
/// \returns a reference to \f$self\f$ after assigning \f$t\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T>
|
||||
variant& operator=(T&& t) {
|
||||
|
||||
// First, check if T is in TypesT...
|
||||
if constexpr((contains_element_v<T, TypesT> or ...)) {
|
||||
using type_t = remove_reference_t<T>;
|
||||
if (_type == find_element_v<type_t, TypesT...>) {
|
||||
*_get<type_t>() = fennec::forward<T>(t);
|
||||
} else {
|
||||
_clear();
|
||||
this->_construct<type_t>(fennec::forward<T>(t));
|
||||
_type = find_element_v<type_t, TypesT...>;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Next, try to assign using the currently held type
|
||||
bool assigned = false;
|
||||
if (_type != nulltype) {
|
||||
((_type == find_element_v<TypesT, TypesT...> ?
|
||||
(*_get<TypesT>() = fennec::forward<T>(t), assigned = true) :
|
||||
((void)0)
|
||||
), ...);
|
||||
}
|
||||
|
||||
if (assigned) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Otherwise, destruct, then construct
|
||||
_clear();
|
||||
using construct_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
|
||||
this->_construct<construct_t>(fennec::forward<T>(t));
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief emplace function
|
||||
/// \tparam T The type to construct
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T, typename...ArgsT> requires(contains_element_v<T, TypesT...>)
|
||||
void emplace(ArgsT&&...args) {
|
||||
_clear();
|
||||
this->_construct<T>(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief deduced emplace function
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param args the argument values
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<size_t I, typename...ArgsT>
|
||||
void emplace(ArgsT&&...args) {
|
||||
using type_t = nth_element_t<I, TypesT...>;
|
||||
_clear();
|
||||
this->_construct<type_t>(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
/// \name Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief get the value of the variant interpreted as \f$T\f$
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T> requires(contains_element_v<T, TypesT...>)
|
||||
T& get() {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<typename T> requires(contains_element_v<T, TypesT...>)
|
||||
const T& get() const {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
|
||||
T& get() {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
///
|
||||
/// \tparam T the type to interpret as
|
||||
/// \returns The value interpreted as \f$T\f$
|
||||
///
|
||||
/// \par Complexity
|
||||
/// \f$O(1)\f$
|
||||
///
|
||||
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
|
||||
const T& get() const {
|
||||
return *_get<T>();
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
byte_t _bytes[size];
|
||||
void* _handle;
|
||||
size_t _type;
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
|
||||
template<typename T>
|
||||
T* _get() const {
|
||||
return static_cast<T*>(_handle);
|
||||
}
|
||||
|
||||
void _clear() {
|
||||
if (_type == nulltype) {
|
||||
return;
|
||||
}
|
||||
|
||||
((_type == find_element_v<TypesT, TypesT...> ?
|
||||
this->_destruct<TypesT>() :
|
||||
((void)0)
|
||||
), ...);
|
||||
_type = nulltype;
|
||||
}
|
||||
|
||||
template<typename ConstructT, typename...ArgsT>
|
||||
void _construct(ArgsT&&...args) {
|
||||
fennec::construct<ConstructT>(_get<ConstructT>(), fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
template<typename DestructT>
|
||||
void _destruct() {
|
||||
fennec::destruct(_get<DestructT>());
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CONTAINERS_VARIANT_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,45 +17,67 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file engine.h
|
||||
/// \file fennec/core/engine.h
|
||||
/// \brief fennec::engine definition
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
///
|
||||
/// \page documentation Documentation
|
||||
/// \page contents Contents
|
||||
///
|
||||
/// 1. \ref introduction "Introduction"
|
||||
/// 1. \ref coding-standards "Coding Standards"
|
||||
/// 2. \ref building-from-source "Building from Source"
|
||||
/// 1. \ref building-from-terminal "Building from Terminal"
|
||||
/// 1. \ref debian "Debian"
|
||||
/// 2. \ref arch "Arch"
|
||||
/// 3. \ref fedora "Fedora"
|
||||
/// 2. \ref building-on-windows "Building on Windows"
|
||||
/// 3. \ref running-the-test-suite "Running the Test Suite"
|
||||
/// 4. \ref usage "Usage"
|
||||
/// 1. \ref licensing "Licensing"
|
||||
/// 5. \ref contribution "Contribution"
|
||||
/// 6. \subpage libraries
|
||||
/// 1. \ref fennec_lang "C++ Language Library"
|
||||
/// 2. \ref fennec_math "Math Library"
|
||||
/// 2. \ref fennec_memory "Memory Management Library"
|
||||
/// 2. \ref fennec_containers "Containers Library"
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
///
|
||||
/// \page libraries Libraries
|
||||
///
|
||||
/// | Library | Brief |
|
||||
/// | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
/// |:---------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
/// | \subpage fennec_lang | Implementation for functions and classes related to the C++ Language, including base types, common utility functions, and metaprogramming templates |
|
||||
/// | \subpage fennec_math | Implementation of math functions according to the [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf). Additional extensions are provided for other common math functions. |
|
||||
/// | \subpage fennec_memory | Implementation of functions related to memory management. |
|
||||
/// | \subpage fennec_containers | Implementation of common data structures, those that are specified in the C++ STD Library, and custom data structures that fennec uses. |
|
||||
///
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
#ifndef FENNEC_CORE_ENGINE_H
|
||||
#define FENNEC_CORE_ENGINE_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class engine {
|
||||
public:
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_ENGINE_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -20,40 +20,148 @@
|
||||
#define FENNEC_CORE_EVENT_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/typeuuid.h>
|
||||
#include <fennec/rtti/enable.h>
|
||||
|
||||
#include <fennec/rtti/typeid.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
struct event;
|
||||
|
||||
///
|
||||
/// \brief Class outlining the interface for an object that listens for events
|
||||
class event_listener {
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
virtual ~event_listener() = default;
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Virtual Destructor
|
||||
virtual ~event_listener();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Event Handling ======================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Event Handling
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief event handler callback
|
||||
/// \param event the event to handle
|
||||
virtual void handle_event(event* event) = 0;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
FENNEC_RTTI_CLASS_ENABLE() {
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Main event interface, includes static methods for registering listeners and dispatching events
|
||||
struct event {
|
||||
const uint64_t type;
|
||||
|
||||
event() = delete;
|
||||
// Constructor & Destructor ============================================================================================
|
||||
public:
|
||||
|
||||
template<typename EventT>
|
||||
event() : type(typeuuid<EventT>()) { }
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
event() = default;
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor
|
||||
event(const event&) = default;
|
||||
|
||||
///
|
||||
/// \brief Move Constructor
|
||||
event(event&&) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief Virtual Destructor
|
||||
virtual ~event() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Static Event System Interface =======================================================================================
|
||||
public:
|
||||
|
||||
/// \name Event System Interface
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Handles the event loop to distribute events.
|
||||
///
|
||||
/// \details Blocking
|
||||
static void handle_events();
|
||||
|
||||
///
|
||||
/// \brief Registers a listener for the event type
|
||||
///
|
||||
/// \details Blocking
|
||||
/// \tparam EventT the event type
|
||||
/// \param listener the listener to register
|
||||
template<typename EventT>
|
||||
static void add_listener(event_listener* listener) {
|
||||
event::add_listener(listener, typeuuid<EventT, event>());
|
||||
event::_add_listener(listener, type::get<EventT>().id());
|
||||
}
|
||||
|
||||
template<typename EventT>
|
||||
static void dispatch(EventT* event) {
|
||||
dispatch(event);
|
||||
}
|
||||
|
||||
static void add_listener(event_listener* listener, uint64_t type);
|
||||
///
|
||||
/// \brief Removes a listener from the event system
|
||||
///
|
||||
/// \details Blocking
|
||||
/// \param listener the listener to remove
|
||||
static void remove_listener(event_listener* listener);
|
||||
static void dispatch(event* event);
|
||||
|
||||
///
|
||||
/// \brief Dispatch an event at the beginning of the next tick.
|
||||
///
|
||||
/// \details Non-Blocking, Lock-Free, Wait-Free
|
||||
/// \tparam EventT The event type
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct the event with
|
||||
template<typename EventT, typename...ArgsT>
|
||||
static void dispatch(ArgsT&&...args) {
|
||||
event::_dispatch(fennec::make_unique<EventT>(fennec::forward<ArgsT>(args)...));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dispatch an event immediately, on the current thread.
|
||||
///
|
||||
/// \details Blocking
|
||||
/// \tparam EventT The event type
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param args The arguments to construct the event with
|
||||
template<typename EventT, typename...ArgsT>
|
||||
static void dispatch_immediate(ArgsT&&...args) {
|
||||
event::_dispatch_immediate(fennec::make_unique<EventT>(fennec::forward<ArgsT>(args)...));
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Helpers =====================================================================================================
|
||||
private:
|
||||
static void _add_listener(event_listener* listener, uint64_t type);
|
||||
static void _handle_event(unique_ptr<event>& event);
|
||||
static void _dispatch(unique_ptr<event>&& event);
|
||||
static void _dispatch_immediate(unique_ptr<event>&& event);
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
FENNEC_RTTI_CLASS_ENABLE() {
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
115
include/fennec/core/logger.h
Normal file
115
include/fennec/core/logger.h
Normal file
@@ -0,0 +1,115 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/core/logger.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_CORE_LOGGER_H
|
||||
#define FENNEC_CORE_LOGGER_H
|
||||
|
||||
#include <fennec/filesystem/file.h>
|
||||
#include <fennec/rtti/singleton.h>
|
||||
#include <fennec/containers/tuple.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief logger class
|
||||
class logger : public singleton<logger> {
|
||||
|
||||
// Logger System Interface =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name System Interface
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Log a string to the log file and cout
|
||||
/// \param str the string to log
|
||||
/// \param _line the line of the log call
|
||||
/// \param _file the file log was called in
|
||||
static void log(const cstring& str,
|
||||
uint32_t _line = FENNEC_BUILTIN_LINE(),
|
||||
const char* _file = FENNEC_BUILTIN_FILE()
|
||||
) {
|
||||
logger& inst = instance();
|
||||
|
||||
if (inst._logfile.is_open()) {
|
||||
inst._logfile.print(cstring(_file, strlen(_file)));
|
||||
inst._logfile.printf("({}): ", _line);
|
||||
inst._logfile.println(str);
|
||||
}
|
||||
|
||||
inst._cout->print(cstring(_file, strlen(_file)));
|
||||
inst._cout->printf("({}): ", _line);
|
||||
inst._cout->println(str);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Log a string to the log file and cout
|
||||
/// \param str the string to log
|
||||
/// \param _line the line of the log call
|
||||
/// \param _file the file log was called in
|
||||
static void log(const string& str,
|
||||
uint32_t _line = FENNEC_BUILTIN_LINE(),
|
||||
const char* _file = FENNEC_BUILTIN_FILE()
|
||||
) {
|
||||
logger& inst = instance();
|
||||
|
||||
if (inst._logfile.is_open()) {
|
||||
inst._logfile.print(cstring(_file, strlen(_file)));
|
||||
inst._logfile.printf("({}): ", _line);
|
||||
inst._logfile.println(str);
|
||||
}
|
||||
|
||||
inst._cout->print(cstring(_file, strlen(_file)));
|
||||
inst._cout->printf("({}): ", _line);
|
||||
inst._cout->println(str);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Private Member Variables ============================================================================================
|
||||
private:
|
||||
file _logfile;
|
||||
file* _cout;
|
||||
|
||||
|
||||
// Private Constructors & Destructor ===================================================================================
|
||||
private:
|
||||
logger();
|
||||
~logger();
|
||||
|
||||
friend struct singleton;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_LOGGER_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#ifndef FENNEC_CORE_SYSTEM_H
|
||||
#define FENNEC_CORE_SYSTEM_H
|
||||
#include <fennec/langproc/strings/string.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
123
include/fennec/core/version.h
Normal file
123
include/fennec/core/version.h
Normal file
@@ -0,0 +1,123 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/core/version.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_CORE_VERSION_H
|
||||
#define FENNEC_CORE_VERSION_H
|
||||
|
||||
#if FENNEC_COMPILER_GCC
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||
#endif
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4201)
|
||||
#endif
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief simple version struct for drivers and systems
|
||||
struct version {
|
||||
|
||||
// Public Member Variables =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Member Variables
|
||||
/// @{
|
||||
|
||||
union {
|
||||
uint64_t num; //!< long version number
|
||||
struct {
|
||||
uint16_t major = { 1 }; //!< the major version
|
||||
uint16_t minor = { 0 }; //!< the minor version
|
||||
uint16_t patch = { 0 }; //!< the patch version
|
||||
uint16_t meta = { 0 }; //!< the meta version, e.g. "rc.1"
|
||||
};
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
// Comparison Operators ================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison Operators
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Equality Operator
|
||||
/// \param lhs The e
|
||||
friend bool operator==(const version& lhs, const version& rhs) {
|
||||
return lhs.num == rhs.num;
|
||||
}
|
||||
|
||||
friend bool operator!=(const version& lhs, const version& rhs) {
|
||||
return lhs.num != rhs.num;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief
|
||||
/// \param lhs
|
||||
/// \param rhs
|
||||
/// \return
|
||||
friend bool operator<(const version& lhs, const version& rhs) {
|
||||
return lhs.num < rhs.num;
|
||||
|
||||
// This generates branching instructions, even in -O4
|
||||
//return lhs.major < rhs.major or(
|
||||
// (lhs.major == rhs.major and lhs.minor < rhs.minor) or (
|
||||
// lhs.minor == rhs.minor and lhs.patch < rhs.patch
|
||||
// )
|
||||
//);
|
||||
}
|
||||
|
||||
friend bool operator>(const version& lhs, const version& rhs) {
|
||||
return lhs.num > rhs.num;
|
||||
}
|
||||
|
||||
/// @}
|
||||
};
|
||||
|
||||
#ifdef FENNEC_COMPILER_GCC
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#if FENNEC_COMPILER_MSVC
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_CORE_VERSION_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,9 +16,9 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#ifndef FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||
#define FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#endif // FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
||||
#endif // FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||
589
include/fennec/filesystem/file.h
Normal file
589
include/fennec/filesystem/file.h
Normal file
@@ -0,0 +1,589 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FILESYSTEM_FILE_H
|
||||
#define FENNEC_FILESYSTEM_FILE_H
|
||||
|
||||
#include <fennec/filesystem/path.h>
|
||||
#include <fennec/format/format.h>
|
||||
|
||||
#include <fennec/string/cstring.h>
|
||||
#include <fennec/string/string.h>
|
||||
#include <fennec/string/wstring.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Mode flags for opening a file
|
||||
///
|
||||
/// fmode_binary and fmode_wide are independent of the other modes
|
||||
///
|
||||
/// \details Valid Flag Combinations
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_LANGPROC_io_fmode">
|
||||
/// <tr><th style="vertical-align: top">Flags
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$read\f$
|
||||
/// <td style="vertical-align: top">Opens file as read-only, reading from start
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$write\f$
|
||||
/// <td style="vertical-align: top">Opens file as write-only, writing to end
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$read | write\f$
|
||||
/// <td style="vertical-align: top">Opens file as read-write, reading from start
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$write | trunc\f$
|
||||
/// <td style="vertical-align: top">Opens file as write-only, destroying contents
|
||||
///
|
||||
/// <tr><td style="vertical-align: top">\f$read | write | trunc\f$
|
||||
/// <td style="vertical-align: top">Opens file as read-write, destroying contents
|
||||
/// </table>
|
||||
enum fmode_ : uint8_t
|
||||
{
|
||||
fmode_read = 0b00000001 //!< Opens file for reading
|
||||
, fmode_write = 0b00000010 //!< Opens file for writing
|
||||
, fmode_trunc = 0b00000100 //!< Contents of the file will be destroyed, only compatible with write enabled modes
|
||||
, fmode_exclusive = 0b00001000 //!< Generates an error if the opened file is not empty
|
||||
, fmode_binary = 0b00010000 //!< Open in binary mode
|
||||
, fmode_wide = 0b00100000 //!< Opens a file in wide mode
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Structure for handling streams of data
|
||||
///
|
||||
/// \details operations, when errored, will return a corresponding error.
|
||||
/// Use file::get_error() to check if an error is present and return a corresponding string.
|
||||
/// Use file::clear_error() to clear the errored state.
|
||||
/// Some operations, specifically file::rename() and file::copy().
|
||||
/// <br>
|
||||
/// This file paradigm is to subvert time-of-check time-of-use (TOCTOU) attacks. This involves a threat actor
|
||||
/// reading that our application checks if a file exists, and replacing the file with another malicious file
|
||||
/// or symlink between our call to open the file.
|
||||
class file
|
||||
{
|
||||
// Constants ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constants
|
||||
/// @{
|
||||
|
||||
/// \brief value of an invalid position
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Validation Functions ================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Validation Functions
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Check if the provided mode bitflags are a valid combination
|
||||
/// \param mode the bitfield
|
||||
/// \returns true if the combination of flags is valid, false otherwise
|
||||
static constexpr bool is_valid(uint8_t mode) {
|
||||
const bool t = mode & fmode_trunc;
|
||||
const bool x = mode & fmode_exclusive;
|
||||
const bool w = mode & fmode_write;
|
||||
|
||||
// when x is true, t must be true
|
||||
// when t is true, w must be true
|
||||
return (t && x && w)
|
||||
|| (t && w)
|
||||
|| !(t || x);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// STD Text Streams ====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name STD Text Streams
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns the c stdout stream
|
||||
static file& cout();
|
||||
|
||||
///
|
||||
/// \returns the c stdin stream
|
||||
static file& cin();
|
||||
|
||||
///
|
||||
/// \returns the c stderr stream
|
||||
static file& cerr();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default constructor
|
||||
/// \details Initializes an empty stream
|
||||
file();
|
||||
|
||||
///
|
||||
/// \details Initializes a stream pointing to \f$path\f$ opened with \f$mode\f$
|
||||
/// \param path the path of the file
|
||||
/// \param mode the mode to open with
|
||||
file(const cstring& path, uint8_t mode)
|
||||
: file() {
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
///
|
||||
/// \details Initializes a stream pointing to \f$path\f$ opened with \f$mode\f$
|
||||
/// \param path the path of the file
|
||||
/// \param mode the mode to open with
|
||||
file(const string& path, uint8_t mode)
|
||||
: file() {
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path constructor
|
||||
/// \details Initializes a stream pointing to \f$path\f$ opened with \f$mode\f$
|
||||
/// \param path the path of the file
|
||||
/// \param mode the mode to open with
|
||||
file(const path& path, uint8_t mode)
|
||||
: file() {
|
||||
open(path, mode);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move constructor
|
||||
/// \param file the stream to take ownership of
|
||||
file(file&& file) noexcept;
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
/// \details Flushes and closes an open stream
|
||||
~file();
|
||||
|
||||
///
|
||||
/// \brief Move assignment
|
||||
/// \param file the stream to take ownership of
|
||||
file& operator=(file&& file) noexcept;
|
||||
|
||||
///@}
|
||||
|
||||
private:
|
||||
// don't allow copying streams
|
||||
file(const file&) = delete;
|
||||
file& operator=(const file&) = delete;
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Properties
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns The path the stream
|
||||
const path& get_path() const {
|
||||
return _path;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The mode of the stream
|
||||
uint8_t mode() const {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if there is a valid, open stream.
|
||||
bool is_open() const {
|
||||
return _handle != nullptr;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// File Access =========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name File Access
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \param path the path to the file
|
||||
/// \param mode the mode flags to open the file with
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool open(const cstring& path, uint8_t mode);
|
||||
|
||||
///
|
||||
/// \param path the path to the file
|
||||
/// \param mode the mode flags to open the file with
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool open(const string& path, uint8_t mode);
|
||||
|
||||
///
|
||||
/// \brief Open a file
|
||||
/// \param path the path to the file
|
||||
/// \param mode the mode flags to open the file with
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool open(const path& path, uint8_t mode);
|
||||
|
||||
///
|
||||
/// \brief Close a stream
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool close();
|
||||
|
||||
///
|
||||
/// \brief Commit the streams buffer to the file
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool commit();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// File Operations =====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name File Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief closes the stream and erases the file
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
bool erase();
|
||||
|
||||
///
|
||||
/// \param path the new path
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
///
|
||||
/// \details
|
||||
/// Copies contents to the new path, and erases the old file.
|
||||
/// * Attempts to open a write-only stream at path
|
||||
/// * Attempts to reopen this file as read-only
|
||||
/// * Copies the contents of this file to the new stream
|
||||
/// * Reopen the new stream with the flags of this file and binds to it
|
||||
/// * Closes the old file
|
||||
bool rename(const cstring& path);
|
||||
|
||||
///
|
||||
/// \param path the new path
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
///
|
||||
/// \details
|
||||
/// Copies contents to the new path, and erases the old file.
|
||||
/// * Attempts to open a write-only stream at path
|
||||
/// * Attempts to reopen this file as read-only
|
||||
/// * Copies the contents of this file to the new stream
|
||||
/// * Reopen the new stream with the flags of this file and binds to it
|
||||
/// * Closes the old file
|
||||
bool rename(const string& path);
|
||||
|
||||
///
|
||||
/// \brief Rebind the stream.
|
||||
/// \param path the new path
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ on error
|
||||
///
|
||||
/// \details
|
||||
/// Copies contents to the new path, and erases the old file.
|
||||
/// * Attempts to open a write-only stream at path
|
||||
/// * Attempts to reopen this file as read-only
|
||||
/// * Copies the contents of this file to the new stream
|
||||
/// * Reopen the new stream with the flags of this file and binds to it
|
||||
/// * Closes the old file
|
||||
bool rename(const path& path);
|
||||
|
||||
///
|
||||
/// \param path the path to copy to
|
||||
/// \returns a file at the new path with the copied contents
|
||||
///
|
||||
/// \details
|
||||
/// Copies the contents to a new stream at the provided path.
|
||||
/// * Attempts to open a write-only stream at path, <br>
|
||||
/// * Attempts to reopen this file as read-only, <br>
|
||||
/// * Copies the contents of this file to the new stream, <br>
|
||||
/// * Reopen the new stream with the flags of this file.
|
||||
file copy(const cstring& path);
|
||||
|
||||
///
|
||||
/// \param path the path to copy to
|
||||
/// \returns a file at the new path with the copied contents
|
||||
///
|
||||
/// \details
|
||||
/// Copies the contents to a new stream at the provided path.
|
||||
/// * Attempts to open a write-only stream at path, <br>
|
||||
/// * Attempts to reopen this file as read-only, <br>
|
||||
/// * Copies the contents of this file to the new stream, <br>
|
||||
/// * Reopen the new stream with the flags of this file.
|
||||
file copy(const string& path);
|
||||
|
||||
///
|
||||
/// \brief Copy contents to a new file.
|
||||
/// \details Copies the contents of the current stream into a new stream bound to the file at the provided path.
|
||||
/// \param path the path to copy to
|
||||
/// \returns a file at the new path with the copied contents
|
||||
///
|
||||
/// \details
|
||||
/// Copies the contents to a new stream at the provided path.
|
||||
/// * Attempts to open a write-only stream at path, <br>
|
||||
/// * Attempts to reopen this file as read-only, <br>
|
||||
/// * Copies the contents of this file to the new stream, <br>
|
||||
/// * Reopen the new stream with the flags of this file.
|
||||
file copy(const path& path);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// File Positioning ====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name File Positioning
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns the position index in the stream
|
||||
size_t get_pos() const;
|
||||
|
||||
///
|
||||
/// \param i the new index to move to
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool set_pos(size_t i);
|
||||
|
||||
///
|
||||
/// \brief return to the start of the stream
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool rewind();
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if the stream has reached the end of the file, \f$false\f$ otherwise
|
||||
bool eof() const;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Binary Read Operations ==============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Binary Read Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief binary read
|
||||
/// \param data the buffer to write to
|
||||
/// \param size the size of each object in bytes
|
||||
/// \param n the number of objects to read
|
||||
/// \returns the number of objects successfully read
|
||||
size_t read(void* data, size_t size, size_t n);
|
||||
|
||||
///
|
||||
/// \brief type read
|
||||
/// \tparam T the type to read
|
||||
/// \param data the buffer to write to
|
||||
/// \param n the number of objects to read
|
||||
/// \returns the number of objects successfully read
|
||||
template<typename T>
|
||||
size_t read(T* data, size_t n) {
|
||||
return read(static_cast<void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief type read
|
||||
/// \tparam T the type to read
|
||||
/// \tparam n the number of objects to read
|
||||
/// \param data the buffer to write to
|
||||
/// \returns the number of objects successfully read
|
||||
template<typename T, size_t n>
|
||||
size_t read(T (&data)[n]) {
|
||||
return read(static_cast<void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Binary Write Operations =============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Binary Write Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief put a character at the current position in the stream
|
||||
/// \param c the character to put
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool putc(char c);
|
||||
|
||||
///
|
||||
/// \brief put a wide character at the current position in the stream
|
||||
/// \param c the character to put
|
||||
/// \returns \f$false\f$ on success, \f$true\f$ otherwise
|
||||
bool putwc(wchar_t c);
|
||||
|
||||
///
|
||||
/// \brief write a buffer to at the current position in the stream
|
||||
/// \param data the buffer to read from
|
||||
/// \param size the size of each object in bytes
|
||||
/// \param n the number of objects to write
|
||||
/// \returns the number of objects successfully written
|
||||
size_t write(const void* data, size_t size, size_t n);
|
||||
|
||||
///
|
||||
/// \brief write a character buffer to at the current position in the stream
|
||||
/// \tparam n the number of characters to write
|
||||
/// \param data the buffer to read from
|
||||
/// \returns the number of objects successfully written
|
||||
template<size_t n>
|
||||
size_t write(const char (&data)[n]) {
|
||||
return write(data, sizeof(char), n - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief write a wide character buffer to at the current position in the stream
|
||||
/// \tparam n the number of characters to write
|
||||
/// \param data the buffer to read from
|
||||
/// \returns the number of objects successfully written
|
||||
template<size_t n>
|
||||
size_t write(const wchar_t (&data)[n]) {
|
||||
return write(data, sizeof(wchar_t), n - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief write a buffer to at the current position in the stream
|
||||
/// \tparam T the object type to write
|
||||
/// \param data the buffer to read from
|
||||
/// \param n the number of objects to write
|
||||
/// \returns the number of objects successfully written
|
||||
template<typename T>
|
||||
size_t write(const T* data, size_t n) {
|
||||
return write(static_cast<const void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief write a buffer to at the current position in the stream
|
||||
/// \tparam T the object type to write
|
||||
/// \tparam n the number of objects to write
|
||||
/// \param data the buffer to read from
|
||||
/// \returns the number of objects successfully written
|
||||
template<typename T, size_t n>
|
||||
size_t write(const T (&data)[n]) {
|
||||
return write(static_cast<const void*>(data), sizeof(T), n);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Read Operations =====================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Read Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \returns the character read at the current position in the stream
|
||||
/// \details Advances the position by one character
|
||||
char getc();
|
||||
|
||||
///
|
||||
/// \returns the wide character read at the current position in the stream
|
||||
/// \details Advances the position by one wide character
|
||||
wchar_t getwc();
|
||||
|
||||
///
|
||||
/// \returns A string containing the characters from the current position to the next newline character
|
||||
/// \details Advances the position to the character following the next newline character
|
||||
string getline();
|
||||
|
||||
///
|
||||
/// \returns A wide string containing the characters from the current position to the next newline character
|
||||
/// \details Advances the position to the character following the next newline character
|
||||
wstring getwline();
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Printing Operations =================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Print Operations
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \param str the string to print
|
||||
void print(const cstring& str);
|
||||
|
||||
///
|
||||
/// \brief print a string to the stream
|
||||
/// \param str the string to print
|
||||
void print(const string& str);
|
||||
|
||||
///
|
||||
/// \param str the string to print
|
||||
void println(const cstring& str);
|
||||
|
||||
///
|
||||
/// \brief print a string to the stream followed by a newline character
|
||||
/// \param str the string to print
|
||||
void println(const string& str);
|
||||
|
||||
///
|
||||
/// \brief print a formatted string to the stream
|
||||
/// \tparam ArgsT the argument types
|
||||
/// \param str the format string
|
||||
/// \param args the argument values
|
||||
template<typename...ArgsT>
|
||||
void printf(const cstring& str, ArgsT&&...args) {
|
||||
string fmt = fennec::format(str, fennec::forward<ArgsT>(args)...);
|
||||
this->print(cstring(fmt.cstr(), fmt.length()));
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
|
||||
|
||||
// Error Handling ======================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Error Handling
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Returns the current error state.
|
||||
/// \returns A string containing the current error.
|
||||
cstring get_error() const { return { _error, ::strlen(_error) }; }
|
||||
|
||||
///
|
||||
/// \brief clears the errored state
|
||||
void clear_error() { _error = nullptr; }
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
FILE* _handle;
|
||||
path _path;
|
||||
uint8_t _mode;
|
||||
char* _error;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FILESYSTEM_FILE_H
|
||||
438
include/fennec/filesystem/path.h
Normal file
438
include/fennec/filesystem/path.h
Normal file
@@ -0,0 +1,438 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FILESYSTEM_PATH_H
|
||||
#define FENNEC_FILESYSTEM_PATH_H
|
||||
|
||||
#include <fennec/filesystem/path.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief struct for handling file paths
|
||||
///
|
||||
/// \details This structure makes no guarantees about the validity of a path.
|
||||
/// Operations do not examine the system's file structure.
|
||||
struct path {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
|
||||
class iterator;
|
||||
friend class iterator;
|
||||
|
||||
|
||||
// Current Working Director ============================================================================================
|
||||
public:
|
||||
|
||||
/// \name Current Working Directory
|
||||
/// @{
|
||||
|
||||
/// \brief Get the current working directory
|
||||
/// \returns a path containing the absolute path to the working directory
|
||||
static path get_current();
|
||||
|
||||
/// \brief Set the current working directory
|
||||
/// \param path the path to the new working directory
|
||||
/// \returns a path containing the absolute path to the working directory
|
||||
static path set_current(const path& path);
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Constructors & Destructor ===========================================================================================
|
||||
public:
|
||||
|
||||
/// \name Constructors & Destructor
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, returns the root of the current working directory
|
||||
path() : _str("/") { }
|
||||
|
||||
///
|
||||
/// \brief C-String Conversion Constructor
|
||||
/// \param str the cstring to convert
|
||||
path(const cstring& str)
|
||||
: _str(str) {
|
||||
if (str.size() > 2 && str[str.size() - 1] == '/') {
|
||||
_str = _str.substring(0, str.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief String Conversion Constructor
|
||||
/// \param str the string to convert
|
||||
path(const string& str)
|
||||
: _str(str) {
|
||||
if (str.size() > 2 && str[str.size() - 1] == '/') {
|
||||
_str = _str.substring(0, str.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Copy Constructor
|
||||
/// \param p the path to copy
|
||||
path(const path& p)
|
||||
: _str(p._str) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Move Constructor
|
||||
/// \param p the path to take ownership of
|
||||
path(path&& p) noexcept : _str(move(p._str)) { }
|
||||
|
||||
///
|
||||
/// \brief Destructor
|
||||
~path() = default;
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Assignment Operators ================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Assignment
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C-String Assignment Operator
|
||||
/// \param str the cstring to assign
|
||||
/// \returns a reference to \f$this\f$ after assigning \f$p\f$
|
||||
template<size_t n>
|
||||
path& operator=(const char (&str)[n]) {
|
||||
_str = str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C-String Assignment Operator
|
||||
/// \param p the cstring to assign
|
||||
/// \returns a reference to \f$this\f$ after assigning \f$p\f$
|
||||
path& operator=(const cstring& p) {
|
||||
_str = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief String Assignment Operator
|
||||
/// \param p the cstring to assign
|
||||
/// \returns a reference to \f$this\f$ after assigning \f$p\f$
|
||||
path& operator=(const string& p) {
|
||||
_str = p;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Copy Assignment Operator
|
||||
/// \param p the path to copy
|
||||
/// \returns a reference to \f$this\f$ after copying \f$p\f$
|
||||
path& operator=(const path& p) {
|
||||
_str = p._str;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Path Move Assignment Operator
|
||||
/// \param p the path to take ownership of
|
||||
/// \returns a reference to \f$this\f$ after taking ownership of \f$p\f$
|
||||
path& operator=(path&& p) noexcept {
|
||||
_str = move(p._str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Comparison
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief path equality operator
|
||||
/// \param p the path to compare against
|
||||
/// \returns \f$true\f$ if the paths are identical, \f$false\f$ otherwise. relative paths are not resolved
|
||||
bool operator==(const path& p) const {
|
||||
return _str == p._str;
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
// Modifiers ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Modifiers
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief path append operator
|
||||
/// \param str the filename to append
|
||||
/// \returns a path containing the current path followed by \f$str\f$
|
||||
path operator/(const cstring& str) const {
|
||||
return path(_str + '/' + str);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief path append operator
|
||||
/// \param str the filename to append
|
||||
/// \returns a path containing the current path followed by \f$str\f$
|
||||
path operator/(const string& str) const {
|
||||
return path(_str + '/' + str);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief path append operator
|
||||
/// \param p the path to append
|
||||
/// \returns a path containing the current path followed by \f$p\f$
|
||||
path operator/(const path& p) const {
|
||||
return path(_str + '/' + p._str);
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
///
|
||||
/// \brief the filename of the current path
|
||||
/// \returns a string containing a copy of the filename
|
||||
string filename() const {
|
||||
size_t i = _str.rfind('/');
|
||||
return _str.substring(i + 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns the underlying string representation
|
||||
const string& str() const { return _str; }
|
||||
|
||||
///
|
||||
/// \returns the underlying C-Style string representation
|
||||
const char* cstr() const { return _str.cstr(); }
|
||||
|
||||
///
|
||||
/// \returns \f$true\f$ if the path is empty or points to root
|
||||
bool is_empty() {
|
||||
size_t size = _str.size();
|
||||
if (size == 0) return true;
|
||||
#if FENNEC_PLATFORM_WINDOWS
|
||||
return (_str[1] == ':' && size == 3);
|
||||
#else
|
||||
return (_str[0] == '/' && size == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns a copy of the path with the filename removed
|
||||
path parent() const {
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
size_t start = _str.size() - 1;
|
||||
start = _str[start] == '/' || _str[start] == '\\' ? start - 1 : start;
|
||||
|
||||
size_t r = _str.rfind('/', start);
|
||||
size_t l = _str.rfind('\\', start);
|
||||
if (r == _str.size()) {
|
||||
start = l;
|
||||
}
|
||||
else if (l == _str.size()) {
|
||||
start = r;
|
||||
}
|
||||
else {
|
||||
start = max(r, l);
|
||||
}
|
||||
return _str.substring(0, start);
|
||||
#else
|
||||
size_t start = _str.size();
|
||||
start = _str[start] == '/' ? start - 1 : start;
|
||||
return path(_str.substring(0, _str.rfind('/', start)));
|
||||
#endif
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief absolute path
|
||||
/// \returns a copy of the path with relative paths resolved
|
||||
path absolute() const {
|
||||
path parse = *this;
|
||||
path working; working._str.resize(0);
|
||||
|
||||
// Check if this is a rooted path;
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
if (_str[1] != ':') {
|
||||
#else
|
||||
if (_str[0] != '/') {
|
||||
#endif
|
||||
working = get_current();
|
||||
}
|
||||
|
||||
while (not parse.is_empty()) {
|
||||
// Handle dots
|
||||
while (not parse.is_empty() && parse._str[0] == '.') {
|
||||
// Check for ".."
|
||||
if (parse._str[1] == '.') {
|
||||
// ".."
|
||||
if (parse._str.size() == 2) {
|
||||
parse = path();
|
||||
working = working.parent();
|
||||
}
|
||||
// "../"
|
||||
else if (parse._str[2] == '/') {
|
||||
working = working.parent();
|
||||
parse._str = parse._str.substring(3);
|
||||
}
|
||||
}
|
||||
// "./"
|
||||
else if (parse._str[1] == '/') {
|
||||
parse._str = parse._str.substring(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (parse.is_empty()) break;
|
||||
|
||||
// Push the path
|
||||
const size_t loc = parse._str.find('/');
|
||||
working._str += '/';
|
||||
working._str += parse._str.substring(0, loc);
|
||||
parse._str = parse._str.substring(loc + 1);
|
||||
}
|
||||
|
||||
return working;
|
||||
}
|
||||
|
||||
|
||||
// Iteration ===========================================================================================================
|
||||
public:
|
||||
|
||||
/// \name Iteration
|
||||
/// @{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \returns an iterator at the first filename in the path
|
||||
iterator begin() const {
|
||||
return iterator(this, 0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \returns an iterator to the end of the path
|
||||
iterator end() const {
|
||||
return iterator(this, _str.size());
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$iterator\f$
|
||||
class iterator {
|
||||
public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param it the iterator to copy
|
||||
constexpr iterator(const iterator& it) = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param it the iterator to move
|
||||
constexpr iterator(iterator&& it) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief dereference operator
|
||||
/// \returns copy of the current file in the path
|
||||
constexpr string operator*() const {
|
||||
if ((*_str)[_pos] == '/') {
|
||||
return string("");
|
||||
}
|
||||
|
||||
size_t e = _str->find('/', _pos);
|
||||
return _str->substring(_pos, e - _pos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief prefix increment operator
|
||||
/// \returns \f$self\f$ after having moved to the next element in the list
|
||||
constexpr iterator& operator++() {
|
||||
_pos = min(_str->find('/', _pos) + 1, _str->size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief postfix increment operator
|
||||
/// \returns \f$self\f$ before having moved to the next element in the list
|
||||
constexpr iterator operator++(int) {
|
||||
iterator it = *this;
|
||||
this->operator++();
|
||||
return it;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator equality operator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are identical, \f$false\f$ otherwise
|
||||
constexpr bool operator==(const iterator& rhs) const {
|
||||
return _str == rhs._str and _pos == rhs._pos;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief iterator inequality operator
|
||||
/// \param rhs the iterator to compare with
|
||||
/// \returns \f$true\f$ if the iterators are different, \f$false\f$ otherwise
|
||||
constexpr bool operator!=(const iterator& rhs) const {
|
||||
return _str != rhs._str or _pos != rhs._pos;
|
||||
}
|
||||
|
||||
private:
|
||||
const string* _str;
|
||||
size_t _pos;
|
||||
|
||||
constexpr iterator(const path* path, size_t p)
|
||||
: _str(&path->_str)
|
||||
, _pos(p) {
|
||||
|
||||
// Handle end()
|
||||
if (p == _str->size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle rooted paths
|
||||
#ifdef FENNEC_PLATFORM_WINDOWS
|
||||
if ((*_str)[1] == ':') {
|
||||
_pos = max(_pos, size_t(3));
|
||||
}
|
||||
#else
|
||||
if ((*_str)[0] == '/') {
|
||||
_pos = max(_pos, size_t(1));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Ensure we are at the start of a directory/file name
|
||||
if (_pos != 0 && (*_str)[_pos - 1] != '/') {
|
||||
_pos = _str->find('/', _pos) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
friend struct path;
|
||||
};
|
||||
|
||||
/// @}
|
||||
|
||||
private:
|
||||
string _str;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FILESYSTEM_PATH_H
|
||||
201
include/fennec/format/charconv.h
Normal file
201
include/fennec/format/charconv.h
Normal file
@@ -0,0 +1,201 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/charconv.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_CHARCONV_H
|
||||
#define FENNEC_FORMAT_CHARCONV_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, char x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed char x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned char x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed short x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned short x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed int x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned int x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed long x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned long x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed long long x, int base);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned long long x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, signed long long x, int base);
|
||||
|
||||
///
|
||||
/// \brief integer to ascii
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param base the base to interpret the integer as
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, unsigned long long x, int base);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, float x);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, float x, char fmt);
|
||||
|
||||
///
|
||||
/// \brief float to ascii
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \param precision the number of decimal places to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, float x, char fmt, int precision);
|
||||
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, double x);
|
||||
|
||||
///
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, double x, char fmt);
|
||||
|
||||
///
|
||||
/// \brief float to ascii
|
||||
/// \param first the start of the buffer
|
||||
/// \param last the end of the buffer
|
||||
/// \param x the value to write
|
||||
/// \param fmt the float string format to use
|
||||
/// \param precision the number of decimal places to write
|
||||
/// \returns a pointer to one past the last written character
|
||||
char* to_chars(char* first, char* last, double x, char fmt, int precision);
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_CHARCONV_H
|
||||
152
include/fennec/format/detail/_format.h
Normal file
152
include/fennec/format/detail/_format.h
Normal file
@@ -0,0 +1,152 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_FORMAT_DETAIL_FORMAT_H
|
||||
#define FENNEC_FORMAT_DETAIL_FORMAT_H
|
||||
|
||||
#include <fennec/memory/pointers.h>
|
||||
#include <fennec/format/formatter.h>
|
||||
#include <fennec/format/format_arg.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
// Impl interface for templated polymorphism fuckery
|
||||
struct _format_argimpl {
|
||||
_format_argimpl() {};
|
||||
virtual ~_format_argimpl() {};
|
||||
virtual string format(const format_arg& fmt) = 0;
|
||||
virtual bool is_integer() = 0;
|
||||
virtual int64_t int_value() = 0;
|
||||
};
|
||||
|
||||
// Polymorphic template specialization
|
||||
template<typename T>
|
||||
struct _format_arg : _format_argimpl {
|
||||
formatter<T> fmtr;
|
||||
const T& val;
|
||||
_format_arg(const T& arg) : val(arg) {
|
||||
}
|
||||
virtual ~_format_arg() = default;
|
||||
string format(const format_arg& fmt) override {
|
||||
return fennec::forward<string>(fmtr(fmt, val));
|
||||
}
|
||||
|
||||
bool is_integer() override {
|
||||
return is_integral_v<T> or is_convertible_v<T, int64_t>;
|
||||
}
|
||||
virtual int64_t int_value() override {
|
||||
if constexpr(is_integral_v<T>) {
|
||||
return val;
|
||||
} else if constexpr(is_convertible_v<T, int64_t>) {
|
||||
return val;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Polymorphic template specialization for x/r/xr value references
|
||||
template<typename T>
|
||||
struct _format_arg<T&> : _format_arg<T> {
|
||||
_format_arg(const T& arg) : _format_arg<T>(arg) {
|
||||
}
|
||||
};
|
||||
|
||||
// Polymorphic template specialization for x/r/xr value references
|
||||
template<typename T>
|
||||
struct _format_arg<const T> : _format_arg<T> {
|
||||
_format_arg(const T& arg) : _format_arg<T>(arg) {
|
||||
}
|
||||
};
|
||||
|
||||
// Polymorphic template specialization for x/r/xr value references
|
||||
template<typename T>
|
||||
struct _format_arg<const T&> : _format_arg<T> {
|
||||
_format_arg(const T& arg) : _format_arg<T>(arg) {
|
||||
}
|
||||
};
|
||||
|
||||
// Containing array for format args
|
||||
template<size_t N>
|
||||
struct _format_argarray {
|
||||
array<unique_ptr<_format_argimpl>, N> args;
|
||||
template<typename...ArgsT>
|
||||
_format_argarray(ArgsT&&...args)
|
||||
: args { unique_ptr<_format_argimpl>(new _format_arg<ArgsT>(fennec::forward<ArgsT>(args)))... } {
|
||||
}
|
||||
|
||||
string format(size_t i, const format_arg& fmt) {
|
||||
return args[i]->format(fmt);
|
||||
}
|
||||
|
||||
bool is_integer(size_t i) {
|
||||
return args[i]->is_integer();
|
||||
}
|
||||
|
||||
int64_t int_value(size_t i) {
|
||||
return args[i]->int_value();
|
||||
}
|
||||
};
|
||||
|
||||
// checks if character is a valid format type
|
||||
constexpr bool _isfmt_t(char c) {
|
||||
switch (c) {
|
||||
default: return false;
|
||||
case 's': case '?': // strings
|
||||
case 'c': // char
|
||||
case 'd': // decimal
|
||||
case 'b': case 'B': // binary
|
||||
case 'o': // octal
|
||||
case 'x': case 'X': // hex
|
||||
case 'a': case 'A': // float hex
|
||||
case 'e': case 'E': // scientific notation
|
||||
case 'f': case 'F': // fixed precision
|
||||
case 'g': case 'G': // general precision
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// checks if character is a valid format int type
|
||||
constexpr bool _isfmt_i(char c) {
|
||||
switch (c) {
|
||||
default: return false;
|
||||
case 'd': // decimal
|
||||
case 'b': case 'B': // binary
|
||||
case 'o': // octal
|
||||
case 'x': case 'X': // hex
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// checks if character is a valid format float type
|
||||
constexpr bool _isfmt_f(char c) {
|
||||
switch (c) {
|
||||
default: return false;
|
||||
case 'a': case 'A': // float hex
|
||||
case 'e': case 'E': // scientific notation
|
||||
case 'f': case 'F': // fixed precision
|
||||
case 'g': case 'G': // general precision
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_DETAIL_FORMAT_H
|
||||
384
include/fennec/format/format.h
Normal file
384
include/fennec/format/format.h
Normal file
@@ -0,0 +1,384 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/format.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_FORMAT_H
|
||||
#define FENNEC_FORMAT_FORMAT_H
|
||||
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
#include <fennec/format/detail/_format.h>
|
||||
#include <fennec/format/formatter.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief C++ 20 format specification
|
||||
/// \tparam ArgsT The argument types
|
||||
/// \param str The format string
|
||||
/// \param args The argument values
|
||||
/// \returns A formatted string using the C++20 format specification
|
||||
template<typename...ArgsT>
|
||||
string format(const cstring& str, ArgsT&&...args) {
|
||||
static constexpr size_t argc = sizeof...(ArgsT);
|
||||
static constexpr format_arg default_fmt = {
|
||||
.fill = ' ',
|
||||
.align = '\0', // default to locale
|
||||
.sign = '\0', // default to sign only for negative numbers, gets handled later in code
|
||||
.alt = false, // default no prefix
|
||||
.upper = false,
|
||||
.width = 0,
|
||||
.precision = 6, // default to 6 sigfigs
|
||||
.base = 10,
|
||||
.type = '\0',
|
||||
};
|
||||
|
||||
// empty case
|
||||
if constexpr(argc == 0) {
|
||||
return str;
|
||||
}
|
||||
|
||||
detail::_format_argarray<argc> argarray = { fennec::forward<ArgsT>(args)... };
|
||||
string res;
|
||||
size_t i = 0;
|
||||
size_t arg_c = -1;
|
||||
|
||||
while (i <= str.length()) {
|
||||
size_t brace = str.find('{', i);
|
||||
size_t end = str.find('}', i);
|
||||
format_arg fmt = default_fmt;
|
||||
|
||||
// check for '}}'
|
||||
if (end < brace) {
|
||||
if (str[end + 1] == '}') {
|
||||
res += string(str.data() + i, end - i);
|
||||
i = end + 2;
|
||||
continue;
|
||||
}
|
||||
assertf(false, "fennec::format syntax error, encountered unexpected '{'")
|
||||
}
|
||||
|
||||
// append string
|
||||
if (brace >= str.length()) { // handle end case
|
||||
res += string(str.data() + i, str.length() - i);
|
||||
break;
|
||||
}
|
||||
res += string(str.data() + i, brace - i);
|
||||
|
||||
// next brace, validate escape
|
||||
size_t next_brace = str.find('{', brace + 1);
|
||||
if (brace + 1 == next_brace) {
|
||||
res += '{';
|
||||
i = next_brace + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// find contained colon
|
||||
size_t colon = str.find(':', brace);
|
||||
|
||||
// validate colon and brace location
|
||||
assertf(colon < next_brace or end < next_brace, "fennec::format syntax error, mismatched '{}'");
|
||||
|
||||
// parse index if present
|
||||
size_t id = min(colon, end) - 1;
|
||||
if (id > brace) {
|
||||
arg_c = 0;
|
||||
} else {
|
||||
++arg_c;
|
||||
}
|
||||
for (size_t j = id, k = 1; j > brace; --j, k *= 10) {
|
||||
size_t u = (str[j] - '0');
|
||||
assertf(u < 10, "fennec::format syntax error, invalid argument index");
|
||||
arg_c += k * u;
|
||||
}
|
||||
|
||||
// store argument to allow nested replacement fields
|
||||
size_t arg = arg_c;
|
||||
|
||||
// validate index
|
||||
assertf(arg < argc, "fennec::format syntax error, invalid argument index");
|
||||
|
||||
// early return case for no colon
|
||||
if (colon > end) {
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// parse format specifiers
|
||||
|
||||
// we're going to parse right-to-left since the valid combinations
|
||||
// of specifiers change based on the type of the argument
|
||||
|
||||
// to compensate for this, the nested replacement fields need to be computed in this loop
|
||||
// (nested replacement deduced)
|
||||
size_t nrfd = 0;
|
||||
size_t nnrf = 0;
|
||||
|
||||
// first find the matching '}' brace, e is not necessarily the matching brace
|
||||
// since some specifiers allow nested replacement fields
|
||||
size_t parse = colon;
|
||||
while (str[parse + 1] != '}') {
|
||||
if (next_brace < end) { // if the next brace is before the next closing brace
|
||||
++nnrf;
|
||||
nrfd += str[end - 1] == '{';
|
||||
parse = end + 1;
|
||||
end = str.find('}', parse);
|
||||
next_brace = str.find('{', parse);
|
||||
} else {
|
||||
parse = end - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assertf(nrfd <= 2 and parse < str.length() - 1 and str[parse + 1] == '}',
|
||||
"fennec::format syntax error, mismatched '{}'");
|
||||
|
||||
// check type
|
||||
switch (str[parse]) {
|
||||
default: break;
|
||||
|
||||
case 's': case '?': // strings
|
||||
case 'c': // char
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
|
||||
case 'd': // decimal
|
||||
fmt.base = 10;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'B': // binary
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'b':
|
||||
fmt.base = 2;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'o': // octal
|
||||
fmt.base = 8;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'X': // hex
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'x':
|
||||
fmt.base = 16;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
|
||||
case 'A':
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'a': // float hex
|
||||
fmt.base = 16;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'E': // scientific notation
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'e':
|
||||
fmt.base = 16;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'F': // fixed precision
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'f':
|
||||
fmt.base = 10;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
|
||||
case 'G': // general precision
|
||||
fmt.upper = true; [[fallthrough]];
|
||||
case 'g':
|
||||
fmt.base = 10;
|
||||
fmt.type = str[parse--];
|
||||
break;
|
||||
}
|
||||
|
||||
// early return
|
||||
if (parse == colon) {
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// search for width and precision
|
||||
size_t x = 0, j = 1;
|
||||
bool found_decimal = false;
|
||||
size_t num_decimals = 0;
|
||||
bool is_float_t = detail::_isfmt_f(fmt.type);
|
||||
bool is_str_t = fmt.type == 's';
|
||||
bool is_integer_t = detail::_isfmt_i(fmt.type);
|
||||
bool ded_width_f = false;
|
||||
bool ded_width = false;
|
||||
size_t ded_temp_i = 0;
|
||||
|
||||
// default "precision" for strings should be 0 for no limit
|
||||
if (is_str_t) {
|
||||
fmt.precision = 0;
|
||||
}
|
||||
|
||||
// parse width and precision
|
||||
while (isdigit(str[parse]) or (found_decimal = (str[parse] == '.')) or str[parse] == '{' or str[parse] == '}') {
|
||||
// handle decimal point for precision
|
||||
if (found_decimal) {
|
||||
assertf(is_float_t or is_str_t, "fennec::format syntax error, encountered precision argument on non-floating point format");
|
||||
assertf(num_decimals == 0, "fennec::format syntax error, multiple decimals detected in floating point format");
|
||||
++num_decimals;
|
||||
found_decimal = false;
|
||||
|
||||
fmt.precision = x;
|
||||
x = 0, j = 1;
|
||||
--parse;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for nested replacement field
|
||||
if (str[parse] == '{') {
|
||||
assertf(str[parse - 1] == '0' or str[parse - 1] == '.' or not isdigit(str[parse - 1]),
|
||||
"fennec::format syntax error, unexpected digit preceding nested replacement field");
|
||||
|
||||
bool prec = str[parse - 1] == '.';
|
||||
bool ded = str[parse + 1] == '}';
|
||||
|
||||
size_t sub;
|
||||
if (nrfd == 2) { // if both are deduced, parse normally. Hack with prefix and postfix.
|
||||
sub = prec ? ++arg_c + 1 : arg_c++;
|
||||
} else if (nrfd == 1 and nnrf == 2 and prec and ded) { // if only precision is nrf, deduce width first
|
||||
ded_width_f = true;
|
||||
ded_temp_i = parse;
|
||||
continue;
|
||||
} else { // otherwise deduce normally
|
||||
sub = ded ? ++arg_c : x;
|
||||
}
|
||||
|
||||
assertf(sub < argc, "fennec::format syntax error, argument index out of range in nested replacement field");
|
||||
assertf(argarray.is_integer(sub), "fennec::format argument error, nested replacement field argument is not convertible to integral type");
|
||||
|
||||
(prec ? fmt.precision : fmt.width) = argarray.int_value(sub);
|
||||
x = 0;
|
||||
|
||||
if (ded_width_f) {
|
||||
ded_width_f = false;
|
||||
ded_width = true;
|
||||
swap(ded_temp_i, parse);
|
||||
arg_c = sub;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ded_width) {
|
||||
parse = ded_temp_i;
|
||||
ded_width = false;
|
||||
}
|
||||
|
||||
parse -= 1 + prec;
|
||||
continue;
|
||||
}
|
||||
|
||||
// ignore closing brace for nested replacement fields
|
||||
if (str[parse] == '}') {
|
||||
--parse;
|
||||
continue;
|
||||
}
|
||||
|
||||
// crude way to only handle 0 case if 0 is the last digit
|
||||
fmt.fill = str[parse] == '0' ? '0' : ' ';
|
||||
|
||||
// parse the number
|
||||
x += j * (str[parse] - '0');
|
||||
j *= 10;
|
||||
--parse;
|
||||
}
|
||||
if (x != 0) {
|
||||
fmt.width = x;
|
||||
}
|
||||
|
||||
// early return
|
||||
if (parse == colon) {
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// check for alt form
|
||||
if (str[parse] == '#') {
|
||||
assertf(is_float_t or is_integer_t, "fennec::format syntax error, encountered alt spec ('#') with non-decimal type");
|
||||
fmt.alt = true;
|
||||
--parse;
|
||||
}
|
||||
|
||||
// check for sign
|
||||
if (str[parse] == '-' or str[parse] == '+' or str[parse] == ' ') {
|
||||
fmt.sign = str[parse];
|
||||
if (str[parse] == ' ') { // handle fill if only space, gets overwritten if encounters fill character
|
||||
fmt.fill = ' ';
|
||||
}
|
||||
--parse;
|
||||
}
|
||||
|
||||
// check for alignment
|
||||
if (str[parse] == '<' or str[parse] == '>' or str[parse] == '^') {
|
||||
fmt.align = str[parse];
|
||||
--parse;
|
||||
}
|
||||
|
||||
// fill character
|
||||
if (str[parse] != ':') {
|
||||
fmt.fill = str[parse];
|
||||
if (str[parse] == ' ') {
|
||||
fmt.sign = fmt.sign == '\0' ? ' ' : fmt.sign;
|
||||
}
|
||||
--parse;
|
||||
}
|
||||
|
||||
// default sign
|
||||
fmt.sign = fmt.sign == '\0' ? '-' : fmt.sign;
|
||||
|
||||
// validate that we handled the entire format arg
|
||||
assertf(parse == colon, "fennec::format syntax error, malformed format string detected, possible double colon");
|
||||
|
||||
|
||||
// add formatted argument
|
||||
res += argarray.format(arg, fmt);
|
||||
i = end + 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_FORMAT_H
|
||||
55
include/fennec/format/format_arg.h
Normal file
55
include/fennec/format/format_arg.h
Normal file
@@ -0,0 +1,55 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/format_arg.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_FORMAT_ARG_H
|
||||
#define FENNEC_FORMAT_FORMAT_ARG_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief helper struct for `fennec::format`
|
||||
struct format_arg {
|
||||
char fill; //!< the fill character of the argument
|
||||
char align; //!< the alignment of the argument
|
||||
char sign; //!< the sign to use
|
||||
bool alt; //!< alternate form
|
||||
bool upper; //!< print as uppercase
|
||||
size_t width; //!< the width of the fill
|
||||
size_t precision; //!< the number of decimal points
|
||||
size_t base; //!< the base to use
|
||||
char type; //!< the argument type
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_FORMAT_ARG_H
|
||||
301
include/fennec/format/formatter.h
Normal file
301
include/fennec/format/formatter.h
Normal file
@@ -0,0 +1,301 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/format/formatter.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_FORMAT_FORMATTER_H
|
||||
#define FENNEC_FORMAT_FORMATTER_H
|
||||
|
||||
#include <fennec/format/charconv.h>
|
||||
#include <fennec/string/string.h>
|
||||
#include <fennec/format/format_arg.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// base template =======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Formatter struct, used to turn values into formatted strings
|
||||
/// \tparam T The type to format
|
||||
template<typename T>
|
||||
struct formatter {
|
||||
///
|
||||
/// \brief default format function
|
||||
///
|
||||
/// \details throws a static assertion
|
||||
/// \returns empty string
|
||||
string operator()(const format_arg&, const T&) {
|
||||
static_assert(false, "Formatter not implemented for the provided type.");
|
||||
return string("");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// strings =============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief formatter of a character array
|
||||
/// \tparam N the number of characters
|
||||
template<size_t N>
|
||||
struct formatter<char[N]> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const char (&str)[N]) {
|
||||
return string(str);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of a character array
|
||||
/// \tparam N the number of characters
|
||||
template<size_t N>
|
||||
struct formatter<const char[N]> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const char (&str)[N]) {
|
||||
return string(str);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of a C-Style string
|
||||
template<>
|
||||
struct formatter<cstring> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const cstring& str) {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of a string
|
||||
template<>
|
||||
struct formatter<string> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param str the string argument
|
||||
/// \returns the formatted version of \f$str\f$
|
||||
string operator()(const format_arg&, const string& str) {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// decimal types =======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief formatter of integral types
|
||||
template<typename IntT> requires(is_integral_v<IntT> and not is_bool_v<IntT>)
|
||||
struct formatter<IntT> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param fmt the format specification
|
||||
/// \param x the integral argument
|
||||
/// \returns the formatted version of \f$x\f$
|
||||
string operator()(const format_arg& fmt, IntT x) {
|
||||
char digits[128] = {};
|
||||
auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.base);
|
||||
assertf(chk != nullptr, "fennec::format error, to_chars error");
|
||||
size_t len = chk - digits;
|
||||
|
||||
// handle uppercase
|
||||
if (fmt.upper) {
|
||||
for (auto& digit : digits) {
|
||||
if (digit == 0) {
|
||||
break;
|
||||
}
|
||||
digit = toupper(digit);
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_sign = (x < 0 or fmt.sign != '-');
|
||||
const bool zero = fmt.fill == '0';
|
||||
const size_t prefix = fmt.alt ? (fmt.type == 'd' ? 0 : 2 - (fmt.type == 'o')) : 0;
|
||||
const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
|
||||
const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
|
||||
const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
|
||||
size_t sign = 0;
|
||||
|
||||
string res = string(explen);
|
||||
|
||||
if (fill > 0) {
|
||||
switch (fmt.align) {
|
||||
case '<':
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
|
||||
break;
|
||||
case '>': case '\0':
|
||||
memcpy(res.data() + explen - len, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, explen - len);
|
||||
break;
|
||||
case '^':
|
||||
size_t bef = fill / 2 + has_sign + prefix;
|
||||
size_t aft = explen - bef;
|
||||
memcpy(res.data() + bef, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, bef);
|
||||
memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
}
|
||||
|
||||
if (has_sign) {
|
||||
res[sign] = (x < 0) ? '-' : fmt.sign;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
res[sign + has_sign] = '0';
|
||||
if (fmt.type != 'o') {
|
||||
res[sign + has_sign + 1] = fmt.type;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of boolean types
|
||||
template<typename BoolT> requires(is_bool_v<BoolT>)
|
||||
struct formatter<BoolT> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param fmt the format specification
|
||||
/// \param x the boolean argument
|
||||
/// \returns the formatted version of \f$x\f$
|
||||
string operator()(const format_arg& fmt, BoolT x) {
|
||||
if (fmt.type == 's' or fmt.type == '\0') {
|
||||
return x ? string("true") : string("false");
|
||||
}
|
||||
|
||||
return formatter<uint8_t>{}(fmt, static_cast<uint8_t>(x));
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief formatter of floating point types
|
||||
template<typename FloatT> requires(is_floating_point_v<FloatT>)
|
||||
struct formatter<FloatT> {
|
||||
///
|
||||
/// \brief format function
|
||||
/// \param fmt the format specification
|
||||
/// \param x the float argument
|
||||
/// \returns the formatted version of \f$x\f$
|
||||
string operator()(const format_arg& fmt, FloatT x) {
|
||||
|
||||
// nan & inf cases
|
||||
if (fennec::isnan(x)) {
|
||||
return string("nan");
|
||||
}
|
||||
if (fennec::isinf(x)) {
|
||||
return string("inf");
|
||||
}
|
||||
|
||||
|
||||
char digits[128] = {};
|
||||
auto chk = fennec::to_chars(digits, digits + sizeof(digits), fennec::abs(x), fmt.type, fmt.precision);
|
||||
assertf(chk != nullptr, "fennec::format error, to_chars error");
|
||||
size_t len = chk - digits;
|
||||
|
||||
// handle uppercase
|
||||
if (fmt.upper) {
|
||||
for (auto& digit : digits) {
|
||||
if (digit == 0) {
|
||||
break;
|
||||
}
|
||||
digit = toupper(digit);
|
||||
}
|
||||
}
|
||||
|
||||
const bool has_sign = (x < 0 or fmt.sign != '-');
|
||||
const bool zero = fmt.fill == '0';
|
||||
const size_t prefix = fmt.alt ? 2 : 0;
|
||||
const size_t sgnlen = len + (zero ? has_sign + prefix : 0);
|
||||
const size_t explen = fennec::max(sgnlen, fmt.width) + (zero ? 0 : has_sign + prefix);
|
||||
const size_t fill = fmt.width > sgnlen ? fmt.width - sgnlen : 0;
|
||||
size_t sign = 0;
|
||||
|
||||
string res = string(explen);
|
||||
|
||||
if (fill > 0) {
|
||||
switch (fmt.align) {
|
||||
case '<':
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
memset(res.data() + has_sign + prefix + len, fmt.fill == '0' ? ' ' : fmt.fill, fill);
|
||||
break;
|
||||
case '>': case '\0':
|
||||
memcpy(res.data() + explen - len, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : explen - len - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, explen - len);
|
||||
break;
|
||||
case '^':
|
||||
size_t bef = fill / 2 + has_sign + prefix;
|
||||
size_t aft = explen - bef;
|
||||
memcpy(res.data() + bef, digits, len);
|
||||
sign = fmt.fill == '0' ? 0 : bef - 1 - prefix;
|
||||
memset(res.data(), fmt.fill, bef);
|
||||
memset(res.data() + bef + len, fmt.fill == '0' ? ' ' : fmt.fill, aft);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
memcpy(res.data() + has_sign + prefix, digits, len);
|
||||
}
|
||||
|
||||
if (has_sign) {
|
||||
res[sign] = (x < 0) ? '-' : fmt.sign;
|
||||
}
|
||||
|
||||
if (prefix) {
|
||||
res[sign + has_sign] = '0';
|
||||
res[sign + has_sign + 1] = fmt.type + ('x' - 'a');
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_FORMAT_FORMATTER_H
|
||||
113
include/fennec/interpret/tokenizer.h
Normal file
113
include/fennec/interpret/tokenizer.h
Normal file
@@ -0,0 +1,113 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/interpret/tokenizer.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_INTERPRET_TOKENIZER_H
|
||||
#define FENNEC_INTERPRET_TOKENIZER_H
|
||||
|
||||
#include <fennec/containers/list.h>
|
||||
#include <fennec/containers/map.h>
|
||||
#include <fennec/containers/priority_queue.h>
|
||||
#include <fennec/string/string.h>
|
||||
|
||||
//
|
||||
// escape sequences are tricky, sometimes they must be separated by white space,
|
||||
// other times they don't. Requiring a list of all possible escape sequences is unrealistic.
|
||||
// We need to allow the user of this struct to specify rules for escape sequences. Here are some basic rules:
|
||||
//
|
||||
// An escape sequence is marked by an escape character, e.g. %, \, {{
|
||||
// Multiple escape characters may be used in a single tokenizer and will have different rules
|
||||
// Escape characters may also be operators, brackets, or quotes
|
||||
// Escape sequences may contain operators, brackets, or quotes
|
||||
//
|
||||
// Here are a few examples of escape sequences from various formats and languages
|
||||
// C: \\, \n, \0, \u200b
|
||||
// PrintF: %s, %2.2f
|
||||
// Python FMT: {{, }}
|
||||
// SPSS: ''
|
||||
//
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
///
|
||||
/// \brief Escape Sequence Specification
|
||||
struct escape_sequence {
|
||||
virtual size_t operator[](const string& str, size_t i) = 0;
|
||||
};
|
||||
|
||||
struct tokenizer {
|
||||
using escseq = escape_sequence*;
|
||||
using escmap = map<char, escape_sequence*>;
|
||||
|
||||
string delimiter; //!< markers that separate tokens
|
||||
string operators; //!< operators are treated as individual tokens
|
||||
string brackets; //!< characters that mark brackets
|
||||
string quotes; //!< characters that mark a string sequence, entire string sequence is treated as one token
|
||||
escmap escapes; //!< characters that mark the start of an escape sequence and validate them
|
||||
bool numbers; //!< Anything that resembles a number
|
||||
|
||||
enum token_ : uint8_t {
|
||||
token_text = 0,
|
||||
token_integer,
|
||||
token_string,
|
||||
token_newline,
|
||||
token_escaped,
|
||||
token_operator,
|
||||
token_bracket,
|
||||
token_quoted,
|
||||
|
||||
num_token_types
|
||||
};
|
||||
|
||||
using token = pair<string, uint8_t>;
|
||||
|
||||
private:
|
||||
static constexpr uint8_t token_delimiter = num_token_types;
|
||||
|
||||
constexpr list<token> operator()(const string& line) {
|
||||
list<token> res;
|
||||
priority_queue<pair<size_t, uint8_t>> idx;
|
||||
|
||||
for (char c : delimiter) {
|
||||
size_t i = 0;
|
||||
while (i != line.size()) {
|
||||
size_t n = line.find(c, i);
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_INTERPRET_TOKENIZER_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,20 +17,22 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file assert.h
|
||||
/// \file fennec/lang/assert.h
|
||||
/// \brief \ref fennec_lang_assert
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_ASSERT_H
|
||||
#define FENNEC_LANG_ASSERT_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
///
|
||||
/// \page fennec_lang_assert Assertions
|
||||
///
|
||||
@@ -46,18 +48,18 @@
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// assert(expr, desc)
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Make an assertion with expression `expr` and provide a description `desc`. Only halts in debug mode.
|
||||
/// Make an assertion with expression \f$expr\f$ and provide a description \f$desc\f$. Only halts in debug mode.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// assertf(expr, desc)
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Make an assertion with expression `expr` and provide a description `desc`. Always halts.
|
||||
/// Make an assertion with expression \f$expr\f$ and provide a description \f$desc\f$. Always halts.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// assertd(expr, desc)
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Make an assertion, ***only in debug mode***, with expression `expr` and provide a description `desc`.
|
||||
/// This should be used when the branching caused by `assert` would hinder performance in release mode.
|
||||
/// Make an assertion, ***only in debug mode***, with expression \f$expr\f$ and provide a description \f$desc\f$.
|
||||
/// This should be used when the branching caused by \f$assert\f$ would hinder performance in release mode.
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
@@ -68,23 +70,46 @@
|
||||
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||
#endif
|
||||
|
||||
using assert_handler = void (*)(const char *, const char *, int , const char *);
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
void _assert_impl(const char* expr, size_t expr_l,
|
||||
const char* file, size_t file_l, int line,
|
||||
const char* func, size_t func_l,
|
||||
const char* desc, bool halt);
|
||||
|
||||
void _assert_impl(const char* expression, const char* file, int line, const char* function, const char* desc, bool halt);
|
||||
template<size_t ExprL, size_t FileL, size_t FuncL>
|
||||
void _assert(const char (&expr)[ExprL],
|
||||
const char (&file)[FileL], int line,
|
||||
const char (&func)[FuncL],
|
||||
const char* desc,
|
||||
bool halt) {
|
||||
::_assert_impl(expr, ExprL, file, FileL, line, func, FuncL, desc, halt);
|
||||
}
|
||||
#endif
|
||||
|
||||
// flagged unlikely to optimize branch prediction
|
||||
///
|
||||
/// \brief base assert function, halts only in debug mode
|
||||
/// \param expression the expression to validate
|
||||
/// \param description the description of the assertion
|
||||
#define assert(expression, description) \
|
||||
if(not(expression)) [[unlikely]] { \
|
||||
_assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, not FENNEC_RELEASE); \
|
||||
_assert(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, not FENNEC_RELEASE); \
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief fail assert function, always halts
|
||||
/// \param expression the expression to validate
|
||||
/// \param description the description of the assertion
|
||||
#define assertf(expression, description) \
|
||||
if(not(expression)) [[unlikely]] { \
|
||||
_assert_impl(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, true); \
|
||||
_assert(#expression, __FILE__, __LINE__, __PRETTY_FUNCTION__, description, true); \
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief debug assert function, only defined in debug mode
|
||||
/// \param expression the expression to validate
|
||||
/// \param description the description of the assertion
|
||||
#if FENNEC_RELEASE
|
||||
#define assertd(expression, description) (0)
|
||||
#define assertd(expression, description)
|
||||
#else
|
||||
#define assertd(expression, description) assert(expression, description)
|
||||
#endif
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file bits.h
|
||||
/// \file fennec/lang/bits.h
|
||||
/// \brief \ref fennec_lang_bit_manipulation
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -26,27 +26,58 @@ namespace fennec
|
||||
|
||||
// equality ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test equality of two values
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> struct equality;
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common equality operator \f$==\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_equals_v<T0, T1>
|
||||
struct equality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x == y;
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common less operator \f$<\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires(not has_equals_v<T0, T1>
|
||||
and has_less_v<T0, T1> and has_less_v<T1, T0>)
|
||||
struct equality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return not(x < y) and not(y < x);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common greater operator \f$>\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires(not(has_equals_v<T0, T1>)
|
||||
and(not has_less_v<T0, T1> or not has_less_v<T1, T0>)
|
||||
and(has_greater_v<T0, T1> and has_greater_v<T1, T0>))
|
||||
struct equality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return not(x > y) and not(y > x);
|
||||
}
|
||||
@@ -55,24 +86,72 @@ struct equality<T0, T1> {
|
||||
|
||||
// inequality ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test inequality of two values
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> struct inequality;
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common inequality operator \f$\neq\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_nequals_v<T0, T1>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x != y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common equality operator \f$==\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_equals_v<T0, T1>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return not (x == y);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common less operator \f$<\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_less_v<T0, T1> and has_less_v<T1, T0>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return (x < y) or (y < x);
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Implementations for two types that have a common greater operator \f$>\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1> requires has_greater_v<T0, T1> and has_greater_v<T1, T0>
|
||||
struct inequality<T0, T1> {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if not equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return (x > y) or (y > x);
|
||||
}
|
||||
@@ -81,8 +160,17 @@ struct inequality<T0, T1> {
|
||||
|
||||
// less ================================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is less than a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_less_v<T0, T1>
|
||||
struct less {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if less, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x < y;
|
||||
}
|
||||
@@ -91,30 +179,57 @@ struct less {
|
||||
|
||||
// less_equal ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is less than or equal to a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_less_equals_v<T0, T1>
|
||||
struct less_equals {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if less than or equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x <= y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// less ================================================================================================================
|
||||
// greater =============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is greater than a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_greater_v<T0, T1>
|
||||
struct greater {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if greater, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x < y;
|
||||
return x > y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// less_equal ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Struct to test if a value of type \f$T0\f$ is greater than or equal to a value of type \f$T1\f$
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0> requires has_greater_equals_v<T0, T1>
|
||||
struct greater_equals {
|
||||
///
|
||||
/// \brief operator to test the two values
|
||||
/// \param x the value of type \f$T0\f$
|
||||
/// \param y the value of type \f$T1\f$
|
||||
/// \returns \f$true\f$ if greater than or equal, \f$false\f$ otherwise
|
||||
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||
return x <= y;
|
||||
return x >= y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file conditional_types.h
|
||||
/// \file fennec/lang/conditional_types.h
|
||||
/// \brief \ref fennec_lang_conditional_types
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace fennec
|
||||
/// \brief select between two types based on a condition
|
||||
///
|
||||
/// \details Selects between \p TrueT and \p FalseT based on the boolean value \p b.
|
||||
/// The chosen type is stored in `conditional::type`.
|
||||
/// The chosen type is stored in `fennec::conditional::type`.
|
||||
/// \tparam B the value of the condition
|
||||
/// \tparam TrueT type to use when \f$B == true\f$
|
||||
/// \tparam FalseT type to use when \f$B == false\f$
|
||||
@@ -84,45 +84,46 @@ struct conditional;
|
||||
|
||||
|
||||
///
|
||||
/// \brief Shorthand for ```typename conditional<ConditionV, TrueT, FalseT>::type```
|
||||
/// \brief Shorthand for `typename fennec::conditional<ConditionV, TrueT, FalseT>::type`
|
||||
template<bool B, typename TrueT, typename FalseT>
|
||||
using conditional_t
|
||||
= typename conditional<B, TrueT, FalseT>::type;
|
||||
|
||||
|
||||
// specialization of fennec::conditional for `true` case
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// specialization of fennec::conditional for \f$true\f$ case
|
||||
template<typename T, typename F>
|
||||
struct conditional<true, T, F> : type_identity<T>{};
|
||||
|
||||
|
||||
// specialization of fennec::conditional for `false` case
|
||||
// specialization of fennec::conditional for \f$false\f$ case
|
||||
template<typename T, typename F>
|
||||
struct conditional<false, T, F> : type_identity<F>{};
|
||||
|
||||
#endif
|
||||
|
||||
// fennec::detect ======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Detect whether `DetectT<ArgsT...>` is a valid type
|
||||
/// \brief Detect whether \f$DetectT<ArgsT...>\f$ is a valid type
|
||||
///
|
||||
/// \details Selects `DetectT<ArgsT...>` if it exists, otherwise selects `DefaultT` The chosen type is stored in `detect::type` and
|
||||
/// a boolean value is stored in `detect::is_detected` representing whether `DetectT<ArgsT...>` is found.
|
||||
/// \details Selects \f$DetectT<ArgsT...>\f$ if it exists, otherwise selects \f$DefaultT\f$ The chosen type is stored in `fennec::detect::type` and
|
||||
/// a boolean value is stored in `fennec::detect::is_detected` representing whether \f$DetectT<ArgsT...>\f$ is found.
|
||||
/// \tparam DefaultT Default type
|
||||
/// \tparam DetectT Type to detect
|
||||
/// \tparam ArgsT Any template arguments for `DetectT<ArgsT>`
|
||||
/// \tparam ArgsT Any template arguments for \f$DetectT<ArgsT>\f$
|
||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||
struct detect
|
||||
{
|
||||
using type = DefaultT;
|
||||
static constexpr bool is_detected = false;
|
||||
using type = DefaultT; //!< the detected type
|
||||
static constexpr bool is_detected = false; //!< whether it was detected
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for ```typename detect<DefaultT, DetectT, ArgsT...>::type```
|
||||
/// \brief Shorthand for `typename fennec::detect<DefaultT, DetectT, ArgsT...>::type`
|
||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||
using detect_t = typename detect<DefaultT, DetectT, ArgsT...>::type;
|
||||
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// true case
|
||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||
requires requires { typename DetectT<ArgsT...>; }
|
||||
@@ -131,6 +132,7 @@ struct detect<DefaultT, DetectT, ArgsT...>
|
||||
using type = DetectT<ArgsT...>;
|
||||
static constexpr bool is_detected = true;
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
// fennec::enable_if ===================================================================================================
|
||||
@@ -138,7 +140,7 @@ struct detect<DefaultT, DetectT, ArgsT...>
|
||||
///
|
||||
/// \brief Leverage SFINAE to conditionally enable a function or class at compile-time
|
||||
///
|
||||
/// \details If `B` is `true`, define a public member type `type`. Otherwise, there is no member. <br>
|
||||
/// \details If \f$B\f$ is \f$true\f$, define a public member type \f$type\f$. Otherwise, there is no member. <br>
|
||||
/// **Example Usage**
|
||||
/// \code{.cpp}
|
||||
/// template<typename TypeT,
|
||||
@@ -152,13 +154,15 @@ template<bool B, typename T = void>
|
||||
struct enable_if {};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for ```typename enable_if<B, T>::type```
|
||||
/// \brief Shorthand for `typename fennec::enable_if<B, T>::type`
|
||||
template<bool B, typename T = void>
|
||||
using enable_if_t = typename enable_if<B, T>::type;
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// true case
|
||||
template<typename T>
|
||||
struct enable_if<true, T> { using type = T; };
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file sequences.h
|
||||
/// \brief \ref fennec_lang_sequences
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_SEQUENCES_H
|
||||
#define FENNEC_LANG_SEQUENCES_H
|
||||
|
||||
///
|
||||
/// \page fennec_lang_sequences Sequences
|
||||
///
|
||||
/// \brief This header is part of the metaprogramming library. It defines structures for sequences of values, used during compile time.
|
||||
///
|
||||
/// \code #include <fennec/lang/sequences.h> \endcode
|
||||
///
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_sequences">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::sequence "sequence<ValueT, Values...>"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::sequence
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::integer_sequence "integer_sequence<IntT, Values...>"<br>
|
||||
/// \ref fennec::make_integer_sequence "typename make_integer_sequence<IntT, N>::type"<br>
|
||||
/// \ref fennec::make_integer_sequence_t "make_integer_sequence_t<IntT, N>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::integer_sequence
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::index_sequence "index_sequence<Indices...>"<br>
|
||||
/// \ref fennec::make_index_sequence "typename make_index_sequence<N>::type"<br>
|
||||
/// \ref fennec::make_index_sequence_t "make_index_sequence_t<N>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::index_sequence
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::concat_sequence "typename concat_sequence<SequenceT0, SequenceT1>::type"<br>
|
||||
/// \ref fennec::concat_sequence_t "concat_sequence_t<SequenceT0, SequenceT1>"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::concat_sequence
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
|
||||
#include <fennec/lang/type_traits.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// fennec::sequence ====================================================================================================
|
||||
|
||||
///
|
||||
/// \brief metaprogramming sequence
|
||||
///
|
||||
/// \details Stores a sequence of values of type `ValueT` as a template pack.
|
||||
/// You can access the parameter pack in another template function, i.e.
|
||||
/// \code{cpp}
|
||||
/// template<typename TypeT, TypeT...Values>
|
||||
/// constexpr TypeT summation(sequence<TypeT, Values...>)
|
||||
/// {
|
||||
/// return (Values + ...);
|
||||
/// }
|
||||
/// \endcode
|
||||
/// \tparam ValueT type of the values
|
||||
/// \tparam Values sequence values
|
||||
template<typename ValueT, ValueT...Values> struct const_sequence
|
||||
{
|
||||
/// \brief type of the sequence
|
||||
using value_type = ValueT;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = const_sequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
///
|
||||
/// \return number of elements in the array
|
||||
inline static constexpr size_t size() noexcept {
|
||||
return sizeof...(Values);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// fennec::integer_sequence ============================================================================================
|
||||
|
||||
///
|
||||
/// \brief metaprogramming integral sequence
|
||||
///
|
||||
/// \details A `fennec::sequence` specialized integer types.
|
||||
/// \tparam IntT type of the values, must satisfy ```fennec::is_integral<T>```
|
||||
/// \tparam Values sequence values
|
||||
template<typename IntT, IntT...Values> requires(is_integral_v<IntT>)
|
||||
struct const_integer_sequence : const_sequence<IntT, Values...>
|
||||
{
|
||||
/// \brief type of the sequence
|
||||
using value_type = IntT;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = const_integer_sequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
///
|
||||
/// \return number of elements in the array
|
||||
inline static constexpr size_t size() noexcept {
|
||||
return sizeof...(Values);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief generate a fennec::integer_sequence \f$\left[\,0\,\ldots\,N\,\right)\f$
|
||||
///
|
||||
/// \details
|
||||
/// \tparam IntT type of the values, must satisfy ```fennec::is_integral<T>```
|
||||
/// \tparam N size of the sequence to generate
|
||||
template<typename IntT, size_t N> struct make_integer_sequence;
|
||||
|
||||
///
|
||||
/// \brief shorthand for ```typename make_integer_sequence<T, N>::type```
|
||||
template<typename IntT, size_t N> using make_integer_sequence_t = typename make_integer_sequence<IntT, N>::type;
|
||||
|
||||
|
||||
|
||||
// fennec::index_sequence ==============================================================================================
|
||||
|
||||
///
|
||||
/// \brief metaprogramming integral sequence
|
||||
///
|
||||
/// \details A `fennec::integer_sequence` specialized for sequences of `size_t` indices.
|
||||
/// \tparam Indices sequence values
|
||||
template<size_t...Indices> struct const_index_sequence : const_integer_sequence<size_t, Indices...>
|
||||
{
|
||||
/// \brief type of the sequence
|
||||
using value_type = size_t;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = const_index_sequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
///
|
||||
/// \return number of elements in the array
|
||||
inline static constexpr size_t size() noexcept {
|
||||
return sizeof...(Indices);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief generate a fennec::index_sequence \f$\left[\,0\,\ldots\,N\,\right)\f$
|
||||
///
|
||||
/// \details
|
||||
/// \tparam T type of the values, must satisfy ```fennec::is_integral<T>```
|
||||
/// \tparam N size of the sequence to generate
|
||||
template<size_t N> struct make_index_sequence;
|
||||
|
||||
///
|
||||
/// \brief shorthand for ```typename make_index_sequence<N>::type```
|
||||
template<size_t N> using make_index_sequence_t = typename make_index_sequence<N>::type;
|
||||
|
||||
|
||||
|
||||
// fennec::concat_sequence =============================================================================================
|
||||
|
||||
///
|
||||
/// \brief concatenate two sequences
|
||||
///
|
||||
/// \details A tool for concatenating two `fennec::sequence` types.
|
||||
/// \tparam SequenceT0 lhs
|
||||
/// \tparam SequenceT1 rhs
|
||||
template<typename SequenceT0, typename SequenceT1> struct concat_sequence;
|
||||
|
||||
///
|
||||
/// \brief shorthand for ```typename concat_sequence<SequenceT0, SequenceT1>::type```
|
||||
template<typename SequenceT0, typename SequenceT1> using concat_sequence_t
|
||||
= typename concat_sequence<SequenceT0, SequenceT1>::type;
|
||||
|
||||
|
||||
|
||||
// Internal ============================================================================================================
|
||||
|
||||
// Implementation for Generating an integer_sequence
|
||||
template<typename T, size_t N> struct make_integer_sequence : concat_sequence_t<make_integer_sequence_t<T, N / 2>, make_integer_sequence_t<T, N - N / 2>>{};
|
||||
|
||||
// Base Case of N=0
|
||||
template<typename T> struct make_integer_sequence<T, 0> : const_integer_sequence<T> {};
|
||||
|
||||
// Base Case of N=1
|
||||
template<typename T> struct make_integer_sequence<T, 1> : const_integer_sequence<T, 0>{};
|
||||
|
||||
|
||||
// Implementation for Generating an index_sequence
|
||||
template<size_t N> struct make_index_sequence : concat_sequence_t<make_index_sequence_t<N / 2>, make_index_sequence_t<N - N / 2>>{};
|
||||
|
||||
// Base Case of N=0
|
||||
template<> struct make_index_sequence<0> : const_index_sequence<> {};
|
||||
|
||||
// Base Case of N=1
|
||||
template<> struct make_index_sequence<1> : const_index_sequence<0>{};
|
||||
|
||||
|
||||
// Specialization for integer sequences
|
||||
template<typename T, T...SequenceV0, T...SequenceV1>
|
||||
struct concat_sequence<const_integer_sequence<T, SequenceV0...>, const_integer_sequence<T, SequenceV1...>>
|
||||
: const_integer_sequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
|
||||
// Specialization for index sequences
|
||||
template<size_t...SequenceV0, size_t...SequenceV1>
|
||||
struct concat_sequence<const_index_sequence<SequenceV0...>, const_index_sequence<SequenceV1...>>
|
||||
: const_index_sequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_SEQUENCES_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file constants.h
|
||||
/// \file fennec/lang/constants.h
|
||||
/// \brief \ref fennec_lang_constants
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
52
include/fennec/lang/declval.h
Normal file
52
include/fennec/lang/declval.h
Normal file
@@ -0,0 +1,52 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/declval.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_DECLVAL_H
|
||||
#define FENNEC_LANG_DECLVAL_H
|
||||
|
||||
#include <fennec/lang/detail/_declval.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// fennec::declval =====================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Metaprogramming helper for testing values of type T
|
||||
/// \tparam T the type
|
||||
/// \returns a metavalue of type \f$T\f$
|
||||
template<typename T> auto declval() noexcept -> decltype(detail::_declval<T>(0)) {
|
||||
static_assert(detail::_declval_protector<T>{}, "declval must not be used");
|
||||
return detail::_declval<T>(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DECLVAL_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
34
include/fennec/lang/detail/_declval.h
Normal file
34
include/fennec/lang/detail/_declval.h
Normal file
@@ -0,0 +1,34 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_LANG_DETAIL_DECLVAL_H
|
||||
#define FENNEC_LANG_DETAIL_DECLVAL_H
|
||||
|
||||
#include <fennec/lang/constants.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
template<typename T, typename U = T&&> U _declval(int);
|
||||
template<typename T> T _declval(long);
|
||||
|
||||
template<typename T> struct _declval_protector : bool_constant<false> {};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_DECLVAL_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,18 @@
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
#ifndef FENNEC_CONCURRENCY_ATOMIC_H
|
||||
#define FENNEC_CONCURRENCY_ATOMIC_H
|
||||
|
||||
#endif // FENNEC_CONCURRENCY_ATOMIC_H
|
||||
#ifndef FENNEC_LANG_DETAIL_FUNCTION_H
|
||||
#define FENNEC_LANG_DETAIL_FUNCTION_H
|
||||
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_FUNCTION_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -26,6 +26,8 @@
|
||||
#define __PTRDIFF_TYPE__ ptrdiff_t
|
||||
#endif
|
||||
|
||||
// Include math since stdint will define its own versions of isinf and isnan
|
||||
#include <math.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -19,13 +19,19 @@
|
||||
#ifndef FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||
#define FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||
|
||||
#include <fennec/math/common.h>
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
template<typename...TypesT> struct _type_sequence {};
|
||||
|
||||
template<typename FirstT, typename... RestT> struct _first_element : type_identity<FirstT> {};
|
||||
|
||||
|
||||
|
||||
// fennec::nth_element =================================================================================================
|
||||
|
||||
template<size_t n, size_t i, typename...TypesT> struct _nth_element;
|
||||
|
||||
template<size_t n, size_t i> struct _nth_element<n, i> : type_identity<void> {};
|
||||
@@ -35,6 +41,56 @@ namespace fennec::detail
|
||||
n == i, HeadT,
|
||||
typename _nth_element<n, i + 1, RestT...>::type
|
||||
> {};
|
||||
|
||||
|
||||
|
||||
// fennec::max_element_size ============================================================================================
|
||||
|
||||
template<size_t, typename...> struct _max_element_size;
|
||||
|
||||
template<size_t M, typename HeadT>
|
||||
struct _max_element_size<M, HeadT>
|
||||
: integral_constant<size_t, fennec::max(M, sizeof(HeadT))> {
|
||||
};
|
||||
|
||||
template<size_t M, typename HeadT, typename...RestT>
|
||||
struct _max_element_size<M, HeadT, RestT...>
|
||||
: _max_element_size<fennec::max(M, sizeof(HeadT)), RestT...> {
|
||||
};
|
||||
|
||||
|
||||
|
||||
// fennec::find_element ================================================================================================
|
||||
|
||||
template<size_t N, typename, typename...> struct _find_element;
|
||||
|
||||
template<size_t N, typename FindT, typename HeadT>
|
||||
struct _find_element<N, FindT, HeadT> : integral_constant<size_t, is_same_v<FindT, HeadT> ? N : N + 1> {};
|
||||
|
||||
template<size_t N, typename FindT, typename HeadT, typename...RestT>
|
||||
struct _find_element<N, FindT, HeadT, RestT...>
|
||||
: conditional_t<is_same_v<FindT, HeadT>, integral_constant<size_t, N>, _find_element<N + 1, FindT, RestT...>> {};
|
||||
|
||||
|
||||
// fennec::search_element ==============================================================================================
|
||||
|
||||
template<template<typename> typename, typename...> struct _search_element;
|
||||
|
||||
template<template<typename> typename SearchT> struct _search_element<SearchT> : type_identity<void> {};
|
||||
|
||||
template<template<typename> typename SearchT, typename HeadT, typename...RestT> requires(SearchT<HeadT>{})
|
||||
struct _search_element<SearchT, HeadT, RestT...>
|
||||
: conditional_t<SearchT<HeadT>{}, type_identity<HeadT>, _search_element<SearchT, RestT...>> {
|
||||
};
|
||||
|
||||
template<template<typename, typename...> typename, typename, typename...> struct _search_element_args;
|
||||
|
||||
template<template<typename, typename...> typename SearchT, typename...ArgsT>
|
||||
struct _search_element_args<SearchT, _type_sequence<ArgsT...>> : type_identity<void> {};
|
||||
|
||||
template<template<typename, typename...> typename SearchT, typename HeadT, typename...RestT, typename...ArgsT>
|
||||
struct _search_element_args<SearchT, _type_sequence<ArgsT...>, HeadT, RestT...>
|
||||
: conditional_t<SearchT<HeadT, ArgsT...>{}, type_identity<HeadT>, _search_element_args<SearchT, _type_sequence<ArgsT...>, RestT...>> {};
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -19,8 +19,9 @@
|
||||
#ifndef FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
#define FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
|
||||
#include <fennec/lang/ranges.h>
|
||||
#include <fennec/lang/constants.h>
|
||||
#include <fennec/lang/float.h>
|
||||
#include <fennec/lang/declval.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
@@ -54,6 +55,12 @@ namespace fennec::detail
|
||||
template<> struct _is_integral<llong_t> : true_type {};
|
||||
template<> struct _is_integral<ullong_t> : true_type {};
|
||||
|
||||
template<typename> struct _is_const : false_type {};
|
||||
template<typename T> struct _is_const<const T> : true_type {};
|
||||
|
||||
template<typename> struct _is_volatile : false_type {};
|
||||
template<typename T> struct _is_volatile<volatile T> : true_type {};
|
||||
|
||||
// Most unsigned types will underflow `-1` to the types maximum value
|
||||
template<typename TypeT> struct _is_signed : bool_constant<TypeT(-1) < TypeT(0)> {};
|
||||
template<typename TypeT> struct _is_unsigned : bool_constant<TypeT(-1) >= TypeT(0)> {};
|
||||
@@ -65,11 +72,80 @@ namespace fennec::detail
|
||||
template<typename> struct _is_pointer : false_type {};
|
||||
template<typename T> struct _is_pointer<T*> : true_type {};
|
||||
|
||||
template<typename T, typename U = T&&> U _declval(int);
|
||||
template<typename T> T _declval(long);
|
||||
template<typename> struct _is_reference : false_type {};
|
||||
template<typename T> struct _is_reference<T&> : true_type {};
|
||||
template<typename T> struct _is_reference<T&&> : true_type {};
|
||||
|
||||
template<typename T> struct _declval_protector : bool_constant<false> {};
|
||||
template<typename> struct _is_lvalue_reference : false_type {};
|
||||
template<typename T> struct _is_lvalue_reference<T&> : true_type {};
|
||||
|
||||
template<typename> struct _is_rvalue_reference : false_type {};
|
||||
template<typename T> struct _is_rvalue_reference<T&&> : true_type {};
|
||||
|
||||
template<typename T> struct _is_complete {
|
||||
template<typename U>
|
||||
static auto test(U*) -> bool_constant<sizeof(U) == sizeof(U)>;
|
||||
static auto test(...) -> false_type;
|
||||
using type = decltype(_is_complete::test(static_cast<T*>(nullptr)));
|
||||
};
|
||||
|
||||
template<typename T> struct _is_destructible {
|
||||
template<typename U, typename = decltype(declval<T&>().~T())>
|
||||
static auto test(int) -> true_type;
|
||||
template<typename>
|
||||
static auto test(...) -> false_type;
|
||||
using type = decltype(test<T>(0));
|
||||
};
|
||||
|
||||
template<typename T> struct _is_nothrow_destructible {
|
||||
template<typename U, typename = decltype(noexcept(declval<T&>().~T()))>
|
||||
static auto test(int) -> true_type;
|
||||
template<typename>
|
||||
static auto test(...) -> false_type;
|
||||
using type = decltype(test<T>(0));
|
||||
};
|
||||
|
||||
// https://stackoverflow.com/questions/13830158/how-to-write-a-trait-which-checks-whether-a-type-is-iterable
|
||||
template<typename T>
|
||||
auto _is_iterable(int) -> decltype(
|
||||
fennec::begin(declval<T&>()) != fennec::end(declval<T&>()),
|
||||
void(),
|
||||
++declval<decltype(fennec::begin(declval<T&>()))&>(),
|
||||
void(*fennec::begin(declval<T&>())),
|
||||
true_type{}
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
auto _is_iterable(...) -> false_type;
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
auto _is_indexable(int) -> decltype(
|
||||
declval<T&>()[0],
|
||||
true_type{}
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
auto _is_indexable(...) -> false_type;
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
auto _is_mappable(int) -> decltype(
|
||||
declval<T&>()[declval<typename T::key_t&>()],
|
||||
true_type{}
|
||||
);
|
||||
|
||||
template<typename T>
|
||||
auto _is_mappable(...) -> false_type;
|
||||
|
||||
|
||||
template<typename B> auto _ptr_conv(const volatile B*) -> true_type;
|
||||
template<typename> auto _ptr_conv(const volatile void*) -> false_type;
|
||||
|
||||
template<typename B, typename D> auto _is_base_of(int) -> decltype(detail::_ptr_conv<B>(static_cast<D*>(nullptr)));
|
||||
template<typename, typename> auto _is_base_of(...) -> false_type;
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -20,29 +20,72 @@
|
||||
#define FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/detail/_type_traits.h>
|
||||
|
||||
namespace fennec::detail
|
||||
{
|
||||
template<typename TypeT> struct _add_pointer : type_identity<TypeT*> {};
|
||||
template<typename TypeT> struct _add_pointer<TypeT&> : type_identity<TypeT*> {};
|
||||
template<typename TypeT> struct _remove_pointer : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_pointer<TypeT*> : type_identity<TypeT> {};
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
template<typename TypeT> struct _add_const : type_identity<const TypeT> {};
|
||||
template<typename TypeT> struct _remove_const : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_const<const TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT> struct _add_volatile : type_identity<volatile TypeT> {};
|
||||
template<typename TypeT> struct _remove_volatile : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_volatile<volatile TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT> struct _add_cv : _add_const<typename _add_volatile<TypeT>::type> {};
|
||||
template<typename TypeT> struct _remove_cv : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_cv<const TypeT> : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_cv<volatile TypeT> : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_cv<const volatile TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _decay_selector : conditional_t<_is_const<const TypeT>{}, _remove_cv<TypeT>, _add_pointer<TypeT>> {};
|
||||
|
||||
template<typename TypeT> requires requires { typename TypeT::decay_t; }
|
||||
struct _decay_selector<TypeT> : type_identity<typename TypeT::decay_t> {};
|
||||
|
||||
template<typename TypeT, size_t N>
|
||||
struct _decay_selector<TypeT[N]> : type_identity<TypeT*> {};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _decay_selector<TypeT[]> : type_identity<TypeT*> {};
|
||||
|
||||
template<typename TypeT> struct _decay {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT> struct _decay<TypeT&> {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT> struct _decay<TypeT&&> {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT, typename = void>
|
||||
struct _add_lvalue_reference {
|
||||
using type = _Tp;
|
||||
using type = TypeT;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct _add_lvalue_reference<_Tp, void_t<_Tp&>> {
|
||||
using type = _Tp&;
|
||||
template<typename TypeT>
|
||||
struct _add_lvalue_reference<TypeT, void_t<TypeT&>> {
|
||||
using type = TypeT&;
|
||||
};
|
||||
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
template<typename TypeT, typename = void>
|
||||
struct _add_rvalue_reference {
|
||||
using type = _Tp;
|
||||
using type = TypeT;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct _add_rvalue_reference<_Tp, void_t<_Tp&&>> {
|
||||
using type = _Tp&&;
|
||||
template<typename TypeT>
|
||||
struct _add_rvalue_reference<TypeT, void_t<TypeT&&>> {
|
||||
using type = TypeT&&;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,13 +17,15 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file float.h
|
||||
/// \file fennec/lang/float.h
|
||||
/// \brief metaprogramming floating point type info
|
||||
///
|
||||
///
|
||||
/// \details this file is automatically generated for the current build environment
|
||||
/// \details This file is automatically generated for the current build environment.
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// Environment for this build: GNU Linux x86_64
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -58,30 +60,55 @@
|
||||
#undef FLT_DENORM_MIN
|
||||
#undef FLT_ROUND_ERR
|
||||
|
||||
/// \brief Does \f$float\f$ have an infinity?
|
||||
#define FLT_HAS_INFINITY 1
|
||||
/// \brief Does \f$float\f$ have a quiet NaN?
|
||||
#define FLT_HAS_QUIET_NAN 1
|
||||
/// \brief Does \f$float\f$ have a signaling NaN?
|
||||
#define FLT_HAS_SIGNALING_NAN 1
|
||||
/// \brief Does \f$float\f$ use denormalization?
|
||||
#define FLT_HAS_DENORM 1
|
||||
/// \brief Does \f$float\f$ have loss with denormalization?
|
||||
#define FLT_HAS_DENORM_LOSS 0
|
||||
/// \brief What rounding style does \f$float\f$ use?
|
||||
#define FLT_ROUNDS 1
|
||||
/// \brief Does \f$float\f$ use the IEEE floating point specification?
|
||||
#define FLT_IS_IEC559 1
|
||||
/// \brief The number of mantissa bits in \f$float\f$.
|
||||
#define FLT_MANT_DIG 24
|
||||
/// \brief The number of decimal digits guaranteed to be preserved in a \f$float\f$ → \f$text\f$ → \f$float\f$.
|
||||
#define FLT_DIG 6
|
||||
/// \brief The decimal precision required to serialize and deserialize a \f$float\f$.
|
||||
#define FLT_DECIMAL_DIG 9
|
||||
/// \brief The radix, or integer base, used to represent a \f$float\f$.
|
||||
#define FLT_RADIX 2
|
||||
/// \brief The minimum negative integer such that \f${FLT_RADIX}^{FLT_MIN_EXP}\f$ results in a normalized \f$float\f$.
|
||||
#define FLT_MIN_EXP -125
|
||||
/// \brief The maximum positive integer such that \f${FLT_RADIX}^{FLT_MAX_EXP}\f$ results in a non-infinite \f$float\f$.
|
||||
#define FLT_MAX_EXP 128
|
||||
/// \brief The minimum negative integer such that \f${10}^{FLT_MIN_EXP}\f$ results in a normalized \f$float\f$.
|
||||
#define FLT_MIN_10_EXP -37
|
||||
/// \brief The maximum positive integer such that \f${10}^{FLT_MAX_EXP}\f$ results in a non-infinite \f$float\f$.
|
||||
#define FLT_MAX_10_EXP 38
|
||||
/// \brief Do arithmetics operations with \f$float\f$ trap?
|
||||
#define FLT_TRAPS 0
|
||||
/// \brief Do arithmetics operations with \f$float\f$ check for underflow?
|
||||
#define FLT_TINYNESS_BEFORE 0
|
||||
/// \brief Smallest positive, finite, normal value of \f$float\f$.
|
||||
#define FLT_MIN fennec::bit_cast<float>(0x800000)
|
||||
/// \brief Largest positive, finite value of \f$float\f$.
|
||||
#define FLT_MAX fennec::bit_cast<float>(0x7f7fffff)
|
||||
/// \brief The difference between \f$1.0\f$ and the next representable value of \f$float\f$.
|
||||
#define FLT_EPSILON fennec::bit_cast<float>(0x34000000)
|
||||
/// \brief A value representing \f$\inf\f$ of type \f$float\f$.
|
||||
#define FLT_INF fennec::bit_cast<float>(0x7f800000)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$float\f$ that does not trap.
|
||||
#define FLT_QUIET_NAN fennec::bit_cast<float>(0x7fc00000)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$float\f$ that traps.
|
||||
#define FLT_SIGNALING_NAN fennec::bit_cast<float>(0x7fa00000)
|
||||
/// \brief Smallest positive, finite, subnormal value of \f$float\f$.
|
||||
#define FLT_DENORM_MIN fennec::bit_cast<float>(0x1)
|
||||
/// \brief Maximum rounding error of type \f$float\f$.
|
||||
#define FLT_ROUND_ERR fennec::bit_cast<float>(0x3f000000)
|
||||
|
||||
#undef DBL_HAS_INFINITY
|
||||
@@ -110,30 +137,55 @@
|
||||
#undef DBL_DENORM_MIN
|
||||
#undef DBL_ROUND_ERR
|
||||
|
||||
/// \brief Does \f$double\f$ have an infinity?
|
||||
#define DBL_HAS_INFINITY 1
|
||||
/// \brief Does \f$double\f$ have a quiet NaN?
|
||||
#define DBL_HAS_QUIET_NAN 1
|
||||
/// \brief Does \f$double\f$ have a signaling NaN?
|
||||
#define DBL_HAS_SIGNALING_NAN 1
|
||||
/// \brief Does \f$double\f$ use denormalization?
|
||||
#define DBL_HAS_DENORM 1
|
||||
/// \brief Does \f$double\f$ have loss with denormalization?
|
||||
#define DBL_HAS_DENORM_LOSS 0
|
||||
/// \brief What rounding style does \f$double\f$ use?
|
||||
#define DBL_ROUNDS 1
|
||||
/// \brief Does \f$double\f$ use the IEEE doubleing point specification?
|
||||
#define DBL_IS_IEC559 1
|
||||
/// \brief The number of mantissa bits in \f$double\f$.
|
||||
#define DBL_MANT_DIG 53
|
||||
/// \brief The number of decimal digits guaranteed to be preserved in a \f$double\f$ → \f$text\f$ → \f$double\f$.
|
||||
#define DBL_DIG 15
|
||||
/// \brief The decimal precision required to serialize and deserialize a \f$double\f$.
|
||||
#define DBL_DECIMAL_DIG 17
|
||||
/// \brief The radix, or integer base, used to represent a \f$double\f$.
|
||||
#define DBL_RADIX 2
|
||||
/// \brief The minimum negative integer such that \f${DBL_RADIX}^{DBL_MIN_EXP}\f$ results in a normalized \f$double\f$.
|
||||
#define DBL_MIN_EXP -1021
|
||||
/// \brief The maximum positive integer such that \f${DBL_RADIX}^{DBL_MAX_EXP}\f$ results in a non-infinite \f$double\f$.
|
||||
#define DBL_MAX_EXP 1024
|
||||
/// \brief The minimum negative integer such that \f${10}^{DBL_MIN_EXP}\f$ results in a normalized \f$double\f$.
|
||||
#define DBL_MIN_10_EXP -307
|
||||
/// \brief The maximum positive integer such that \f${10}^{DBL_MAX_EXP}\f$ results in a non-infinite \f$double\f$.
|
||||
#define DBL_MAX_10_EXP 308
|
||||
/// \brief Do arithmetics operations with \f$double\f$ trap?
|
||||
#define DBL_TRAPS 0
|
||||
/// \brief Do arithmetics operations with \f$double\f$ check for underflow?
|
||||
#define DBL_TINYNESS_BEFORE 0
|
||||
/// \brief Smallest positive, finite, normal value of \f$double\f$.
|
||||
#define DBL_MIN fennec::bit_cast<double>(0x10000000000000ll)
|
||||
/// \brief Largest positive, finite value of \f$double\f$.
|
||||
#define DBL_MAX fennec::bit_cast<double>(0x7fefffffffffffffll)
|
||||
/// \brief The difference between \f$1.0\f$ and the next representable value of \f$double\f$.
|
||||
#define DBL_EPSILON fennec::bit_cast<double>(0x3cb0000000000000ll)
|
||||
/// \brief A value representing \f$\inf\f$ of type \f$double\f$.
|
||||
#define DBL_INF fennec::bit_cast<double>(0x7ff0000000000000ll)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$double\f$ that does not trap.
|
||||
#define DBL_QUIET_NAN fennec::bit_cast<double>(0x7ff8000000000000ll)
|
||||
/// \brief A value representing \f$NaN\f$ of type \f$double\f$ that traps.
|
||||
#define DBL_SIGNALING_NAN fennec::bit_cast<double>(0x7ff4000000000000ll)
|
||||
/// \brief Smallest positive, finite, subnormal value of \f$double\f$.
|
||||
#define DBL_DENORM_MIN fennec::bit_cast<double>(0x1ll)
|
||||
/// \brief Maximum rounding error of type \f$double\f$.
|
||||
#define DBL_ROUND_ERR fennec::bit_cast<double>(0x3fe0000000000000ll)
|
||||
|
||||
#endif // FENNEC_LANG_FLOAT_H
|
||||
|
||||
136
include/fennec/lang/function.h
Normal file
136
include/fennec/lang/function.h
Normal file
@@ -0,0 +1,136 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/function.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_LANG_FUNCTION_H
|
||||
#define FENNEC_LANG_FUNCTION_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/assert.h>
|
||||
#include <fennec/lang/utility.h>
|
||||
|
||||
#include <fennec/lang/detail/_function.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
///
|
||||
/// \brief a class capable of holding a function or non-capturing lambda
|
||||
template<typename> class function;
|
||||
|
||||
///
|
||||
/// \brief a class capable of holding a function or non-capturing lambda
|
||||
/// \tparam ReturnT The return type of the function
|
||||
/// \tparam ArgsT The argument types of the function
|
||||
template<typename ReturnT, typename...ArgsT>
|
||||
class function<ReturnT(ArgsT...)> {
|
||||
public:
|
||||
///
|
||||
/// \brief default constructor
|
||||
constexpr function() noexcept
|
||||
: call(nullptr) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
constexpr ~function() = default;
|
||||
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param func the function to copy
|
||||
constexpr function(const function& func) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
/// \param func the function to take ownership of
|
||||
constexpr function(function&& func) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief function constructor
|
||||
/// \param func the function to capture
|
||||
constexpr function(ReturnT (*func)(ArgsT...))
|
||||
: call(func) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief null constructor
|
||||
constexpr function(nullptr_t) noexcept : function() {}
|
||||
|
||||
///
|
||||
/// \brief copy assignment
|
||||
/// \param func the function to copy
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(const function& func) = default;
|
||||
|
||||
///
|
||||
/// \brief move assignment
|
||||
/// \param func the function to capture
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(function&& func) = default;
|
||||
|
||||
///
|
||||
/// \brief null assignment
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(nullptr_t) {
|
||||
call = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief function assignment
|
||||
/// \param func the function to capture
|
||||
/// \returns a reference to self
|
||||
constexpr function& operator=(ReturnT (*func)(ArgsT...)) {
|
||||
call = func;
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief implicit bool check
|
||||
/// \returns \f$true\f$ if a function is captured, \f$false\f$ otherwise
|
||||
constexpr operator bool() const noexcept {
|
||||
return call != nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief function call operator
|
||||
/// \param args the arguments to call the function with
|
||||
/// \returns the result of the function call
|
||||
ReturnT operator()(ArgsT...args) const noexcept {
|
||||
assertf(call != nullptr, "Attempted to call a null function object!");
|
||||
return call(fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
ReturnT (*call)(ArgsT&&...) { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_FUNCTION_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
#include <fennec/lang/type_traits.h>
|
||||
#include <fennec/lang/bits.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
@@ -30,10 +31,13 @@ namespace fennec
|
||||
/// \tparam Key The type to hash
|
||||
template<typename Key> struct hash;
|
||||
|
||||
// Murmur3 Hash for 64-bit ints
|
||||
/// \brief Murmur3 Hash for 64-bit ints
|
||||
template<>
|
||||
struct hash<uint64_t> {
|
||||
using type_t = uint64_t;
|
||||
using type_t = uint64_t; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param x the value to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(uint64_t x) const {
|
||||
// Murmur3
|
||||
x ^= x >> 33U;
|
||||
@@ -45,16 +49,22 @@ struct hash<uint64_t> {
|
||||
}
|
||||
};
|
||||
|
||||
// Wrapper for casting ints
|
||||
/// \brief Hashing for integer types
|
||||
/// \tparam IntT the integer type
|
||||
template<typename IntT>
|
||||
requires is_integral_v<IntT>
|
||||
struct hash<IntT> : hash<uint64_t> {
|
||||
using type_t = IntT;
|
||||
using type_t = IntT; //!< the type of the hash
|
||||
};
|
||||
|
||||
// Wrapper for pointers
|
||||
/// \brief Hashing for pointer types
|
||||
/// \tparam PtrT The base type
|
||||
template<typename PtrT>
|
||||
struct hash<PtrT*> : hash<uintptr_t> {
|
||||
using type_t = PtrT*; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param ptr the pointer to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(PtrT* ptr) const {
|
||||
return hash<uintptr_t>::operator()((uintptr_t)(const void*)ptr);
|
||||
}
|
||||
@@ -63,6 +73,10 @@ struct hash<PtrT*> : hash<uintptr_t> {
|
||||
// Float
|
||||
template<>
|
||||
struct hash<float> : hash<uint32_t> {
|
||||
using type_t = float; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param x the value to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(float x) const {
|
||||
return hash<uint32_t>::operator()(bit_cast<uint32_t>(x));
|
||||
}
|
||||
@@ -70,6 +84,10 @@ struct hash<float> : hash<uint32_t> {
|
||||
|
||||
template<>
|
||||
struct hash<double> : hash<uint64_t> {
|
||||
using type_t = double; //!< the type of the hash
|
||||
/// \brief hash operator
|
||||
/// \param x the value to hash
|
||||
/// \returns an integer hash for the value
|
||||
constexpr size_t operator()(double x) const {
|
||||
return hash<uint64_t>::operator()(bit_cast<uint64_t>(x));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,13 +17,15 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file integer.h
|
||||
/// \file fennec/lang/integer.h
|
||||
/// \brief metaprogramming integer type info
|
||||
///
|
||||
///
|
||||
/// \details this file is automatically generated for the current build environment
|
||||
/// \details This file is automatically generated for the current build environment.
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// Environment for this build: GNU Linux x86_64
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -51,114 +53,212 @@
|
||||
#undef ULLONG_MIN
|
||||
#undef ULLONG_MAX
|
||||
|
||||
/// \brief Is \f$char\f$ signed?
|
||||
#define CHAR_IS_SIGNED true
|
||||
/// \brief Rounding style of type \f$char\f$.
|
||||
#define CHAR_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$char\f$.
|
||||
#define CHAR_RADIX_DIG 0x7
|
||||
/// \brief Number of decimal digits represented by \f$char\f$.
|
||||
#define CHAR_DIG 0x2
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$char\f$.
|
||||
#define CHAR_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$char\f$.
|
||||
#define CHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$char\f$ trap?
|
||||
#define CHAR_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$char\f$.
|
||||
#define CHAR_MIN 0x80
|
||||
/// \brief Largest finite value of \f$char\f$.
|
||||
#define CHAR_MAX 0x7f
|
||||
|
||||
/// \brief Is \f$wchar_t\f$ signed?
|
||||
#define WCHAR_IS_SIGNED true
|
||||
/// \brief Rounding style of type \f$wchar_t\f$.
|
||||
#define WCHAR_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$wchar_t\f$.
|
||||
#define WCHAR_RADIX_DIG 0x1f
|
||||
/// \brief Number of decimal digits represented by \f$wchar_t\f$.
|
||||
#define WCHAR_DIG 0x9
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$wchar_t\f$.
|
||||
#define WCHAR_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$wchar_t\f$.
|
||||
#define WCHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$wchar_t\f$ trap?
|
||||
#define WCHAR_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$wchar_t\f$.
|
||||
#define WCHAR_MIN 0x80000000
|
||||
/// \brief Largest finite value of \f$wchar_t\f$.
|
||||
#define WCHAR_MAX 0x7fffffff
|
||||
|
||||
/// \brief Is \f$signed char\f$ signed?
|
||||
#define SCHAR_ROUNDS 0x0
|
||||
/// \brief Rounding style of type \f$signed char\f$.
|
||||
#define SCHAR_RADIX_DIG 0x7
|
||||
/// \brief Number of radix digits represented by \f$signed char\f$.
|
||||
#define SCHAR_DIG 0x2
|
||||
/// \brief Number of decimal digits represented by \f$signed char\f$.
|
||||
#define SCHAR_DECIMAL_DIG 0x0
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$signed char\f$.
|
||||
#define SCHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$signed char\f$ trap?
|
||||
#define SCHAR_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$signed char\f$.
|
||||
#define SCHAR_MIN 0x80
|
||||
/// \brief Largest finite value of \f$signed char\f$.
|
||||
#define SCHAR_MAX 0x7f
|
||||
|
||||
/// \brief Is \f$unsigned char\f$ unsigned?
|
||||
#define UCHAR_ROUNDS 0x0
|
||||
/// \brief Rounding style of type \f$unsigned char\f$.
|
||||
#define UCHAR_RADIX_DIG 0x8
|
||||
/// \brief Number of radix digits represented by \f$unsigned char\f$.
|
||||
#define UCHAR_DIG 0x2
|
||||
/// \brief Number of decimal digits represented by \f$unsigned char\f$.
|
||||
#define UCHAR_DECIMAL_DIG 0x0
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned char\f$.
|
||||
#define UCHAR_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned char\f$ trap?
|
||||
#define UCHAR_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned char\f$.
|
||||
#define UCHAR_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned char\f$.
|
||||
#define UCHAR_MAX 0xff
|
||||
|
||||
/// \brief Rounding style of type \f$short\f$.
|
||||
#define SHORT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$short\f$.
|
||||
#define SHORT_RADIX_DIG 0xf
|
||||
/// \brief Number of decimal digits represented by \f$short\f$.
|
||||
#define SHORT_DIG 0x4
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$short\f$.
|
||||
#define SHORT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$short\f$.
|
||||
#define SHORT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$short\f$ trap?
|
||||
#define SHORT_TRAPS 0xtrue
|
||||
#define SHORT_MIN 0x8000
|
||||
/// \brief Smallest finite value of \f$short\f$.
|
||||
#define SHORT_MIN 0xffff8000
|
||||
/// \brief Largest finite value of \f$short\f$.
|
||||
#define SHORT_MAX 0x7fff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned short\f$.
|
||||
#define USHORT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned short\f$.
|
||||
#define USHORT_RADIX_DIG 0x10
|
||||
/// \brief Number of decimal digits represented by \f$unsigned short\f$.
|
||||
#define USHORT_DIG 0x4
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned short\f$.
|
||||
#define USHORT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$unsigned short\f$.
|
||||
#define USHORT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned short\f$ trap?
|
||||
#define USHORT_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned short\f$.
|
||||
#define USHORT_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned short\f$.
|
||||
#define USHORT_MAX 0xffff
|
||||
|
||||
/// \brief Rounding style of type \f$int\f$.
|
||||
#define INT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$int\f$.
|
||||
#define INT_RADIX_DIG 0x1f
|
||||
/// \brief Number of decimal digits represented by \f$int\f$.
|
||||
#define INT_DIG 0x9
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$int\f$.
|
||||
#define INT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or integer base, used to represent a \f$int\f$.
|
||||
#define INT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$int\f$ trap?
|
||||
#define INT_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$int\f$.
|
||||
#define INT_MIN 0x80000000
|
||||
/// \brief Largest finite value of \f$int\f$.
|
||||
#define INT_MAX 0x7fffffff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned int\f$.
|
||||
#define UINT_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned int\f$.
|
||||
#define UINT_RADIX_DIG 0x20
|
||||
/// \brief Number of decimal digits represented by \f$unsigned int\f$.
|
||||
#define UINT_DIG 0x9
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned int\f$.
|
||||
#define UINT_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or unsigned integer base, used to represent a \f$unsigned int\f$.
|
||||
#define UINT_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned int\f$ trap?
|
||||
#define UINT_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned int\f$.
|
||||
#define UINT_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned int\f$.
|
||||
#define UINT_MAX 0xffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$long int\f$.
|
||||
#define LONG_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$long int\f$.
|
||||
#define LONG_RADIX_DIG 0x3f
|
||||
/// \brief Number of decimal digits represented by \f$long int\f$.
|
||||
#define LONG_DIG 0x12
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$long int\f$.
|
||||
#define LONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or long integer base, used to represent a \f$long int\f$.
|
||||
#define LONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$long int\f$ trap?
|
||||
#define LONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$long int\f$.
|
||||
#define LONG_MIN 0x8000000000000000
|
||||
/// \brief Largest finite value of \f$long int\f$.
|
||||
#define LONG_MAX 0x7fffffffffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned long int\f$.
|
||||
#define ULONG_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned long int\f$.
|
||||
#define ULONG_RADIX_DIG 0x40
|
||||
/// \brief Number of decimal digits represented by \f$unsigned long int\f$.
|
||||
#define ULONG_DIG 0x13
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned long int\f$.
|
||||
#define ULONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or unsigned long integer base, used to represent a \f$unsigned long int\f$.
|
||||
#define ULONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned long int\f$ trap?
|
||||
#define ULONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned long int\f$.
|
||||
#define ULONG_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned long int\f$.
|
||||
#define ULONG_MAX 0xffffffffffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$long long\f$.
|
||||
#define LLONG_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$long long\f$.
|
||||
#define LLONG_RADIX_DIG 0x3f
|
||||
/// \brief Number of decimal digits represented by \f$long long\f$.
|
||||
#define LLONG_DIG 0x12
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$long long\f$.
|
||||
#define LLONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or long longeger base, used to represent a \f$long long\f$.
|
||||
#define LLONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$long long\f$ trap?
|
||||
#define LLONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$long long\f$.
|
||||
#define LLONG_MIN 0x8000000000000000
|
||||
/// \brief Largest finite value of \f$long long\f$.
|
||||
#define LLONG_MAX 0x7fffffffffffffff
|
||||
|
||||
/// \brief Rounding style of type \f$unsigned long long\f$.
|
||||
#define ULLONG_ROUNDS 0x0
|
||||
/// \brief Number of radix digits represented by \f$unsigned long long\f$.
|
||||
#define ULLONG_RADIX_DIG 0x40
|
||||
/// \brief Number of decimal digits represented by \f$unsigned long long\f$.
|
||||
#define ULLONG_DIG 0x13
|
||||
/// \brief Number of decimal digits necessary to differentiate all values of type \f$unsigned long long\f$.
|
||||
#define ULLONG_DECIMAL_DIG 0x0
|
||||
/// \brief The radix, or unsigned long longeger base, used to represent a \f$unsigned long long\f$.
|
||||
#define ULLONG_RADIX 0x2
|
||||
/// \brief Do arithmetics operations with \f$unsigned long long\f$ trap?
|
||||
#define ULLONG_TRAPS 0xtrue
|
||||
/// \brief Smallest finite value of \f$unsigned long long\f$.
|
||||
#define ULLONG_MIN 0x0
|
||||
/// \brief Largest finite value of \f$unsigned long long\f$.
|
||||
#define ULLONG_MAX 0xffffffffffffffff
|
||||
|
||||
#endif // FENNEC_LANG_INTEGER_H
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,10 +17,10 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file intrinsics.h
|
||||
/// \file fennec/lang/intrinsics.h
|
||||
/// \brief \ref fennec_lang_intrinsics
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -40,59 +40,59 @@
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_BIT_CAST` <br>
|
||||
/// `Y FENNEC_BUILTIN_BIT_CAST(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_BIT_CAST\f$ <br>
|
||||
/// \f$Y FENNEC_BUILTIN_BIT_CAST(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// An intrinsic for doing a bitwise cast without using `reinterpret_cast`.
|
||||
/// An intrinsic for doing a bitwise cast without using \f$reinterpret_cast\f$.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_ADDRESSOF` <br>
|
||||
/// `Y FENNEC_BUILTIN_ADDRESSOF(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_ADDRESSOF\f$ <br>
|
||||
/// \f$Y FENNEC_BUILTIN_ADDRESSOF(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Obtains the true address of an object in circumstances where `operator&` is overloaded.
|
||||
/// Obtains the true address of an object in circumstances where \f$operator&\f$ is overloaded.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_CONVERTIBLE` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_CONVERTIBLE\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` can be converted to type `Y`.
|
||||
/// Checks if type \f$X\f$ can be converted to type \f$Y\f$.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_EMPTY` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_EMPTY(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_EMPTY\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_EMPTY(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` stores no data.
|
||||
/// Checks if type \f$X\f$ stores no data.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_POLYMORPHIC` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_POLYMORPHIC(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_POLYMORPHIC\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_POLYMORPHIC(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` is polymorphic, this is for classes only thus checks only for subtyping
|
||||
/// Checks if type \f$X\f$ is polymorphic, this is for classes only thus checks only for subtyping
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_FINAL` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_FINAL(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_FINAL\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_FINAL(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` is final, meaning a function or class cannot be derived from.
|
||||
/// Checks if type \f$X\f$ is final, meaning a function or class cannot be derived from.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_ABSTRACT` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_ABSTRACT(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_ABSTRACT\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_ABSTRACT(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Opposite of `FENNEC_BUILTIN_IS_FINAL`, checks if abstract, meaning `X` has at least one pure virtual function.
|
||||
/// Opposite of \f$FENNEC_BUILTIN_IS_FINAL\f$, checks if abstract, meaning \f$X\f$ has at least one pure virtual function.
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if `X` has a standard layout, here is [full criteria](https://www.cppreference.com/w/cpp/language/classes.html#Standard-layout_class)
|
||||
/// Checks if \f$X\f$ has a standard layout, here is [full criteria](https://www.cppreference.com/w/cpp/language/classes.html#Standard-layout_class)
|
||||
/// for this trait
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// `FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE` <br>
|
||||
/// `B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)`
|
||||
/// \f$FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE\f$ <br>
|
||||
/// \f$B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)\f$
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// Checks if type `X` is constructible with args `...`, such that `X::X(...)` exists.
|
||||
/// Checks if type \f$X\f$ is constructible with args \f$\ldots\f$, such that \f$X::X(...)\f$ exists.
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
@@ -102,6 +102,9 @@
|
||||
// Most major compilers support __has_builtin, notably GCC, MINGW, and CLANG
|
||||
#if defined(__has_builtin)
|
||||
|
||||
|
||||
// UTILITIES ===========================================================================================================
|
||||
|
||||
// addressof is very difficult to implement without intrinsics.
|
||||
#if __has_builtin(__builtin_addressof)
|
||||
# define FENNEC_HAS_BUILTIN_ADDRESSOF 1
|
||||
@@ -118,6 +121,37 @@
|
||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_LINE)
|
||||
# define FENNEC_HAS_BUILTIN_LINE 1
|
||||
# define FENNEC_BUILTIN_LINE() __builtin_LINE()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_LINE 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_COLUMN)
|
||||
# define FENNEC_HAS_BUILTIN_COLUMN 1
|
||||
# define FENNEC_BUILTIN_COLUMN() __builtin_COLUMN()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_COLUMN 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_FILE)
|
||||
# define FENNEC_HAS_BUILTIN_FILE 1
|
||||
# define FENNEC_BUILTIN_FILE() __builtin_FILE()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_FILE 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__builtin_FUNCTION)
|
||||
# define FENNEC_HAS_BUILTIN_FUNCTION 1
|
||||
# define FENNEC_BUILTIN_FUNCTION() __builtin_FUNCTION()
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_FUNCTION 0
|
||||
#endif
|
||||
|
||||
|
||||
// PROPERTIES ==========================================================================================================
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_abstract)
|
||||
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 1
|
||||
@@ -134,6 +168,14 @@
|
||||
# define FENNEC_HAS_BUILTIN_IS_ARRAY
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_bounded_array)
|
||||
# define FENNEC_HAS_BUILTIN_IS_BOUNDED_ARRAY 1
|
||||
# define FENNEC_BUILTIN_IS_BOUNDED_ARRAY(arg) __is_bounded_array(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_BOUNDED_ARRAY
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_class)
|
||||
# define FENNEC_HAS_BUILTIN_IS_CLASS 1
|
||||
@@ -142,6 +184,36 @@
|
||||
# define FENNEC_HAS_BUILTIN_IS_CLASS
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_scoped_enum)
|
||||
# define FENNEC_HAS_BUILTIN_IS_SCOPED_ENUM 1
|
||||
# define FENNEC_BUILTIN_IS_SCOPED_ENUM(arg) __is_scoped_enum(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_SCOPED_ENUM
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__is_member_pointer)
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_POINTER 1
|
||||
# define FENNEC_BUILTIN_IS_MEMBER_POINTER(arg) __is_member_pointer(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_POINTER 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__is_member_function_pointer)
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_FUNCTION_POINTER 1
|
||||
# define FENNEC_BUILTIN_IS_MEMBER_FUNCTION_POINTER(arg) __is_member_function_pointer(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_FUNCTION_POINTER 0
|
||||
#endif
|
||||
|
||||
#if __has_builtin(__is_member_object_pointer)
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_OBJECT_POINTER 1
|
||||
# define FENNEC_BUILTIN_IS_MEMBER_OBJECT_POINTER(arg) __is_member_object_pointer(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_MEMBER_OBJECT_POINTER 0
|
||||
#endif
|
||||
|
||||
|
||||
// CONSTRUCTORS ========================================================================================================
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
@@ -155,11 +227,19 @@
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivially_constructible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE(type) __is_trivially_constructible(type)
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE(type, ...) __is_trivially_constructible(type __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_nothrow_constructible)
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE 1
|
||||
# define FENNEC_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE(type, ...) __is_nothrow_constructible(type __VA_OPT__(,) __VA_ARGS__)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_CONSTRUCTIBLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__has_trivial_destructor)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1
|
||||
@@ -179,6 +259,54 @@
|
||||
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivially_assignable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_ASSIGNABLE(a, b) __is_trivially_assignable(a, b)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_ASSIGNABLE 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_nothrow_assignable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_ASSIGNABLE 1
|
||||
# define FENNEC_BUILTIN_IS_NOTHROW_ASSIGNABLE(a, b) __is_nothrow_assignable(a, b)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_NOTHROW_ASSIGNABLE 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivial)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIAL 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIAL(a) __is_trivial(a)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIAL 0
|
||||
#endif
|
||||
|
||||
// Difficult and Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_trivially_copyable)
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_COPYABLE 1
|
||||
# define FENNEC_BUILTIN_IS_TRIVIALLY_COPYABLE(a) __is_trivially_copyable(a)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_COPYABLE 0
|
||||
#endif
|
||||
|
||||
// Impossible without instrinsics
|
||||
#if __has_builtin(__is_standard_layout)
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
|
||||
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 0
|
||||
#endif
|
||||
|
||||
// Impossible without instrinsics
|
||||
#if __has_builtin(__has_unique_object_representations)
|
||||
# define FENNEC_HAS_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 1
|
||||
# define FENNEC_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS(arg) __has_unique_object_representations(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_HAS_UNIQUE_OBJECT_REPRESENTATIONS 0
|
||||
#endif
|
||||
|
||||
|
||||
// Type Traits
|
||||
// can_convert is also very difficult to implement without intrinsics
|
||||
@@ -213,6 +341,14 @@
|
||||
# define FENNEC_HAS_BUILTIN_IS_ENUM 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics.
|
||||
#if __has_builtin(__is_union)
|
||||
# define FENNEC_HAS_BUILTIN_IS_UNION 1
|
||||
# define FENNEC_BUILTIN_IS_UNION(arg) __is_union(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_UNION 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_final)
|
||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
|
||||
@@ -221,12 +357,36 @@
|
||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
|
||||
#endif
|
||||
|
||||
// Inconsistent with dynamic intrinsics, requires a massive table for static intrinsics
|
||||
#if __has_builtin(__is_fundamental)
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 1
|
||||
# define FENNEC_BUILTIN_IS_FUNDAMENTAL(arg) __is_fundamental(arg)
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_aggregate)
|
||||
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 1
|
||||
# define FENNEC_BUILTIN_IS_AGGREGATE(arg) __is_aggregate(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 0
|
||||
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__builtin_is_implicit_lifetime)
|
||||
# define FENNEC_HAS_BUILTIN_IS_IMPLICIT_LIFETIME 1
|
||||
# define FENNEC_BUILTIN_IS_IMPLICIT_LIFETIME(arg) __builtin_is_implicit_lifetime(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_IMPLICIT_LIFETIME 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_function)
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 1
|
||||
# define FENNEC_BUILTIN_IS_FUNCTION(arg) __is_function(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
#if __has_builtin(__is_object)
|
||||
# define FENNEC_HAS_BUILTIN_IS_OBJECT 1
|
||||
# define FENNEC_BUILTIN_IS_OBJECT(arg) __is_object(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
|
||||
#endif
|
||||
|
||||
// Inconsistent without intrinsics
|
||||
@@ -237,14 +397,6 @@
|
||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 0
|
||||
#endif
|
||||
|
||||
// Impossible without instrinsics
|
||||
#if __has_builtin(__is_standard_layout)
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 1
|
||||
# define FENNEC_BUILTIN_IS_STANDARD_LAYOUT(arg) __is_standard_layout(arg)
|
||||
#else
|
||||
# define FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT 0
|
||||
#endif
|
||||
|
||||
|
||||
// For compilers without or differently named builtins
|
||||
#else
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file lang.h
|
||||
/// \file fennec/lang/lang.h
|
||||
/// \brief \ref fennec_lang
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file limits.h
|
||||
/// \file fennec/lang/limits.h
|
||||
/// \brief \ref fennec_lang_limits
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -222,42 +222,42 @@ enum float_round_style
|
||||
/// \tparam TypeT Numeric types, may be overloaded for custom types
|
||||
template<typename TypeT> struct numeric_limits
|
||||
{
|
||||
static constexpr bool is_specialized = false; ///< Check if the template is specialized for TypeT
|
||||
static constexpr bool is_signed = false; ///< Check if TypeT is signed
|
||||
static constexpr bool is_integer = false; ///< Check if TypeT is of an integral type
|
||||
static constexpr bool is_exact = false; ///< Check if TypeT is exact in its precision
|
||||
static constexpr bool has_infinity = false; ///< Check if TypeT can hold a value representing infinity
|
||||
static constexpr bool has_quiet_nan = false; ///< Check if TypeT can hold a non-signaling nan
|
||||
static constexpr bool has_signaling_nan = false; ///< Check if TypeT can hold a signaling nan
|
||||
static constexpr bool has_denorm = false; ///< Check if TypeT denormalizes
|
||||
static constexpr bool has_denorm_loss = false; ///< Check if TypeT has precision loss when denormalized
|
||||
static constexpr bool is_iec559 = false; ///< Check if a TypeT representing a float is IEC 559 or IEEE 754
|
||||
static constexpr bool is_bounded = false; ///< Check if TypeT represents a finite set of values
|
||||
static constexpr bool is_modulo = false; ///< Check if TypeT can handle modulo arithmetic
|
||||
static constexpr bool tinyness_before = false; ///< Check if TypeT checks for tinyness before rounding
|
||||
static constexpr bool traps = false; ///< Check if TypeT can cause operations to trap
|
||||
static constexpr bool is_specialized = false; //!< Check if the template is specialized for TypeT
|
||||
static constexpr bool is_signed = false; //!< Check if TypeT is signed
|
||||
static constexpr bool is_integer = false; //!< Check if TypeT is of an integral type
|
||||
static constexpr bool is_exact = false; //!< Check if TypeT is exact in its precision
|
||||
static constexpr bool has_infinity = false; //!< Check if TypeT can hold a value representing infinity
|
||||
static constexpr bool has_quiet_nan = false; //!< Check if TypeT can hold a non-signaling nan
|
||||
static constexpr bool has_signaling_nan = false; //!< Check if TypeT can hold a signaling nan
|
||||
static constexpr bool has_denorm = false; //!< Check if TypeT denormalizes
|
||||
static constexpr bool has_denorm_loss = false; //!< Check if TypeT has precision loss when denormalized
|
||||
static constexpr bool is_iec559 = false; //!< Check if a TypeT representing a float is IEC 559 or IEEE 754
|
||||
static constexpr bool is_bounded = false; //!< Check if TypeT represents a finite set of values
|
||||
static constexpr bool is_modulo = false; //!< Check if TypeT can handle modulo arithmetic
|
||||
static constexpr bool tinyness_before = false; //!< Check if TypeT checks for tinyness before rounding
|
||||
static constexpr bool traps = false; //!< Check if TypeT can cause operations to trap
|
||||
|
||||
static constexpr int radix = 0; ///< Get the base representation of the type
|
||||
static constexpr int digits = 0; ///< Get the number of radix digits TypeT represents
|
||||
static constexpr int digits10 = 0; ///< Get the number of decimal digits TypeT represents
|
||||
static constexpr int max_digits10 = 0; ///< Get the maximum number of decimal digits TypeT represents
|
||||
static constexpr int min_exponent = 0; ///< Get the minimum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int min_exponent10 = 0; ///< Get the minimum number of decimal digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent = 0; ///< Get the maximum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent10 = 0; ///< Get the maximum number of decimal digits that represent the exponent of TypeT
|
||||
static constexpr int radix = 0; //!< Get the base representation of the type
|
||||
static constexpr int digits = 0; //!< Get the number of radix digits TypeT represents
|
||||
static constexpr int digits10 = 0; //!< Get the number of decimal digits TypeT represents
|
||||
static constexpr int max_digits10 = 0; //!< Get the maximum number of decimal digits TypeT represents
|
||||
static constexpr int min_exponent = 0; //!< Get the minimum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int min_exponent10 = 0; //!< Get the minimum number of decimal digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent = 0; //!< Get the maximum number of radix digits that represent the exponent of TypeT
|
||||
static constexpr int max_exponent10 = 0; //!< Get the maximum number of decimal digits that represent the exponent of TypeT
|
||||
|
||||
static constexpr float_round_style rounding_style = round_indeterminate; ///< The rounding style of TypeT
|
||||
static constexpr float_round_style rounding_style = round_indeterminate; //!< The rounding style of TypeT
|
||||
|
||||
// This is very poorly named and defined in the C++ Standard so these functions differ
|
||||
static constexpr TypeT min() { return TypeT(); } ///< Returns the minimum finite value of TypeT
|
||||
static constexpr TypeT max() { return TypeT(); } ///< Returns the maximum finite value of TypeT
|
||||
static constexpr TypeT lowest() { return TypeT(); } ///< Returns the smallest positive value of TypeT
|
||||
static constexpr TypeT epsilon() { return TypeT(); } ///< Returns the difference between 1.0 and the next representable value
|
||||
static constexpr TypeT round_error() { return TypeT(); } ///< Returns the max rounding error of TypeT
|
||||
static constexpr TypeT infinity() { return TypeT(); } ///< Returns a value of TypeT holding a positive infinity
|
||||
static constexpr TypeT quiet_NaN() { return TypeT(); } ///< Returns a value of TypeT holding a quiet NaN
|
||||
static constexpr TypeT signaling_NaN() { return TypeT(); } ///< Returns a value of TypeT holding a signaling NaN
|
||||
static constexpr TypeT denorm_min() { return TypeT(); } ///< Returns a value of TypeT holding the smallest positive subnormal
|
||||
static constexpr TypeT min() { return TypeT(); } //!< \returns the minimum finite value of TypeT
|
||||
static constexpr TypeT max() { return TypeT(); } //!< \returns the maximum finite value of TypeT
|
||||
static constexpr TypeT lowest() { return TypeT(); } //!< \returns the smallest positive value of TypeT
|
||||
static constexpr TypeT epsilon() { return TypeT(); } //!< \returns the difference between 1.0 and the next representable value
|
||||
static constexpr TypeT round_error() { return TypeT(); } //!< \returns the max rounding error of TypeT
|
||||
static constexpr TypeT infinity() { return TypeT(); } //!< \returns a value of TypeT holding a positive infinity
|
||||
static constexpr TypeT quiet_NaN() { return TypeT(); } //!< \returns a value of TypeT holding a quiet NaN
|
||||
static constexpr TypeT signaling_NaN() { return TypeT(); } //!< \returns a value of TypeT holding a signaling NaN
|
||||
static constexpr TypeT denorm_min() { return TypeT(); } //!< \returns a value of TypeT holding the smallest positive subnormal
|
||||
};
|
||||
|
||||
// Overload definitions for basic types
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file metaprogramming.h
|
||||
/// \file fennec/lang/metaprogramming.h
|
||||
/// \brief \ref fennec_lang
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
/// - \subpage fennec_lang_constants
|
||||
/// - \subpage fennec_lang_conditional_types
|
||||
/// - \subpage fennec_lang_numeric_transforms
|
||||
/// - \subpage fennec_lang_sequences
|
||||
/// - \subpage fennec_lang_metasequences
|
||||
/// - \subpage fennec_lang_type_identity
|
||||
/// - \subpage fennec_lang_type_sequences
|
||||
/// - \subpage fennec_lang_type_traits
|
||||
/// - \subpage fennec_lang_type_transforms
|
||||
|
||||
246
include/fennec/lang/metasequences.h
Normal file
246
include/fennec/lang/metasequences.h
Normal file
@@ -0,0 +1,246 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/metasequences.h
|
||||
/// \brief \ref fennec_lang_metasequences
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_SEQUENCES_H
|
||||
#define FENNEC_LANG_SEQUENCES_H
|
||||
|
||||
///
|
||||
/// \page fennec_lang_metasequences Metasequences
|
||||
///
|
||||
/// \brief This header is part of the metaprogramming library. It defines structures for metasequences of values, used during compile time.
|
||||
///
|
||||
/// \code #include <fennec/lang/metasequences.h> \endcode
|
||||
///
|
||||
/// <table width="100%" class="fieldtable" id="table_fennec_lang_metasequences">
|
||||
/// <tr><th style="vertical-align: top">Syntax
|
||||
/// <th style="vertical-align: top">Description
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::metasequence "metasequence<ValueT, Values...>"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::metasequence
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::integer_metasequence "integer_metasequence<IntT, Values...>"<br>
|
||||
/// \ref fennec::make_integer_metasequence "typename make_integer_metasequence<IntT, N>::type"<br>
|
||||
/// \ref fennec::make_integer_metasequence_t "make_integer_metasequence_t<IntT, N>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::integer_metasequence
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::index_metasequence "index_metasequence<Indices...>"<br>
|
||||
/// \ref fennec::make_index_metasequence "typename make_index_metasequence<N>::type"<br>
|
||||
/// \ref fennec::make_index_metasequence_t "make_index_metasequence_t<N>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::index_metasequence
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::concat_metasequence "typename concat_metasequence<metasequenceT0, metasequenceT1>::type"<br>
|
||||
/// \ref fennec::concat_metasequence_t "concat_metasequence_t<metasequenceT0, metasequenceT1>"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydetails fennec::concat_metasequence
|
||||
///
|
||||
/// </table>
|
||||
///
|
||||
|
||||
#include <fennec/lang/type_traits.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// fennec::metasequence ====================================================================================================
|
||||
|
||||
///
|
||||
/// \brief metaprogramming sequence
|
||||
///
|
||||
/// \details Stores a sequence of values of type \f$ValueT\f$ as a template pack.
|
||||
/// You can access the parameter pack in another template function, i.e.
|
||||
/// \code{cpp}
|
||||
/// template<typename TypeT, TypeT...Values>
|
||||
/// constexpr TypeT summation(metasequence<TypeT, Values...>)
|
||||
/// {
|
||||
/// return (Values + ...);
|
||||
/// }
|
||||
/// \endcode
|
||||
/// \tparam ValueT type of the values
|
||||
/// \tparam Values sequence values
|
||||
template<typename ValueT, ValueT...Values> struct metasequence
|
||||
{
|
||||
/// \brief type of the metasequence
|
||||
using value_type = ValueT;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = metasequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
///
|
||||
/// \return number of elements in the array
|
||||
inline static constexpr size_t size() noexcept {
|
||||
return sizeof...(Values);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// fennec::integer_metasequence ============================================================================================
|
||||
|
||||
///
|
||||
/// \brief metaprogramming integral metasequence
|
||||
///
|
||||
/// \details A `fennec::metasequence` specialized integer types.
|
||||
/// \tparam IntT type of the values, must satisfy `fennec::is_integral<T>`
|
||||
/// \tparam Values sequence values
|
||||
template<typename IntT, IntT...Values> requires(is_integral_v<IntT>)
|
||||
struct integer_metasequence : metasequence<IntT, Values...>
|
||||
{
|
||||
/// \brief type of the sequence
|
||||
using value_type = IntT;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = integer_metasequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
///
|
||||
/// \return number of elements in the array
|
||||
inline static constexpr size_t size() noexcept {
|
||||
return sizeof...(Values);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief generate a fennec::integer_metasequence \f$\left[\,0\,\ldots\,N\,\right)\f$
|
||||
///
|
||||
/// \details
|
||||
/// \tparam IntT type of the values, must satisfy `fennec::is_integral<T>`
|
||||
/// \tparam N size of the metasequence to generate
|
||||
template<typename IntT, size_t N> struct make_integer_metasequence;
|
||||
|
||||
///
|
||||
/// \brief shorthand for `typename fennec::make_integer_sequence<T, N>::type`
|
||||
template<typename IntT, size_t N> using make_integer_metasequence_t = typename make_integer_metasequence<IntT, N>::type;
|
||||
|
||||
|
||||
|
||||
// fennec::index_metasequence ==============================================================================================
|
||||
|
||||
///
|
||||
/// \brief metaprogramming integral metasequence
|
||||
///
|
||||
/// \details A `fennec::integer_metasequence` specialized for sequences of `size_t` indices.
|
||||
/// \tparam Indices sequence values
|
||||
template<size_t...Indices> struct index_metasequence : integer_metasequence<size_t, Indices...>
|
||||
{
|
||||
/// \brief type of the sequence
|
||||
using value_type = size_t;
|
||||
|
||||
/// \brief self-referential type
|
||||
using type = index_metasequence;
|
||||
|
||||
///
|
||||
/// \brief returns the number of elements
|
||||
///
|
||||
/// \return number of elements in the array
|
||||
inline static constexpr size_t size() noexcept {
|
||||
return sizeof...(Indices);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
///
|
||||
/// \brief generate a fennec::index_metasequence \f$\left[\,0\,\ldots\,N\,\right)\f$
|
||||
///
|
||||
/// \details
|
||||
/// \tparam T type of the values, must satisfy `fennec::is_integral<T>`
|
||||
/// \tparam N size of the sequence to generate
|
||||
template<size_t N> struct make_index_metasequence;
|
||||
|
||||
///
|
||||
/// \brief shorthand for `typename fennec::make_index_metasequence<N>::type`
|
||||
template<size_t N> using make_index_metasequence_t = typename make_index_metasequence<N>::type;
|
||||
|
||||
|
||||
|
||||
// fennec::concat_metasequence =============================================================================================
|
||||
|
||||
///
|
||||
/// \brief concatenate two metasequences
|
||||
///
|
||||
/// \details A tool for concatenating two `fennec::metasequence` types.
|
||||
/// \tparam SequenceT0 lhs
|
||||
/// \tparam SequenceT1 rhs
|
||||
template<typename SequenceT0, typename SequenceT1> struct concat_metasequence;
|
||||
|
||||
///
|
||||
/// \brief shorthand for `typename fennec::concat_metasequence<SequenceT0, SequenceT1>::type`
|
||||
template<typename SequenceT0, typename SequenceT1> using concat_metasequence_t
|
||||
= typename concat_metasequence<SequenceT0, SequenceT1>::type;
|
||||
|
||||
|
||||
|
||||
// Internal ============================================================================================================
|
||||
|
||||
// Implementation for Generating an integer_sequence
|
||||
template<typename T, size_t N> struct make_integer_metasequence : concat_metasequence_t<make_integer_metasequence_t<T, N / 2>, make_integer_metasequence_t<T, N - N / 2>>{};
|
||||
|
||||
// Base Case of N=0
|
||||
template<typename T> struct make_integer_metasequence<T, 0> : integer_metasequence<T> {};
|
||||
|
||||
// Base Case of N=1
|
||||
template<typename T> struct make_integer_metasequence<T, 1> : integer_metasequence<T, 0>{};
|
||||
|
||||
|
||||
// Implementation for Generating an index_sequence
|
||||
template<size_t N> struct make_index_metasequence : concat_metasequence_t<make_index_metasequence_t<N / 2>, make_index_metasequence_t<N - N / 2>>{};
|
||||
|
||||
// Base Case of N=0
|
||||
template<> struct make_index_metasequence<0> : index_metasequence<> {};
|
||||
|
||||
// Base Case of N=1
|
||||
template<> struct make_index_metasequence<1> : index_metasequence<0>{};
|
||||
|
||||
|
||||
// Specialization for integer sequences
|
||||
template<typename T, T...SequenceV0, T...SequenceV1>
|
||||
struct concat_metasequence<integer_metasequence<T, SequenceV0...>, integer_metasequence<T, SequenceV1...>>
|
||||
: integer_metasequence<T, SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
|
||||
// Specialization for index sequences
|
||||
template<size_t...SequenceV0, size_t...SequenceV1>
|
||||
struct concat_metasequence<index_metasequence<SequenceV0...>, index_metasequence<SequenceV1...>>
|
||||
: index_metasequence<SequenceV0..., (sizeof...(SequenceV0) + SequenceV1)...>{};
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_SEQUENCES_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file numeric_transforms.h
|
||||
/// \file fennec/lang/numeric_transforms.h
|
||||
/// \brief \ref fennec_lang_numeric_transforms
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_NUMERIC_TRANSFORMS_H
|
||||
|
||||
104
include/fennec/lang/ranges.h
Normal file
104
include/fennec/lang/ranges.h
Normal file
@@ -0,0 +1,104 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file fennec/lang/ranges.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
#ifndef FENNEC_LANG_RANGES_H
|
||||
#define FENNEC_LANG_RANGES_H
|
||||
|
||||
#include <fennec/lang/types.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \tparam ContainerT the container type
|
||||
/// \param c the container to iterate on
|
||||
/// \returns an iterator at the start of the container
|
||||
template<typename ContainerT>
|
||||
inline constexpr auto begin(ContainerT& c) noexcept(noexcept(c.begin())) -> decltype(c.begin()) {
|
||||
return c.begin();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \tparam ContainerT the container type
|
||||
/// \param c the container to iterate on
|
||||
/// \returns an iterator at the start of the container
|
||||
template<typename ContainerT>
|
||||
inline constexpr auto begin(const ContainerT& c) noexcept(noexcept(c.begin())) -> decltype(c.begin()) {
|
||||
return c.begin();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$begin()\f$
|
||||
/// \tparam T the element type
|
||||
/// \tparam N the bounds of the array
|
||||
/// \param arr a bounded array to iterate on
|
||||
/// \returns an iterator at the start of the array
|
||||
template<typename T, size_t N>
|
||||
inline constexpr T* begin(T (&arr)[N]) noexcept {
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \tparam ContainerT the container type
|
||||
/// \param c the container to iterate on
|
||||
/// \returns an iterator at the end of the container
|
||||
template<typename ContainerT>
|
||||
inline constexpr auto end(ContainerT& c) noexcept(noexcept(c.end())) -> decltype(c.end()) {
|
||||
return c.end();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \tparam ContainerT the container type
|
||||
/// \param c the container to iterate on
|
||||
/// \returns an iterator at the end of the container
|
||||
template<typename ContainerT>
|
||||
inline constexpr auto end(const ContainerT& c) noexcept(noexcept(c.end())) -> decltype(c.end()) {
|
||||
return c.end();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief C++ Iterator Specification \f$end()\f$
|
||||
/// \tparam T the element type
|
||||
/// \tparam N the bounds of the array
|
||||
/// \param arr a bounded array to iterate on
|
||||
/// \returns an iterator at the end of the array
|
||||
template<typename T, size_t N>
|
||||
inline constexpr T* end(T (&arr)[N]) noexcept {
|
||||
return arr + N;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_LANG_RANGES_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -19,10 +19,19 @@
|
||||
#ifndef FENNEC_LANG_STARTUP_H
|
||||
#define FENNEC_LANG_STARTUP_H
|
||||
|
||||
// Helper for running a function before main()
|
||||
#define STATIC_CONSTRUCTOR(f) \
|
||||
///
|
||||
/// \brief Macro for running a function before main
|
||||
/// \param f the name of the function
|
||||
#define FENNEC_PRIVATE_STATIC_CONSTRUCTOR(f) \
|
||||
inline static void f(void); \
|
||||
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
|
||||
inline static void f(void)
|
||||
|
||||
///
|
||||
/// \brief Macro for running a function before main in a class scope
|
||||
/// \param f the name of the function
|
||||
#define FENNEC_CLASS_STATIC_CONSTRUCTOR(f) \
|
||||
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
|
||||
inline static void f(void)
|
||||
|
||||
#endif // FENNEC_LANG_STARTUP_H
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -26,26 +26,38 @@ namespace fennec
|
||||
|
||||
// has_equals ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if a type has a defined equality operator
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0>
|
||||
struct has_equals {
|
||||
private:
|
||||
// Use SFINAE to check for the operator
|
||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() == declval<V>());
|
||||
template<typename, typename> static auto test(...) -> false_type;
|
||||
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
|
||||
public:
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
|
||||
};
|
||||
|
||||
template<typename T0, typename T1 = T0> constexpr bool has_equals_v = has_equals<T0, T1>::value;
|
||||
|
||||
// has_nequals =========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if a type has a defined inequality operator
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0>
|
||||
struct has_nequals {
|
||||
private:
|
||||
// Use SFINAE to check for the operator
|
||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() != declval<V>());
|
||||
template<typename, typename> static auto test(...) -> false_type;
|
||||
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
|
||||
public:
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
|
||||
};
|
||||
|
||||
template<typename T0, typename T1 = T0> constexpr bool has_nequals_v = has_nequals<T0, T1>::value;
|
||||
@@ -53,13 +65,19 @@ template<typename T0, typename T1 = T0> constexpr bool has_nequals_v = has_nequa
|
||||
|
||||
// has_less ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if a type has a defined less operator
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0>
|
||||
struct has_less {
|
||||
private:
|
||||
// Use SFINAE to check for the operator
|
||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() < declval<V>());
|
||||
template<typename, typename> static auto test(...) -> false_type;
|
||||
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
|
||||
public:
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
|
||||
};
|
||||
|
||||
template<typename T0, typename T1 = T0> constexpr bool has_less_v = has_less<T0, T1>::value;
|
||||
@@ -67,13 +85,19 @@ template<typename T0, typename T1 = T0> constexpr bool has_less_v = has_less<T0,
|
||||
|
||||
// has_less_equals =====================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if a type has a defined less equals operator
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0>
|
||||
struct has_less_equals {
|
||||
private:
|
||||
// Use SFINAE to check for the operator
|
||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() <= declval<V>());
|
||||
template<typename, typename> static auto test(...) -> false_type;
|
||||
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
|
||||
public:
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
|
||||
};
|
||||
|
||||
template<typename T0, typename T1 = T0> constexpr bool has_less_equals_v = has_less_equals<T0, T1>::value;
|
||||
@@ -81,13 +105,19 @@ template<typename T0, typename T1 = T0> constexpr bool has_less_equals_v = has_l
|
||||
|
||||
// has_greater =========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if a type has a defined greater operator
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0>
|
||||
struct has_greater {
|
||||
private:
|
||||
// Use SFINAE to check for the operator
|
||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() > declval<V>());
|
||||
template<typename, typename> static auto test(...) -> false_type;
|
||||
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
|
||||
public:
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
|
||||
};
|
||||
|
||||
template<typename T0, typename T1 = T0> constexpr bool has_greater_v = has_greater<T0, T1>::value;
|
||||
@@ -95,13 +125,19 @@ template<typename T0, typename T1 = T0> constexpr bool has_greater_v = has_great
|
||||
|
||||
// has_greater_equals ==================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if a type has a defined greater equals operator
|
||||
/// \tparam T0 The first type
|
||||
/// \tparam T1 The second type
|
||||
template<typename T0, typename T1 = T0>
|
||||
struct has_greater_equals {
|
||||
private:
|
||||
// Use SFINAE to check for the operator
|
||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() >= declval<V>());
|
||||
template<typename, typename> static auto test(...) -> false_type;
|
||||
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>;
|
||||
public:
|
||||
static constexpr bool value = is_same_v<bool, decltype(test<T0, T1>(0))>; //!< The result of the check
|
||||
};
|
||||
|
||||
template<typename T0, typename T1 = T0> constexpr bool has_greater_equals_v = has_greater_equals<T0, T1>::value;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
// Copyright © 2025 - 2026 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -17,14 +17,14 @@
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file type_sequences.h
|
||||
/// \file fennec/lang/type_sequences.h
|
||||
/// \brief \ref fennec_lang_type_sequences
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
@@ -51,6 +51,12 @@
|
||||
/// \ref fennec::replace_first_element "typename replace_first_element<ClassT, SubT, OriginT, RestT...>::type"<br>
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydoc fennec::replace_first_element
|
||||
///
|
||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||
/// \ref fennec::is_unique "is_unique<TypesT...>::value"<br>
|
||||
/// \ref fennec::is_unique_v "is_unique_v<TypesT...>"
|
||||
/// <td width="50%" style="vertical-align: top">
|
||||
/// \copydoc fennec::is_unique
|
||||
/// </table>
|
||||
///
|
||||
|
||||
@@ -59,23 +65,47 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
|
||||
template<typename...TypesT> struct type_sequence {};
|
||||
|
||||
|
||||
// fennec::first_element ===============================================================================================
|
||||
|
||||
///
|
||||
/// \brief Get the first element of a template parameter pack
|
||||
/// \tparam TypesT the Parameter Pack
|
||||
template<typename...TypesT> struct first_element : detail::_first_element<TypesT...> {};
|
||||
|
||||
///
|
||||
/// \brief alias for first_element<TypesT>::type
|
||||
/// \brief alias for `first_element<TypesT...>::type`
|
||||
/// \tparam TypesT the Parameter Pack
|
||||
template<typename...TypesT> using first_element_t = typename first_element<TypesT...>::type;
|
||||
|
||||
|
||||
template<size_t n, typename...TypesT> struct nth_element : detail::_nth_element<n, 0, TypesT...> {};
|
||||
|
||||
// fennec::nth_element =================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Take a Template with a Pack `ClassT<ArgsT...>` and replace the first `ArgT` of `ArgsT...` with `SubT`
|
||||
/// \brief Gets the type of the nth element of the type sequence `TypesT...`
|
||||
/// \tparam n The index in the type sequence
|
||||
/// \tparam TypesT The type sequence
|
||||
template<size_t n, typename...TypesT> struct nth_element : detail::_nth_element<n, 0, TypesT...> {};
|
||||
|
||||
///
|
||||
/// \brief alias for nth_element<n, TypesT>::type
|
||||
/// \tparam n The index in the type sequence
|
||||
/// \tparam TypesT the Parameter Pack
|
||||
template<size_t n, typename...TypesT> using nth_element_t = nth_element<n, TypesT...>::type;
|
||||
|
||||
|
||||
|
||||
// fennec::replace_first_element =======================================================================================
|
||||
|
||||
///
|
||||
/// \brief Take a Template with a Pack `ClassT<ArgsT...>` and replace the first \f$ArgT\f$ of `ArgsT...` with \f$SubT\f$
|
||||
template<typename ClassT, typename SubT> struct replace_first_element { };
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// Implementation
|
||||
template<
|
||||
template<typename, typename...> class ClassT // The Base Template
|
||||
@@ -83,6 +113,106 @@ template<
|
||||
, typename... RestT> // The Rest of the Parameter Pack
|
||||
struct replace_first_element<ClassT<OriginT, RestT...>, SubT> // Specialization
|
||||
{ using type = ClassT<SubT, RestT...>; }; // Definition
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// fennec::max_element_size ============================================================================================
|
||||
|
||||
///
|
||||
/// \brief Gets the max value of the size of each type in the sequence, i.e. `max(sizeof(Ts)...)`
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename...Ts> struct max_element_size : detail::_max_element_size<0, Ts...> {};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for `max_element_size<Ts...>::value`
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename...Ts> constexpr size_t max_element_size_v = max_element_size<Ts...>::value;
|
||||
|
||||
|
||||
|
||||
// fennec::find_element ================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Finds the index of \f$T\f$ in \f$Ts\f$, if \f$T\f$ is not found, results in `sizeof...(Ts)`
|
||||
/// \tparam T The type to find
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename T, typename...Ts> struct find_element : detail::_find_element<0, T, Ts...> {};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for `find_element<T, Ts...>::value`
|
||||
/// \tparam T The type to find
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename T, typename...Ts> constexpr size_t find_element_v = find_element<T, Ts...>::value;
|
||||
|
||||
|
||||
|
||||
// fennec::search_element ==============================================================================================
|
||||
|
||||
|
||||
///
|
||||
/// \brief Find the first element in `TypesT...` that satisfies `SearchT<T>`
|
||||
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
|
||||
/// \tparam TypesT The type sequence to search
|
||||
template<template<typename> typename SearchT, typename...TypesT> struct search_element : detail::_search_element<SearchT, TypesT...> {};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for `search_element_t<T, Ts...>::type`
|
||||
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
|
||||
/// \tparam TypesT The type sequence to search
|
||||
template<template<typename> typename SearchT, typename...TypesT> using search_element_t = search_element<SearchT, TypesT...>::type;
|
||||
|
||||
template<template<typename, typename...> typename, typename, typename...> struct search_element_args;
|
||||
|
||||
///
|
||||
/// \brief Find the first element `T` in `TypesT...` that satisfies `SearchT<T, ArgsT...>`
|
||||
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
|
||||
/// \tparam TypesT The type sequence to search
|
||||
/// \tparam ArgsT The arguments for the search
|
||||
template<template<typename, typename...> typename SearchT, typename...TypesT, typename...ArgsT>
|
||||
struct search_element_args<SearchT, type_sequence<ArgsT...>, TypesT...>
|
||||
: detail::_search_element_args<SearchT, detail::_type_sequence<ArgsT...>, TypesT...> {
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
// fennec::contains_element ============================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if the type sequence `Ts...` contains \f$T\f$
|
||||
/// \tparam T The type to find
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename T, typename...Ts> struct contains_element : bool_constant<(is_same_v<T, Ts> or ...)> {};
|
||||
|
||||
///
|
||||
/// \brief Shorthand for `contains_element_v<T, Ts...>::value`
|
||||
/// \tparam T The type to find
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename T, typename...Ts> constexpr bool contains_element_v = contains_element<T, Ts...>::value;
|
||||
|
||||
|
||||
|
||||
// fennec::is_unique ===================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Checks if all types in a type sequence are unique
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename...Ts> struct is_unique : false_type {};
|
||||
|
||||
#ifndef FENNEC_DOXYGEN
|
||||
// Single type case
|
||||
template<typename T> struct is_unique<T> : true_type {};
|
||||
|
||||
// Recursion case
|
||||
template<typename T, typename...Ts> requires(not is_same_v<T, Ts> && ...)
|
||||
struct is_unique<T, Ts...> : is_unique<Ts...> {};
|
||||
#endif
|
||||
|
||||
///
|
||||
/// \brief Shorthand for `is_unique<Ts...>::value`
|
||||
/// \tparam Ts The type sequence to check
|
||||
template<typename...Ts> constexpr bool is_unique_v = is_unique<Ts...>::value;
|
||||
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user