Compare commits
70 Commits
38b7221fa0
...
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 | |||
| 3ddc2b3d97 | |||
| e91c2aa9f1 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@
|
|||||||
/docs/
|
/docs/
|
||||||
/bin/
|
/bin/
|
||||||
/lib/
|
/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
|
# 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
|
# 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
|
# 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/>.
|
# 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)
|
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
|
# External dependencies should be loaded here
|
||||||
|
|
||||||
# CppTrace is a dependency of the project, added as a git submodule
|
|
||||||
add_subdirectory(external/cpptrace)
|
add_subdirectory(external/cpptrace)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 23)
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
set(CMAKE_C_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 scripts
|
||||||
include("${FENNEC_SOURCE_DIR}/cmake/version.cmake")
|
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.
|
# Metaprogramming is a dependency for generating various type info before compilation of the engine.
|
||||||
add_subdirectory(metaprogramming)
|
add_subdirectory(metaprogramming)
|
||||||
|
|
||||||
|
# Specify where to send libraries and executables
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
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_LIBRARY_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/lib/${FENNEC_BUILD_NAME})
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${FENNEC_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME})
|
||||||
|
|
||||||
# add the test suite as a sub-project
|
# Core Files
|
||||||
add_subdirectory(test)
|
fennec_add_sources(
|
||||||
|
|
||||||
add_library(fennec STATIC
|
|
||||||
|
|
||||||
# CORE =================================================================================================================
|
# CORE =================================================================================================================
|
||||||
include/fennec/core/engine.h source/core/engine.cpp
|
include/fennec/core/engine.h source/core/engine.cpp
|
||||||
include/fennec/core/event.h source/core/event.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
|
include/fennec/core/system.h
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# SCENE ================================================================================================================
|
# SCENE ================================================================================================================
|
||||||
include/fennec/scene/scene.h
|
include/fennec/scene/scene.h source/scene/scene.cpp
|
||||||
include/fennec/scene/component.h
|
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 ===========================================================================================================
|
# CONTAINERS ===========================================================================================================
|
||||||
|
include/fennec/containers/containers.h
|
||||||
|
|
||||||
include/fennec/containers/array.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/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/list.h
|
||||||
include/fennec/containers/map.h
|
include/fennec/containers/map.h
|
||||||
|
include/fennec/containers/object_pool.h
|
||||||
include/fennec/containers/optional.h
|
include/fennec/containers/optional.h
|
||||||
include/fennec/containers/pair.h
|
include/fennec/containers/pair.h
|
||||||
|
include/fennec/containers/priority_queue.h
|
||||||
include/fennec/containers/rdtree.h
|
include/fennec/containers/rdtree.h
|
||||||
|
include/fennec/containers/sequence.h
|
||||||
include/fennec/containers/set.h
|
include/fennec/containers/set.h
|
||||||
|
include/fennec/containers/traversal.h
|
||||||
include/fennec/containers/tuple.h
|
include/fennec/containers/tuple.h
|
||||||
|
include/fennec/containers/variant.h
|
||||||
|
|
||||||
|
|
||||||
include/fennec/containers/detail/_tuple.h
|
include/fennec/containers/detail/_tuple.h
|
||||||
|
|
||||||
|
|
||||||
# LANG =================================================================================================================
|
|
||||||
|
# lang =================================================================================================================
|
||||||
include/fennec/lang/lang.h
|
include/fennec/lang/lang.h
|
||||||
include/fennec/lang/metaprogramming.h
|
include/fennec/lang/metaprogramming.h
|
||||||
|
|
||||||
|
|
||||||
include/fennec/lang/bits.h
|
include/fennec/lang/bits.h
|
||||||
include/fennec/lang/constants.h
|
include/fennec/lang/constants.h
|
||||||
include/fennec/lang/conditional_types.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/hashing.h
|
||||||
include/fennec/lang/intrinsics.h
|
include/fennec/lang/intrinsics.h
|
||||||
include/fennec/lang/limits.h
|
include/fennec/lang/limits.h
|
||||||
include/fennec/lang/numeric_transforms.h
|
include/fennec/lang/numeric_transforms.h
|
||||||
include/fennec/lang/const_sequences.h
|
include/fennec/lang/metasequences.h
|
||||||
include/fennec/lang/startup.h
|
include/fennec/lang/ranges.h
|
||||||
|
include/fennec/lang/static_constructor.h
|
||||||
include/fennec/lang/type_identity.h
|
include/fennec/lang/type_identity.h
|
||||||
include/fennec/lang/type_operators.h
|
include/fennec/lang/type_operators.h
|
||||||
include/fennec/lang/type_sequences.h
|
include/fennec/lang/type_sequences.h
|
||||||
include/fennec/lang/type_traits.h
|
include/fennec/lang/type_traits.h
|
||||||
include/fennec/lang/type_transforms.h
|
include/fennec/lang/type_transforms.h
|
||||||
include/fennec/lang/typeuuid.h
|
|
||||||
include/fennec/lang/types.h
|
include/fennec/lang/types.h
|
||||||
include/fennec/lang/utility.h
|
include/fennec/lang/utility.h
|
||||||
include/fennec/lang/integer.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/_bits.h
|
||||||
|
include/fennec/lang/detail/_declval.h
|
||||||
|
include/fennec/lang/detail/_function.h
|
||||||
include/fennec/lang/detail/_int.h
|
include/fennec/lang/detail/_int.h
|
||||||
include/fennec/lang/detail/_numeric_transforms.h
|
include/fennec/lang/detail/_numeric_transforms.h
|
||||||
include/fennec/lang/detail/_stdlib.h
|
include/fennec/lang/detail/_stdlib.h
|
||||||
include/fennec/lang/detail/_type_traits.h
|
include/fennec/lang/detail/_type_traits.h
|
||||||
include/fennec/lang/detail/_type_transforms.h
|
include/fennec/lang/detail/_type_transforms.h
|
||||||
include/fennec/lang/detail/_type_sequences.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 ===============================================================================================================
|
# MEMORY ===============================================================================================================
|
||||||
@@ -130,15 +208,13 @@ add_library(fennec STATIC
|
|||||||
|
|
||||||
include/fennec/memory/detail/_ptr_traits.h
|
include/fennec/memory/detail/_ptr_traits.h
|
||||||
|
|
||||||
# CONCURRENCY ==========================================================================================================
|
|
||||||
include/fennec/concurrency/thread.h
|
|
||||||
include/fennec/concurrency/mutex.h
|
|
||||||
include/fennec/concurrency/atomic.h
|
|
||||||
|
|
||||||
# DEBUG ================================================================================================================
|
# DEBUG ================================================================================================================
|
||||||
source/debug/assert_impl.cpp
|
source/debug/assert_impl.cpp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# MATH =================================================================================================================
|
# MATH =================================================================================================================
|
||||||
include/fennec/math/math.h
|
include/fennec/math/math.h
|
||||||
|
|
||||||
@@ -164,64 +240,107 @@ add_library(fennec STATIC
|
|||||||
include/fennec/math/ext/constants.h
|
include/fennec/math/ext/constants.h
|
||||||
include/fennec/math/ext/primes.h
|
include/fennec/math/ext/primes.h
|
||||||
include/fennec/math/ext/quaternion.h
|
include/fennec/math/ext/quaternion.h
|
||||||
|
include/fennec/math/ext/rect.h
|
||||||
include/fennec/math/ext/transform.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/_math.h
|
||||||
include/fennec/math/detail/_matrix.h
|
include/fennec/math/detail/_matrix.h
|
||||||
include/fennec/math/detail/_types.h
|
include/fennec/math/detail/_types.h
|
||||||
include/fennec/math/detail/_vector_traits.h
|
include/fennec/math/detail/_vector_traits.h
|
||||||
|
|
||||||
|
|
||||||
# langproc ================================================================================================================
|
|
||||||
|
|
||||||
# Strings
|
# threading ============================================================================================================
|
||||||
include/fennec/langproc/strings/cstring.h
|
|
||||||
include/fennec/langproc/strings/locale.h
|
|
||||||
include/fennec/langproc/strings/string.h
|
|
||||||
|
|
||||||
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 =============================================================================================================
|
# PLATFORM =============================================================================================================
|
||||||
|
include/fennec/platform/interface/forward.h
|
||||||
include/fennec/platform/interface/fwd.h
|
include/fennec/platform/interface/display_server.h
|
||||||
include/fennec/platform/interface/platform.h source/platform/interface/platform.cpp
|
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/window.h source/platform/interface/window.cpp
|
||||||
include/fennec/platform/interface/gfxcontext.h
|
include/fennec/platform/window_manager.h source/platform/window_manager.cpp
|
||||||
|
|
||||||
|
|
||||||
# EXTRA SOURCES ========================================================================================================
|
|
||||||
|
|
||||||
${FENNEC_EXTRA_SOURCES}
|
# GRAPHICS =============================================================================================================
|
||||||
include/fennec/containers/traversal.h
|
|
||||||
include/fennec/containers/graph.h
|
|
||||||
include/fennec/containers/deque.h
|
|
||||||
include/fennec/containers/object_pool.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_dependencies(fennec metaprogramming)
|
# add the test suite as a sub-project
|
||||||
|
add_subdirectory(test)
|
||||||
|
|
||||||
target_compile_definitions(fennec PUBLIC
|
add_library(fennec STATIC
|
||||||
${FENNEC_COMPILE_DEFINITIONS}
|
${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.
|
# 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
|
# 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
|
target_link_libraries(fennec PRIVATE
|
||||||
cpptrace::cpptrace
|
|
||||||
${FENNEC_LINK_LIBRARIES}
|
${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 ==============================================================================================================
|
# DOXYGEN ==============================================================================================================
|
||||||
@@ -232,9 +351,10 @@ file(COPY logo DESTINATION docs/logo)
|
|||||||
if(DOXYGEN_FOUND)
|
if(DOXYGEN_FOUND)
|
||||||
add_dependencies(fennec fennecdocs)
|
add_dependencies(fennec fennecdocs)
|
||||||
set(DOXY_OUTPUT_DIR "${FENNEC_SOURCE_DIR}/docs")
|
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
|
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_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
|
configure_file(${DOXYGEN_CONFIG_IN} ${DOXYGEN_CONFIG_OUT} @ONLY) # Execute preprocessing step
|
||||||
message("Doxygen Build Started.")
|
message("Doxygen Build Started.")
|
||||||
@@ -243,7 +363,7 @@ if(DOXYGEN_FOUND)
|
|||||||
# Target for building docs
|
# Target for building docs
|
||||||
add_custom_target(fennecdocs ALL
|
add_custom_target(fennecdocs ALL
|
||||||
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_CONFIG_OUT}
|
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
|
COMMAND ${CMAKE_COMMAND} -E copy ${FENNEC_SOURCE_DIR}/logo/raster.png
|
||||||
${DOXY_OUTPUT_DIR}/logo/raster.png
|
${DOXY_OUTPUT_DIR}/logo/raster.png
|
||||||
COMMENT "Generating Doxygen Documentation"
|
COMMENT "Generating Doxygen Documentation"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# Readings
|
# Readings
|
||||||
|
|
||||||
Here is a list of relevant books and articles on various concepts related to
|
Here is a list of relevant books and articles on various concepts related to
|
||||||
developing a game engine and its subsystems.
|
developing a game engine and its subsystems.
|
||||||
|
|
||||||
- Game Engine Architecture, Ed. 3 – Jason Gregory
|
- Game Engine Architecture, Ed. 3 – Jason Gregory
|
||||||
|
|||||||
248
README.md
248
README.md
@@ -8,17 +8,24 @@
|
|||||||
|
|
||||||
<br><br>
|
<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>
|
||||||
<br>
|
<br>
|
||||||
@@ -27,8 +34,8 @@
|
|||||||
<a id="introduction"></a>
|
<a id="introduction"></a>
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
  fennec is designed to be a general purpose, educational game engine. fennec
|
  fennec is designed to be a general purpose, educational game engine. fennec
|
||||||
may be used through the provided editor application, or as a standalone library to
|
may be used through the provided editor application, or as a standalone library to
|
||||||
link against your application.
|
link against your application.
|
||||||
|
|
||||||
|
|
||||||
@@ -38,53 +45,73 @@ link against your application.
|
|||||||
<a id="coding-standards"></a>
|
<a id="coding-standards"></a>
|
||||||
### Coding Standards
|
### Coding Standards
|
||||||
|
|
||||||
Interfacing with the API in C++ follows the [GNU Coding Standards](https://www.gnu.org/prep/standards/html_node/index.html).
|
Interfacing with the API in C++ follows the [GNU Coding Standards](https://www.gnu.org/prep/standards/html_node/index.html).
|
||||||
Some main areas where the engine strays from the GNU standard includes the following:
|
Some main areas where the engine strays from the GNU standard includes the following:
|
||||||
|
|
||||||
* [Section 4.7, Standards for Graphical Interfaces](https://www.gnu.org/prep/standards/html_node/Graphical-Interfaces.html).
|
* [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.
|
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.
|
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
|
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.
|
Makefile. CMake, although overwhelming at first, is much more friendly to those who are learning build systems for the first time.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
fennec Standards:
|
fennec Standards:
|
||||||
|
|
||||||
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by
|
* As per the GNU standard, macros should be `SCREAMING_SNAKE_CASE`. Additionally, Macros should be preceded by
|
||||||
`<APP_NAME>_`. Macros that wrap C-Style functions may use normal `snake_case`.
|
`<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.
|
- 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`.
|
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`.
|
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.
|
* 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
|
In the case of global functions, helpers should be placed in a similarly named file in a subdirectory and namespace
|
||||||
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
|
called `detail`. Helper functions should be documented with C-Style comments, however it is not necessary to provide
|
||||||
Doxygen documentation.
|
Doxygen documentation.
|
||||||
|
|
||||||
- **DO NOT USE C++ EXCEPTIONS** they will not be supported because they are shit.<sup>[[1]](#f1)</sup>
|
- **DO NOT USE C++ EXCEPTIONS** they will not be supported because they are shit.<sup>[[1]](#f1)</sup>
|
||||||
|
|
||||||
* Most behaviours should be type independent. Specifically interactions with the core systems of the engine.
|
* 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>
|
<br><br>
|
||||||
|
|
||||||
<a id="f1"></a>
|
<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
|
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.
|
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
|
The assertion paradigm is better at handling this because you are defining erroneous behaviour in the
|
||||||
code and how it is handled. In a debug build we can immediately halt the program, we don't care about
|
code and how it is handled. In a debug build we can immediately halt the program, we don't care about
|
||||||
the state afterward, only beforehand. Now for a release build, this is first and foremost a game engine,
|
the state afterward, only beforehand. Now for a release build, this is first and foremost a game engine,
|
||||||
so we want to crash as gracefully as possible, prevent data loss, and get some debug information for it.
|
so we want to crash as gracefully as possible, prevent data loss, and get some debug information for it.
|
||||||
fennec defines its own `assert` macro to be used, defining a hook in the private version of the
|
fennec defines its own `assert` macro to be used, defining a hook in the private version of the
|
||||||
function. This hook is used to clean up any state information within the engine and may be used to send
|
function. This hook is used to clean up any state information within the engine and may be used to send
|
||||||
immediate events to listeners so that outside functionality may decide how to handle the impending crash.
|
immediate events to listeners so that outside functionality may decide how to handle the impending crash.
|
||||||
In Debug Mode there is nothing that can be done to stop the crash, as soon as the branch finishes,
|
In Debug Mode there is nothing that can be done to stop the crash, as soon as the branch finishes,
|
||||||
`abort()` will be called.
|
`abort()` will be called.
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
@@ -104,11 +131,11 @@ There are a few reasons for this:
|
|||||||
<a id="building-from-source"></a>
|
<a id="building-from-source"></a>
|
||||||
## Building from Source
|
## Building from Source
|
||||||
|
|
||||||
  fennec uses the CMake build manager. The CMake build script provides several
|
  fennec uses the CMake build manager. The CMake build script provides several
|
||||||
targets for building parts of the engine.
|
targets for building parts of the engine.
|
||||||
|
|
||||||
  Using an IDE will streamline the build process for you and add additional configuration
|
  Using an IDE will streamline the build process for you and add additional configuration
|
||||||
options. Eclipse, Visual Studio, and CLion provide built-in support for CMake. VSCode
|
options. Eclipse, Visual Studio, and CLion provide built-in support for CMake. VSCode
|
||||||
is also a viable IDE but involves some extra setup.
|
is also a viable IDE but involves some extra setup.
|
||||||
|
|
||||||
| Target | Description |
|
| Target | Description |
|
||||||
@@ -119,16 +146,24 @@ is also a viable IDE but involves some extra setup.
|
|||||||
| fennecdocs-clean | Cleans the generated html documentation files. |
|
| fennecdocs-clean | Cleans the generated html documentation files. |
|
||||||
| fennec-test | Test suite for verifying engine functionality. |
|
| fennec-test | Test suite for verifying engine functionality. |
|
||||||
|
|
||||||
<a id="dependencies"></a>
|
<br>
|
||||||
|
|
||||||
| Dependency | Notes |
|
<a id="dependencies"></a>
|
||||||
|-------------------|----------------------------------------------------------------------------------------------------------|
|
### Dependencies
|
||||||
| 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 |
|
| Dependency | Notes |
|
||||||
| 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. |
|
| C/C++ Compiler | GCC/G++ is the compiler that fennec is designed around, however, Clang, MSVC, and MinGW may also be used |
|
||||||
| Doxygen | Doxygen is required for building the documentation for fennec. This is an optional dependency |
|
| CMake | The build manager used by the engine |
|
||||||
| Graphviz | Graphviz is a required dependency for Doxygen |
|
| 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<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>
|
<br>
|
||||||
|
|
||||||
@@ -138,24 +173,70 @@ is also a viable IDE but involves some extra setup.
|
|||||||
`build.sh` provides profiles for building the main engine. Run `./build.sh --help`
|
`build.sh` provides profiles for building the main engine. Run `./build.sh --help`
|
||||||
for more info.
|
for more info.
|
||||||
|
|
||||||
  By default, the CMake generator used is Ninja, which requires Ninja to be installed. You can modify the
|
  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).
|
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.
|
||||||
|
|
||||||
  I will at no point provide official cross-compilation toolchains for fennec. However, I will provide tools for
|
<a id="git"></a>
|
||||||
using specific toolchains for specific platforms that necessitate this. The primary examples would be Android and iOS.
|
#### Git
|
||||||
If you wish to build for Windows *and* Linux, your options are WSL or Dual Boot. I recommend Dual Boot over WSL.
|
|
||||||
|
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>
|
<br>
|
||||||
|
|
||||||
<a id="building-on-windows"></a>
|
<a id="building-on-windows"></a>
|
||||||
### Building on Windows
|
### Building on Windows
|
||||||
|
|
||||||
  The bash script can be run natively on Windows when WSL is enabled. You do not need to run the
|
  The bash script can be run natively on Windows when WSL is enabled. You do not need to run the
|
||||||
script in WSL, simply use the "bash" command in Command Prompt or PowerShell. The script will require
|
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.
|
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.
|
Otherwise, follow the sequence of commands provided in the bash script.
|
||||||
|
|
||||||
@@ -178,8 +259,8 @@ The value of `<profile>` may be one of the following:
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
  If you would like to use Visual Studio without CMake, you can use the build script to generate
|
  If you would like to use Visual Studio without CMake, you can use the build script to generate
|
||||||
a Visual Studio project for the source. For a list of available Visual Studio generators, [see this section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
a Visual Studio project for the source. For a list of available Visual Studio generators, [see this section](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
|
||||||
of the CMake docs. Running the following command will generate the Visual Studio project.
|
of the CMake docs. Running the following command will generate the Visual Studio project.
|
||||||
|
|
||||||
```commandline
|
```commandline
|
||||||
@@ -194,9 +275,9 @@ cmake -G "Visual Studio 17 2022" -A x64
|
|||||||
|
|
||||||
`test.sh` provides profiles for building the test suite and executes them.
|
`test.sh` provides profiles for building the test suite and executes them.
|
||||||
|
|
||||||
  By default, the program runs in debug mode and the first failed test will throw an assertion.
|
  By default, the program runs in debug mode and the first failed test will throw an assertion.
|
||||||
Any tests that involve running as an application will spawn a subprocess with a window, and give
|
Any tests that involve running as an application will spawn a subprocess with a window, and give
|
||||||
a short description of the behaviour in the terminal. It will then have you confirm whether the
|
a short description of the behaviour in the terminal. It will then have you confirm whether the
|
||||||
information displayed is correct.
|
information displayed is correct.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@@ -206,33 +287,36 @@ information displayed is correct.
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
||||||
|
<a id="licensing"></a>
|
||||||
### Licensing
|
### Licensing
|
||||||
|
|
||||||
The following content of this section is not legal advice, nor is it legally binding, and nor does it change the terms
|
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.
|
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
|
TLDR; You may license your game under whichever license you please. Any C++ code that is by definition a derivative work
|
||||||
fennec and using it in a commercial manner. This of course does not bar them from using fennec commercially, however
|
must be licensed under GPLv3 and freely available, everything else; assets, scripts, design documents, etc. may be under
|
||||||
it will prevent them from being able to make the derivative work proprietary. You are free to use and redistribute
|
the license of your choosing and remain proprietary.
|
||||||
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
|
  fennec is licensed under GPLv3. The primary reason for the choice of license is to dissuade corporations from
|
||||||
copyrighted under a non-compliant license. Think of it in terms of using Blender to create a mesh for a game, then
|
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.
|
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
|
||||||
  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.
|
intermediate step and will be erased when no longer needed.
|
||||||
|
|
||||||
  As long as you use the official editor, it will properly include licenses in project content when provided a license
|
  As long as you use the official editor, it will properly include licenses in project content when provided a license
|
||||||
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
|
and the name of the license holder. Archive packs will include the license holders non-GPLv3 license in them and any
|
||||||
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
|
linked code will be covered by GPLv3 under the name of the license holder. Be aware that the parts of your project
|
||||||
licensed under GPLv3 must be available upon request.
|
licensed under GPLv3 must be available upon request.
|
||||||
|
|
||||||
  A release project will consist of an executable, a shared library for your code, an archive pak, and streamable assets.
|
  A release project will consist of an executable, a shared library for your code, an archive pak, and streamable assets.
|
||||||
The executable and shared library are under the GPLv3 license, while the archive pak and streamable assets are under your license.
|
The executable and shared library are under the GPLv3 license, while the archive pak and streamable assets are under your license.
|
||||||
|
|
||||||
It is to my discretion whether I enforce the terms of the license on a party.
|
It is to my discretion whether I enforce the terms of the license on a party.
|
||||||
@@ -248,7 +332,7 @@ The following practices are more likely to get my attention and enforcement:
|
|||||||
- Subscription-Based Sales Model
|
- Subscription-Based Sales Model
|
||||||
- NFTs or Nonfungible Tokens
|
- NFTs or Nonfungible Tokens
|
||||||
|
|
||||||
I encourage those who wish to commercialize derivative works crowdfund rather than use a sales model. I also ask
|
I encourage those who wish to commercialize derivative works crowdfund rather than use a sales model. I also ask
|
||||||
that you kindly support me as a developer, I will set up a Buy Me a Coffee link at some point.
|
that you kindly support me as a developer, I will set up a Buy Me a Coffee link at some point.
|
||||||
|
|
||||||
GPLv3 is bound by fair use; here is the clause, 17 U.S. Code § 107:
|
GPLv3 is bound by fair use; here is the clause, 17 U.S. Code § 107:
|
||||||
@@ -272,23 +356,23 @@ The fact that a work is unpublished shall not itself bar a finding of fair use i
|
|||||||
consideration of all the above factors.
|
consideration of all the above factors.
|
||||||
```
|
```
|
||||||
|
|
||||||
If you have any questions or concerns, please seek legal council. If you believe someone else has violated the terms
|
If you have any questions or concerns, please seek legal council. If you believe someone else has violated the terms
|
||||||
of this license, please contact me at [mslockbo@gmail.com](mailto:mslockbo@gmail.com).
|
of this license, please contact me at [mslockbo@gmail.com](mailto:mslockbo@gmail.com).
|
||||||
|
|
||||||
I am aware of Universities with Game Development programs such as DigiPen Institute of Technology and Full-Sail
|
I am aware of Universities with Game Development programs such as DigiPen Institute of Technology and Full-Sail
|
||||||
University which license student work to protect them and the faculty.
|
University which license student work to protect them and the faculty.
|
||||||
|
|
||||||
Champlain College does not license student projects and constitutes fair use.
|
Champlain College does not license student projects and constitutes fair use.
|
||||||
|
|
||||||
I have not worked with Full-Sail University before, so I am not familiar with any of their staff members, and I will
|
I have not worked with Full-Sail University before, so I am not familiar with any of their staff members, and I will
|
||||||
require legal council to consult with them which may dissuade permission to use my engine.
|
require legal council to consult with them which may dissuade permission to use my engine.
|
||||||
|
|
||||||
I hold a Bachelor's Degree in Computer Science and Real-Time Interactive Simulation from DigiPen Institute of Technology,
|
I hold a Bachelor's Degree in Computer Science and Real-Time Interactive Simulation from DigiPen Institute of Technology,
|
||||||
so I am familiar with their copyright policy. Ask your professor about usage of my engine, and they or someone with
|
so I am familiar with their copyright policy. Ask your professor about usage of my engine, and they or someone with
|
||||||
appropriate standing will reach out to me. Eventually, with interest, I may reach out on my own terms to negotiate usage
|
appropriate standing will reach out to me. Eventually, with interest, I may reach out on my own terms to negotiate usage
|
||||||
of fennec for educative purposes at DigiPen while retaining their license on student work.
|
of fennec for educative purposes at DigiPen while retaining their license on student work.
|
||||||
|
|
||||||
If your University is not listed here, reach out to your professor for permission. Ask them to reach out to me, I am
|
If your University is not listed here, reach out to your professor for permission. Ask them to reach out to me, I am
|
||||||
willing to work with educational institutes to protect both fennec and student work in accordance to university policy.
|
willing to work with educational institutes to protect both fennec and student work in accordance to university policy.
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|||||||
10
build.sh
10
build.sh
@@ -1,6 +1,6 @@
|
|||||||
## =====================================================================================================================
|
## =====================================================================================================================
|
||||||
## fennec, a free and open source game engine
|
## 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
|
## 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
|
## it under the terms of the GNU General Public License as published by
|
||||||
@@ -38,7 +38,7 @@ Help()
|
|||||||
Debug()
|
Debug()
|
||||||
{
|
{
|
||||||
mkdir -p build/debug
|
mkdir -p build/debug
|
||||||
cd ./build/debug
|
cd ./build/debug || exit
|
||||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B .
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -S ../.. -B .
|
||||||
cmake --build . --target fennec
|
cmake --build . --target fennec
|
||||||
cd ../..
|
cd ../..
|
||||||
@@ -47,7 +47,7 @@ Debug()
|
|||||||
Release()
|
Release()
|
||||||
{
|
{
|
||||||
mkdir -p build/release
|
mkdir -p build/release
|
||||||
cd ./build/release
|
cd ./build/release || exit
|
||||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B .
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -S ../.. -B .
|
||||||
cmake --build . --target fennec
|
cmake --build . --target fennec
|
||||||
cd ../..
|
cd ../..
|
||||||
@@ -56,7 +56,7 @@ Release()
|
|||||||
RelWithDebInfo()
|
RelWithDebInfo()
|
||||||
{
|
{
|
||||||
mkdir -p build/relwithdebinfo
|
mkdir -p build/relwithdebinfo
|
||||||
cd ./build/relwithdebinfo
|
cd ./build/relwithdebinfo || exit
|
||||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B .
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -S ../.. -B .
|
||||||
cmake --build . --target fennec
|
cmake --build . --target fennec
|
||||||
cd ../..
|
cd ../..
|
||||||
@@ -65,7 +65,7 @@ RelWithDebInfo()
|
|||||||
MinSizeRel()
|
MinSizeRel()
|
||||||
{
|
{
|
||||||
mkdir -p build/minsizerel
|
mkdir -p build/minsizerel
|
||||||
cd ./build/minsizerel
|
cd ./build/minsizerel || exit
|
||||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B .
|
cmake -G Ninja -DCMAKE_BUILD_TYPE=MinSizeRel -S ../.. -B .
|
||||||
cmake --build . --target fennec
|
cmake --build . --target fennec
|
||||||
cd ../..
|
cd ../..
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,13 @@
|
|||||||
|
|
||||||
# this script finds the compiler being used
|
# 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")
|
if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||||
set(FENNEC_COMPILER "GCC")
|
set(FENNEC_COMPILER "GCC")
|
||||||
include("${FENNEC_SOURCE_DIR}/cmake/gcc.cmake")
|
include("${FENNEC_SOURCE_DIR}/cmake/gcc.cmake")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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")
|
include("${FENNEC_SOURCE_DIR}/cmake/unix.cmake")
|
||||||
|
|
||||||
# compile definitions
|
# compile definitions
|
||||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
fennec_add_definitions(
|
||||||
FENNEC_PLATFORM_NAME="Linux"
|
FENNEC_PLATFORM_NAME="Linux"
|
||||||
FENNEC_PLATFORM_LINUX=1
|
FENNEC_PLATFORM_LINUX=1
|
||||||
)
|
)
|
||||||
|
|
||||||
# extra source files
|
# extra source files
|
||||||
list(APPEND FENNEC_EXTRA_SOURCES
|
fennec_add_sources(
|
||||||
include/fennec/platform/linux/platform.h source/platform/linux/platform.cpp
|
include/fennec/platform/linux/platform.h source/platform/linux/platform.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
|
|
||||||
|
find_package(OpenGL)
|
||||||
|
|
||||||
if(FENNEC_GRAPHICS_WANT_EGL)
|
if(FENNEC_GRAPHICS_WANT_EGL)
|
||||||
find_package(OpenGL REQUIRED COMPONENTS EGL)
|
fennec_add_sources(
|
||||||
find_package(GLEW REQUIRED)
|
include/fennec/platform/opengl/egl/fwd.h
|
||||||
message(STATUS "EGL Requested")
|
include/fennec/platform/opengl/egl/error.h
|
||||||
else()
|
include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp
|
||||||
find_package(OpenGL)
|
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()
|
endif()
|
||||||
|
|
||||||
if(TARGET OpenGL::GL AND TARGET GLEW::GLEW)
|
if(TARGET OpenGL::GL)
|
||||||
message(STATUS "Found OpenGL: ${OPENGL_gl_LIBRARY}")
|
fennec_add_link_libraries(OpenGL::GL)
|
||||||
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::GL GLEW::GLEW)
|
fennec_add_definitions(FENNEC_GRAPHICS_OPENGL=1)
|
||||||
list(APPEND FENNEC_COMPILE_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()
|
else()
|
||||||
message(FATAL_ERROR "No Suitable OpenGL implementation found.")
|
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()
|
endif()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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")
|
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
|
# Check for Linux
|
||||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
if(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
set(FENNEC_PLATFORM "Linux")
|
set(FENNEC_PLATFORM "Linux")
|
||||||
include("${FENNEC_SOURCE_DIR}/cmake/linux.cmake")
|
include("${FENNEC_SOURCE_DIR}/cmake/linux.cmake")
|
||||||
endif ()
|
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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,11 +19,11 @@
|
|||||||
# generic unix functionality
|
# generic unix functionality
|
||||||
|
|
||||||
# compile definitions
|
# compile definitions
|
||||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
fennec_add_definitions(
|
||||||
FENNEC_PLATFORM_UNIX=1
|
FENNEC_PLATFORM_UNIX=1
|
||||||
)
|
)
|
||||||
|
|
||||||
# extra source files
|
# extra source files
|
||||||
list(APPEND FENNEC_EXTRA_SOURCES
|
fennec_add_sources(
|
||||||
include/fennec/platform/unix/platform.h source/platform/unix/platform.cpp
|
include/fennec/platform/unix/platform.h source/platform/unix/platform.cpp
|
||||||
)
|
)
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,6 +19,34 @@
|
|||||||
# https://gist.github.com/mariobadr/acc3c8adf4b4e722705be38c3deac59a
|
# https://gist.github.com/mariobadr/acc3c8adf4b4e722705be38c3deac59a
|
||||||
# this script finds libwayland and dependencies
|
# 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)
|
macro(fennec_check_wayland)
|
||||||
set(WAYLAND_CLIENT_FOUND 0)
|
set(WAYLAND_CLIENT_FOUND 0)
|
||||||
|
|
||||||
@@ -30,6 +58,7 @@ macro(fennec_check_wayland)
|
|||||||
WAYLAND_CLIENT_LIBRARY
|
WAYLAND_CLIENT_LIBRARY
|
||||||
NAMES wayland-client libwayland-client
|
NAMES wayland-client libwayland-client
|
||||||
)
|
)
|
||||||
|
find_program(WAYLAND_SCANNER NAMES wayland-scanner)
|
||||||
|
|
||||||
# EGL is required
|
# EGL is required
|
||||||
find_path(
|
find_path(
|
||||||
@@ -41,13 +70,31 @@ macro(fennec_check_wayland)
|
|||||||
NAMES wayland-egl libwayland-egl
|
NAMES wayland-egl libwayland-egl
|
||||||
)
|
)
|
||||||
|
|
||||||
if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY)
|
|
||||||
AND (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_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}")
|
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")
|
include("${FENNEC_SOURCE_DIR}/cmake/xkb.cmake")
|
||||||
fennec_check_xkb()
|
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(
|
get_filename_component(
|
||||||
WAYLAND_CLIENT_LIBRARY
|
WAYLAND_CLIENT_LIBRARY
|
||||||
${WAYLAND_CLIENT_LIBRARY}
|
${WAYLAND_CLIENT_LIBRARY}
|
||||||
@@ -63,22 +110,60 @@ macro(fennec_check_wayland)
|
|||||||
set(WAYLAND_EGL_FOUND 1)
|
set(WAYLAND_EGL_FOUND 1)
|
||||||
set(FENNEC_GRAPHICS_WANT_EGL 1)
|
set(FENNEC_GRAPHICS_WANT_EGL 1)
|
||||||
|
|
||||||
list(APPEND FENNEC_EXTRA_SOURCES
|
fennec_add_sources(
|
||||||
# Dynamic Library Files
|
# 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/sym.h
|
||||||
include/fennec/platform/linux/wayland/lib/wayland-client.h
|
include/fennec/platform/linux/wayland/lib/wayland.h
|
||||||
include/fennec/platform/linux/wayland/lib/wayland-util.h
|
|
||||||
include/fennec/platform/linux/wayland/lib/loader.h source/platform/linux/wayland/lib/loader.cpp
|
include/fennec/platform/linux/wayland/lib/loader.h source/platform/linux/wayland/lib/loader.cpp
|
||||||
|
|
||||||
# Fennec Files
|
# 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_HAS_WAYLAND=1
|
||||||
FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}"
|
FENNEC_LIB_WAYLAND="${WAYLAND_CLIENT_LIBRARY}"
|
||||||
FENNEC_LIB_WAYLAND_EGL="${WAYLAND_EGL_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()
|
endif()
|
||||||
endmacro()
|
endmacro()
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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)
|
set(XKB_FOUND 1)
|
||||||
|
|
||||||
list(APPEND FENNEC_EXTRA_SOURCES
|
fennec_add_sources(
|
||||||
# Dynamic Library Files
|
# 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/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
|
include/fennec/platform/linux/xkb/lib/loader.h source/platform/linux/xkb/lib/loader.cpp
|
||||||
|
|
||||||
# Fennec files
|
# Fennec files
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FENNEC_COMPILE_DEFINITIONS
|
fennec_add_definitions(
|
||||||
FENNEC_HAS_XKB=1
|
FENNEC_HAS_XKB=1
|
||||||
FENNEC_LIB_XKB="${XKB_LIBRARY}"
|
FENNEC_LIB_XKB="${XKB_LIBRARY}"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -49,6 +49,7 @@
|
|||||||
<includes visible="$SHOW_HEADERFILE"/>
|
<includes visible="$SHOW_HEADERFILE"/>
|
||||||
<inheritancegraph visible="$CLASS_GRAPH"/>
|
<inheritancegraph visible="$CLASS_GRAPH"/>
|
||||||
<collaborationgraph visible="yes"/>
|
<collaborationgraph visible="yes"/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<nestedclasses visible="yes" title=""/>
|
<nestedclasses visible="yes" title=""/>
|
||||||
<publictypes title=""/>
|
<publictypes title=""/>
|
||||||
@@ -83,7 +84,6 @@
|
|||||||
<related title="" subtitle=""/>
|
<related title="" subtitle=""/>
|
||||||
<membergroups visible="yes"/>
|
<membergroups visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
<memberdef>
|
<memberdef>
|
||||||
<inlineclasses title=""/>
|
<inlineclasses title=""/>
|
||||||
<typedefs title=""/>
|
<typedefs title=""/>
|
||||||
@@ -105,6 +105,7 @@
|
|||||||
<!-- Layout definition for a namespace page -->
|
<!-- Layout definition for a namespace page -->
|
||||||
<namespace>
|
<namespace>
|
||||||
<briefdescription visible="yes"/>
|
<briefdescription visible="yes"/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<nestednamespaces visible="yes" title=""/>
|
<nestednamespaces visible="yes" title=""/>
|
||||||
<constantgroups visible="yes" title=""/>
|
<constantgroups visible="yes" title=""/>
|
||||||
@@ -121,7 +122,6 @@
|
|||||||
<variables title=""/>
|
<variables title=""/>
|
||||||
<membergroups visible="yes"/>
|
<membergroups visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
<memberdef>
|
<memberdef>
|
||||||
<inlineclasses title=""/>
|
<inlineclasses title=""/>
|
||||||
<typedefs title=""/>
|
<typedefs title=""/>
|
||||||
@@ -150,6 +150,7 @@
|
|||||||
<includegraph visible="yes"/>
|
<includegraph visible="yes"/>
|
||||||
<includedbygraph visible="yes"/>
|
<includedbygraph visible="yes"/>
|
||||||
<sourcelink visible="yes"/>
|
<sourcelink visible="yes"/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<interfaces visible="yes" title=""/>
|
<interfaces visible="yes" title=""/>
|
||||||
<classes visible="yes" title=""/>
|
<classes visible="yes" title=""/>
|
||||||
@@ -167,7 +168,6 @@
|
|||||||
<variables title=""/>
|
<variables title=""/>
|
||||||
<membergroups visible="yes"/>
|
<membergroups visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
<memberdef>
|
<memberdef>
|
||||||
<inlineclasses title=""/>
|
<inlineclasses title=""/>
|
||||||
<defines title=""/>
|
<defines title=""/>
|
||||||
@@ -185,6 +185,7 @@
|
|||||||
<group>
|
<group>
|
||||||
<briefdescription visible="yes"/>
|
<briefdescription visible="yes"/>
|
||||||
<groupgraph visible="yes"/>
|
<groupgraph visible="yes"/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<nestedgroups visible="yes" title=""/>
|
<nestedgroups visible="yes" title=""/>
|
||||||
<modules visible="yes" title=""/>
|
<modules visible="yes" title=""/>
|
||||||
@@ -210,7 +211,6 @@
|
|||||||
<friends title=""/>
|
<friends title=""/>
|
||||||
<membergroups visible="yes"/>
|
<membergroups visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
<memberdef>
|
<memberdef>
|
||||||
<pagedocs/>
|
<pagedocs/>
|
||||||
<inlineclasses title=""/>
|
<inlineclasses title=""/>
|
||||||
@@ -237,6 +237,7 @@
|
|||||||
<module>
|
<module>
|
||||||
<briefdescription visible="yes"/>
|
<briefdescription visible="yes"/>
|
||||||
<exportedmodules visible="yes"/>
|
<exportedmodules visible="yes"/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<concepts visible="yes" title=""/>
|
<concepts visible="yes" title=""/>
|
||||||
<classes visible="yes" title=""/>
|
<classes visible="yes" title=""/>
|
||||||
@@ -246,7 +247,6 @@
|
|||||||
<variables title=""/>
|
<variables title=""/>
|
||||||
<membergroups title=""/>
|
<membergroups title=""/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<files visible="yes"/>
|
<files visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
@@ -256,10 +256,10 @@
|
|||||||
<directory>
|
<directory>
|
||||||
<briefdescription visible="yes"/>
|
<briefdescription visible="yes"/>
|
||||||
<directorygraph visible="yes"/>
|
<directorygraph visible="yes"/>
|
||||||
|
<detaileddescription title=""/>
|
||||||
<memberdecl>
|
<memberdecl>
|
||||||
<dirs visible="yes"/>
|
<dirs visible="yes"/>
|
||||||
<files visible="yes"/>
|
<files visible="yes"/>
|
||||||
</memberdecl>
|
</memberdecl>
|
||||||
<detaileddescription title=""/>
|
|
||||||
</directory>
|
</directory>
|
||||||
</doxygenlayout>
|
</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.
|
# of the file/class/namespace that contains it.
|
||||||
# The default value is: NO.
|
# 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
|
# 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.
|
# 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.
|
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
|
||||||
# The default value is: NO.
|
# 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
|
# 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
|
# 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.
|
# if EXTRACT_ALL is enabled.
|
||||||
# The default value is: NO.
|
# 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
|
# 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
|
# declarations. If set to NO, these declarations will be included in the
|
||||||
@@ -701,7 +701,7 @@ SORT_BRIEF_DOCS = NO
|
|||||||
# detailed member documentation.
|
# detailed member documentation.
|
||||||
# The default value is: NO.
|
# 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
|
# 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
|
# 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
|
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||||
# Note: If this tag is empty the current directory is searched.
|
# Note: If this tag is empty the current directory is searched.
|
||||||
|
|
||||||
INPUT = "@PROJECT_SOURCE_DIR@/include" \
|
INPUT = "@PROJECT_SOURCE_DIR@/include/" \
|
||||||
"@PROJECT_SOURCE_DIR@/source" \
|
|
||||||
"@PROJECT_SOURCE_DIR@/README.md"
|
"@PROJECT_SOURCE_DIR@/README.md"
|
||||||
|
|
||||||
# This tag can be used to specify the character encoding of the source files
|
# 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
|
# Note that relative paths are relative to the directory from which doxygen is
|
||||||
# run.
|
# 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
|
# 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
|
# 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
|
# that contain images that are to be included in the documentation (see the
|
||||||
# \image command).
|
# \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
|
# 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
|
# 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.
|
# The default directory is: latex.
|
||||||
# This tag requires that the tag GENERATE_LATEX is set to YES.
|
# 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
|
# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
|
||||||
# invoked.
|
# invoked.
|
||||||
@@ -2402,7 +2404,7 @@ MACRO_EXPANSION = YES
|
|||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
# 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
|
# If the SEARCH_INCLUDES tag is set to YES, the include files in the
|
||||||
# INCLUDE_PATH will be searched if a #include is found.
|
# INCLUDE_PATH will be searched if a #include is found.
|
||||||
@@ -2417,7 +2419,7 @@ SEARCH_INCLUDES = YES
|
|||||||
# RECURSIVE has no effect here.
|
# RECURSIVE has no effect here.
|
||||||
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
|
# 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
|
# 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
|
# 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.
|
# recursively expanded use the := operator instead of the = operator.
|
||||||
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
|
||||||
|
|
||||||
PREDEFINED = "FENNEC_SCALAR_TEMPLATE=" \
|
PREDEFINED = "FENNEC_DOXYGEN="
|
||||||
"FENNEC_VECTOR_TEMPLATE="
|
|
||||||
|
|
||||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
|
# 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
|
# 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.
|
# and usage relations if the target is undocumented or is not a class.
|
||||||
# The default value is: YES.
|
# 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
|
# 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:
|
# available from the path. This tool is part of Graphviz (see:
|
||||||
@@ -2664,7 +2665,7 @@ TEMPLATE_RELATIONS = NO
|
|||||||
# The default value is: YES.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to 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
|
# 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
|
# 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.
|
# The default value is: YES.
|
||||||
# This tag requires that the tag HAVE_DOT is set to 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
|
# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
|
||||||
# dependency graph for every global function or class method.
|
# dependency graph for every global function or class method.
|
||||||
|
|||||||
@@ -286,4 +286,9 @@ html.dark-mode {
|
|||||||
|
|
||||||
td.odd_c {
|
td.odd_c {
|
||||||
background-color: var(--odd-color)
|
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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
# GDB CODE =============================================================================================================
|
# GDB CODE =============================================================================================================
|
||||||
|
|
||||||
import gdb
|
import gdb
|
||||||
|
import gdb.printing
|
||||||
|
|
||||||
from . import containers
|
from . import containers
|
||||||
from . import strings
|
from . import strings
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -138,6 +138,45 @@ class ListPrinter:
|
|||||||
return 'array'
|
return 'array'
|
||||||
|
|
||||||
|
|
||||||
|
# DEQUE ================================================================================================================
|
||||||
|
|
||||||
|
class DequePrinter:
|
||||||
|
"""Print a fennec::deque"""
|
||||||
|
|
||||||
|
class Iterator:
|
||||||
|
def __init__(self, start):
|
||||||
|
self.node = start
|
||||||
|
self.index = 0
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
if self.node is None:
|
||||||
|
raise StopIteration
|
||||||
|
|
||||||
|
i = self.index
|
||||||
|
value = self.node.dereference()['value']
|
||||||
|
|
||||||
|
self.node = self.node.dereference()['next']
|
||||||
|
self.index = self.index + 1
|
||||||
|
|
||||||
|
return '[{}]'.format(i), value
|
||||||
|
|
||||||
|
def __init__(self, val):
|
||||||
|
self.first = val['_first']
|
||||||
|
self.last = val['_last']
|
||||||
|
self.size = val['_size']
|
||||||
|
|
||||||
|
def to_string(self):
|
||||||
|
if self.first is None:
|
||||||
|
return "{ empty }"
|
||||||
|
return "{ length " + str(self.size) + " }"
|
||||||
|
|
||||||
|
def children(self):
|
||||||
|
return self.Iterator(self.first)
|
||||||
|
|
||||||
|
|
||||||
# SET ==================================================================================================================
|
# SET ==================================================================================================================
|
||||||
|
|
||||||
class SetPrinter:
|
class SetPrinter:
|
||||||
@@ -332,7 +371,7 @@ class RDTreePrinter:
|
|||||||
index += '└'
|
index += '└'
|
||||||
|
|
||||||
index += '─'
|
index += '─'
|
||||||
index += '[{}, {}]'.format(node, i)
|
index += '[{}]'.format(node)
|
||||||
return index, value
|
return index, value
|
||||||
|
|
||||||
|
|
||||||
@@ -350,6 +389,200 @@ class RDTreePrinter:
|
|||||||
return self.Iterator(self.tree, 0, self.capacity)
|
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 ================================================================================================================
|
# Graph ================================================================================================================
|
||||||
|
|
||||||
class GraphPrinter:
|
class GraphPrinter:
|
||||||
@@ -357,10 +590,10 @@ class GraphPrinter:
|
|||||||
|
|
||||||
class Iterator:
|
class Iterator:
|
||||||
def __init__(self, val):
|
def __init__(self, val):
|
||||||
self.node_pool = val['_node_pool']['_table']['_alloc']['_data']
|
self.node_pool = val['_vertex_pool']['_table']['_alloc']['_data']
|
||||||
self.max_nodes = val['_node_pool']['_table']['_alloc']['_capacity']
|
self.max_nodes = val['_vertex_pool']['_table']['_alloc']['_capacity']
|
||||||
self.conn_map = val['_conn_map']['_alloc']['_data']
|
self.conn_map = val['_edge_map']['_alloc']['_data']
|
||||||
self.max_conn = val['_conn_map']['_size']
|
self.max_conn = val['_edge_map']['_size']
|
||||||
self.index = 0
|
self.index = 0
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
@@ -408,7 +641,7 @@ class GraphPrinter:
|
|||||||
|
|
||||||
def __init__(self, val):
|
def __init__(self, val):
|
||||||
self.val = val
|
self.val = val
|
||||||
self.size = val['_node_pool']['_size']
|
self.size = val['_vertex_pool']['_size']
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
if self.size == 0:
|
if self.size == 0:
|
||||||
@@ -425,17 +658,21 @@ class GraphPrinter:
|
|||||||
def register_printers():
|
def register_printers():
|
||||||
print("registering containers")
|
print("registering containers")
|
||||||
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers")
|
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers")
|
||||||
pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
|
pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
|
||||||
pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
|
pp.add_printer('fennec::deque', '^fennec::deque<.*>$', DequePrinter)
|
||||||
pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter)
|
pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
|
||||||
pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
|
pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter)
|
||||||
pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
|
pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
|
||||||
pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter)
|
pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
|
||||||
pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
|
pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter)
|
||||||
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
|
pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
|
||||||
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
|
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
|
||||||
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
|
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
|
||||||
pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
|
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
|
return pp
|
||||||
|
|
||||||
printer = register_printers()
|
printer = register_printers()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,7 +24,7 @@ from . import utility
|
|||||||
class VectorPrinter:
|
class VectorPrinter:
|
||||||
def __init__(self, val):
|
def __init__(self, val):
|
||||||
self.val = val
|
self.val = val
|
||||||
self.base = val['data']['elements']
|
self.base = val['data']['data']
|
||||||
self.len = self.base.type.range()[1] + 1
|
self.len = self.base.type.range()[1] + 1
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
@@ -51,7 +51,7 @@ class VectorPrinter:
|
|||||||
class QuaternionPrinter:
|
class QuaternionPrinter:
|
||||||
def __init__(self, val):
|
def __init__(self, val):
|
||||||
self.val = val
|
self.val = val
|
||||||
self.base = val['data']['elements']
|
self.base = val['data']['data']
|
||||||
|
|
||||||
def to_string(self):
|
def to_string(self):
|
||||||
res = ("< "
|
res = ("< "
|
||||||
@@ -72,7 +72,7 @@ class QuaternionPrinter:
|
|||||||
# VECTOR =================================================================================================================
|
# VECTOR =================================================================================================================
|
||||||
class MatrixPrinter:
|
class MatrixPrinter:
|
||||||
def __init__(self, val):
|
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_columns = self.columns.type.range()[1] + 1
|
||||||
self.num_rows = val.type.template_argument(1)
|
self.num_rows = val.type.template_argument(1)
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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
|
# 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
|
# 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
|
# it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,7 +27,7 @@ class CStringPrinter:
|
|||||||
return 'string'
|
return 'string'
|
||||||
|
|
||||||
def to_string(self):
|
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
|
return value
|
||||||
|
|
||||||
def display_hint(self):
|
def display_hint(self):
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# ======================================================================================================================
|
# ======================================================================================================================
|
||||||
# fennec, a free and open source game engine
|
# 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
|
# 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
|
# 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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \file array.h
|
/// \file fennec/containers/array.h
|
||||||
/// \brief statically allocated array wrapper
|
/// \brief A header containing the definition for a static/stack allocated array
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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/types.h>
|
||||||
#include <fennec/lang/assert.h>
|
#include <fennec/lang/assert.h>
|
||||||
|
#include <fennec/lang/metasequences.h>
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \brief wrapper for fixed size arrays
|
/// \brief Data Structure that defines a compile-time allocated array
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// | Property | Value |
|
/// | Property | Value |
|
||||||
/// |:--------:|:-----------------------:|
|
/// |:-----------:|:----------:|
|
||||||
/// | stable | \emoji heavy_check_mark |
|
/// | stable | ✅ |
|
||||||
/// | access | \f$O(1)\f$ |
|
/// | dynamic | ⛔ |
|
||||||
/// | space | \f$O(N)\f$ |
|
/// | 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 ValueT value type
|
||||||
/// \tparam ElemV number of elements
|
/// \tparam N number of elements
|
||||||
template<typename ValueT, size_t ElemV>
|
template<typename ValueT, size_t N>
|
||||||
struct array
|
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
|
/// \brief Returns the number of elements in the array.
|
||||||
ValueT elements[ElemV];
|
/// \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
|
/// \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
|
/// \details Returns a reference to the element at \c i
|
||||||
/// \param i index of the element to return
|
/// \param i index of the element to return
|
||||||
/// \return reference to the requested element
|
/// \return reference to the requested element
|
||||||
///
|
///
|
||||||
/// \par Time-Complexity
|
/// \par Complexity
|
||||||
/// Constant
|
/// \f$O(1)\f$
|
||||||
///
|
///
|
||||||
/// \par Space-Complexity
|
constexpr value_t& operator[](size_t i) {
|
||||||
/// Constant
|
assertd(i < N, "Array Out of Bounds");
|
||||||
constexpr const ValueT& operator[](size_t i) const {
|
return data[i];
|
||||||
assertd(i < ElemV, "Array Out of Bounds");
|
|
||||||
return elements[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
|
// Comparison ==========================================================================================================
|
||||||
/// @{
|
public:
|
||||||
|
|
||||||
///
|
|
||||||
/// \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; }
|
|
||||||
|
|
||||||
/// @}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// \name Comparison Operators
|
/// \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) {
|
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) {
|
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:
|
private:
|
||||||
template<size_t...i>
|
template<size_t...i>
|
||||||
static bool _compare(const array& lhs, const array& rhs, const_index_sequence<i...>) {
|
static bool _compare(const array& lhs, const array& rhs, index_metasequence<i...>) {
|
||||||
return ((lhs[i] == rhs[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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_DEQUE_H
|
||||||
#define FENNEC_CONTAINERS_DEQUE_H
|
#define FENNEC_CONTAINERS_DEQUE_H
|
||||||
|
|
||||||
@@ -26,108 +38,297 @@
|
|||||||
namespace fennec
|
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>>
|
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||||
struct deque {
|
struct deque {
|
||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
|
private:
|
||||||
|
struct node;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using elem_t = TypeT;
|
|
||||||
|
|
||||||
struct node {
|
/// \name Definitions
|
||||||
elem_t value;
|
/// @{
|
||||||
node *prev, *next;
|
|
||||||
|
|
||||||
template<typename...ArgsT>
|
using value_t = TypeT; //!< Alias for the value type
|
||||||
node(node* prev, node* next, ArgsT&&...args)
|
using alloc_t = allocator_traits<AllocT>::template rebind<node>; //!< The underlying allocator type
|
||||||
: value(fennec::forward<ArgsT>(args)...)
|
using elem_t = node*; //!< The underlying element type
|
||||||
, prev(prev), next(next) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~node() = default;
|
/// @}
|
||||||
};
|
|
||||||
|
|
||||||
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
|
|
||||||
|
class iterator;
|
||||||
|
|
||||||
|
|
||||||
// Constructors ========================================================================================================
|
// Constructors ========================================================================================================
|
||||||
|
|
||||||
|
/// \name Constructors & Destructor
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Default Constructor, initializes an empty deque
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
deque()
|
deque()
|
||||||
: _alloc()
|
: _alloc()
|
||||||
, _first(nullptr)
|
, _first(nullptr)
|
||||||
, _last(nullptr) {
|
, _last(nullptr)
|
||||||
|
, _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
|
deque(deque&& deque) noexcept
|
||||||
: _alloc(deque._alloc)
|
: _alloc(deque._alloc)
|
||||||
, _first(deque._first)
|
, _first(deque._first)
|
||||||
, _last(deque._last) {
|
, _last(deque._last)
|
||||||
|
, _size(deque._size) {
|
||||||
deque._first = nullptr;
|
deque._first = nullptr;
|
||||||
deque._last = nullptr;
|
deque._last = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Destructor, calls deque::clear
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
~deque() {
|
~deque() {
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
// Properties ==========================================================================================================
|
// 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;
|
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;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Access ==============================================================================================================
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
elem_t& front() {
|
/// \name Access
|
||||||
assert(not empty(), "Attempted to access an empty deque.");
|
/// @{
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \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;
|
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;
|
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);
|
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);
|
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>
|
template<typename...ArgsT>
|
||||||
void emplace_front(ArgsT&&...args) {
|
void emplace_front(ArgsT&&...args) {
|
||||||
this->_push_front(fennec::forward<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);
|
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);
|
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>
|
template<typename...ArgsT>
|
||||||
void emplace_back(ArgsT&&...args) {
|
void emplace_back(ArgsT&&...args) {
|
||||||
this->_push_back(fennec::forward<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() {
|
void clear() {
|
||||||
node* it = _first;
|
elem_t it = _first;
|
||||||
while (it) {
|
while (it) {
|
||||||
node* next = it->next;
|
elem_t next = it->next;
|
||||||
fennec::destruct(it);
|
fennec::destruct(it);
|
||||||
_alloc.deallocate(it);
|
_alloc.deallocate(it);
|
||||||
it = next;
|
it = next;
|
||||||
@@ -137,11 +338,17 @@ public:
|
|||||||
_size = 0;
|
_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Erase the First Element
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
void pop_front() {
|
void pop_front() {
|
||||||
if (_first == nullptr) {
|
if (_first == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node* next = _first->next;
|
elem_t next = _first->next;
|
||||||
fennec::destruct(_first);
|
fennec::destruct(_first);
|
||||||
_alloc.deallocate(_first);
|
_alloc.deallocate(_first);
|
||||||
_first = next;
|
_first = next;
|
||||||
@@ -149,11 +356,17 @@ public:
|
|||||||
--_size;
|
--_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Erase the Last Element
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
void pop_back() {
|
void pop_back() {
|
||||||
if (_last == nullptr) {
|
if (_last == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
node* prev = _last->prev;
|
elem_t prev = _last->prev;
|
||||||
fennec::destruct(_last);
|
fennec::destruct(_last);
|
||||||
_alloc.deallocate(_last);
|
_alloc.deallocate(_last);
|
||||||
_last = prev;
|
_last = prev;
|
||||||
@@ -161,14 +374,28 @@ public:
|
|||||||
--_size;
|
--_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
|
// Iteration ===========================================================================================================
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: Decide whether you should be able to iterate over a deque
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
// Private Member Variables ============================================================================================
|
||||||
private:
|
private:
|
||||||
alloc_t _alloc;
|
alloc_t _alloc;
|
||||||
node *_first, *_last;
|
node *_first, *_last;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Helpers =====================================================================================================
|
||||||
|
private:
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
void _push_front(ArgsT&&...args) {
|
void _push_front(ArgsT&&...args) {
|
||||||
node* next = _first;
|
elem_t next = _first;
|
||||||
_first = _alloc.allocate(1);
|
_first = _alloc.allocate(1);
|
||||||
fennec::construct(_first, nullptr, next, fennec::forward<ArgsT>(args)...);
|
fennec::construct(_first, nullptr, next, fennec::forward<ArgsT>(args)...);
|
||||||
if (next) {
|
if (next) {
|
||||||
@@ -181,7 +408,7 @@ private:
|
|||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
void _push_back(ArgsT&&...args) {
|
void _push_back(ArgsT&&...args) {
|
||||||
node* prev = _last;
|
elem_t prev = _last;
|
||||||
_last = _alloc.allocate(1);
|
_last = _alloc.allocate(1);
|
||||||
fennec::construct(_last, prev, nullptr, fennec::forward<ArgsT>(args)...);
|
fennec::construct(_last, prev, nullptr, fennec::forward<ArgsT>(args)...);
|
||||||
if (prev) {
|
if (prev) {
|
||||||
@@ -191,6 +418,22 @@ private:
|
|||||||
}
|
}
|
||||||
++_size;
|
++_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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,20 +18,23 @@
|
|||||||
|
|
||||||
#ifndef FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
#ifndef FENNEC_CONTAINERS_DETAIL_TUPLE_H
|
||||||
#define 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>
|
#include <fennec/lang/utility.h>
|
||||||
|
|
||||||
namespace fennec::detail
|
namespace fennec::detail
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
template <std::size_t I, typename T>
|
template <size_t I, typename T>
|
||||||
struct _tuple_leaf
|
struct _tuple_leaf
|
||||||
{
|
{
|
||||||
template <typename ArgT>
|
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;
|
T value;
|
||||||
};
|
};
|
||||||
@@ -40,12 +43,17 @@ template <typename, typename...>
|
|||||||
struct _tuple;
|
struct _tuple;
|
||||||
|
|
||||||
template <size_t...IndicesV, typename...TypesT>
|
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>
|
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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,20 +17,21 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \file dynarray.h
|
/// \file fennec/containers/dynarray.h
|
||||||
/// \brief dynamically allocated array wrapper
|
/// \brief A header containing the definition for a dynamically allocated array
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
#ifndef FENNEC_CONTAINERS_DYNARRAY_H
|
||||||
#define FENNEC_CONTAINERS_DYNARRAY_H
|
#define FENNEC_CONTAINERS_DYNARRAY_H
|
||||||
|
|
||||||
|
#include <fennec/containers/initializer_list.h>
|
||||||
#include <fennec/lang/utility.h>
|
#include <fennec/lang/utility.h>
|
||||||
#include <fennec/memory/allocator.h>
|
#include <fennec/memory/allocator.h>
|
||||||
#include <fennec/memory/new.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
|
/// \details
|
||||||
/// | Property | Value |
|
/// | Property | Value |
|
||||||
/// |-----------|:----------:|
|
/// |:-----------:|:----------:|
|
||||||
/// | stable | \emoji x |
|
/// | stable | ⛔ |
|
||||||
/// | access | \f$O(1)\f$ |
|
/// | dynamic | ✅ |
|
||||||
/// | insertion | \f$O(N)\f$ |
|
/// | homogeneous | ✅ |
|
||||||
/// | deletion | \f$O(N)\f$ |
|
/// | distinct | ⛔ |
|
||||||
/// | space | \f$O(N)\f$ |
|
/// | 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
|
/// \tparam TypeT value type
|
||||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||||
class dynarray {
|
struct dynarray {
|
||||||
public:
|
|
||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
using element_t = TypeT;
|
/// \name Definitions
|
||||||
using alloc_t = Alloc;
|
/// @{
|
||||||
|
|
||||||
|
using value_t = TypeT; //!< Alias for the value type
|
||||||
|
using alloc_t = Alloc; //!< Alias for the allocator type
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Constructors ========================================================================================================
|
// Constructors ========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Constructors & Destructor
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Default Constructor, initializes an empty allocation.
|
/// \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.
|
/// \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.
|
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some
|
||||||
constexpr dynarray(const alloc_t& alloc)
|
/// data.
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
|
explicit constexpr dynarray(const alloc_t& alloc)
|
||||||
: _alloc(8, alloc)
|
: _alloc(8, alloc)
|
||||||
, _size(0) {
|
, _size(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Alloc Move Constructor, initialize empty allocation with allocator instance.
|
/// \brief Sized Allocation, initializes a dynarray with \f$n\f$ elements using the default constructor.
|
||||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
|
/// \param n The number of elements.
|
||||||
constexpr dynarray(alloc_t&& alloc) noexcept
|
|
||||||
: _alloc(8, alloc)
|
|
||||||
, _size(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Sized Allocation, create an allocation of size `n` elements, initialized with the default constructor.
|
/// \par Complexity
|
||||||
constexpr dynarray(size_t n)
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
|
explicit constexpr dynarray(size_t n)
|
||||||
: _alloc(n)
|
: _alloc(n)
|
||||||
, _size(n)
|
, _size(n)
|
||||||
{
|
{
|
||||||
element_t* addr = _alloc.data();
|
value_t* addr = _alloc.data();
|
||||||
for(; n > 0; --n, ++addr) {
|
for(; n > 0; --n, ++addr) {
|
||||||
fennec::construct(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 n The number of elements
|
||||||
/// \param alloc The allocator object to copy
|
/// \param alloc The allocator object to copy
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
constexpr dynarray(size_t n, const alloc_t& alloc)
|
constexpr dynarray(size_t n, const alloc_t& alloc)
|
||||||
: _alloc(n, alloc)
|
: _alloc(n, alloc)
|
||||||
, _size(n) {
|
, _size(n) {
|
||||||
element_t* addr = _alloc.data();
|
value_t* addr = _alloc.data();
|
||||||
for(; n > 0; --n, ++addr) {
|
for(; n > 0; --n, ++addr) {
|
||||||
fennec::construct(addr);
|
fennec::construct(addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Sized Allocation Alloc Move Constructor, initializes a dynarray with allocator `alloc` and `n` elements using the default constructor.
|
/// \brief Sized Allocation Copy Constructor, Create an allocation of size \f$n\f$ elements, with each element
|
||||||
/// \param n The number of elements
|
/// constructed using the copy constructor
|
||||||
/// \param alloc The allocator object to copy
|
/// \param n the number of elements
|
||||||
constexpr dynarray(size_t n, alloc_t&& alloc)
|
/// \param val the value to copy
|
||||||
: _alloc(n, alloc)
|
///
|
||||||
, _size(n) {
|
/// \par Complexity
|
||||||
element_t* addr = _alloc.data();
|
/// \f$O(N)\f$
|
||||||
for(; n > 0; --n, ++addr) {
|
|
||||||
fennec::construct(addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \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)
|
constexpr dynarray(size_t n, const TypeT& val)
|
||||||
: _alloc(n)
|
: _alloc(n)
|
||||||
, _size(n) {
|
, _size(n) {
|
||||||
element_t* addr = _alloc.data();
|
value_t* addr = _alloc.data();
|
||||||
for(; n > 0; --n, ++addr) {
|
for(; n > 0; --n, ++addr) {
|
||||||
fennec::construct(addr, val);
|
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
|
/// \brief Emplace Constructor
|
||||||
/// \tparam ArgsT A sequence of argument types
|
/// \tparam ArgsT A sequence of argument types
|
||||||
/// \param n The number of objects to create
|
/// \param n The number of objects to create
|
||||||
/// \param args The arguments to create each object with
|
/// \param args The arguments to create each object with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr dynarray(size_t n, ArgsT&&...args) {
|
constexpr explicit dynarray(size_t n, ArgsT&&...args)
|
||||||
element_t* addr = _alloc.data();
|
: _alloc(n)
|
||||||
for(; n > 0; --n, ++addr) {
|
, _size(n) {
|
||||||
fennec::construct(addr, fennec::forward<ArgsT>(args)...);
|
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
|
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
constexpr ~dynarray() {
|
constexpr ~dynarray() {
|
||||||
element_t* addr = _alloc.data();
|
value_t* addr = _alloc.data();
|
||||||
if (addr == nullptr) return;
|
if (addr == nullptr) return;
|
||||||
for(int n = _size; n > 0; --n, ++addr) {
|
for(int n = _size; n > 0; --n, ++addr) {
|
||||||
fennec::destruct(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 ==========================================================================================================
|
// Properties ==========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Properties
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns The size of the dynarray in elements
|
/// \returns The size of the dynarray in elements
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t size() const {
|
constexpr size_t size() const {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns The current capacity, in elements, of the underlying allocation
|
/// \returns The current capacity, in elements, of the underlying allocation
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t capacity() const {
|
constexpr size_t capacity() const {
|
||||||
return _alloc.capacity();
|
return _alloc.capacity();
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns True when there are no elements active, otherwise false
|
/// \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;
|
return _size == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Element Access ======================================================================================================
|
// Element Access ======================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Access
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Array Access Operator
|
/// \brief Array Access Operator
|
||||||
/// \param i The index to access
|
/// \param i The index to access
|
||||||
/// \returns A reference to the element at index `i`
|
/// \returns A reference to the element at index \f$i\f$
|
||||||
constexpr TypeT& operator[](int i) {
|
///
|
||||||
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
|
/// \par Complexity
|
||||||
return _alloc.data()[i];
|
/// \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)
|
/// \brief Array Access Operator (const)
|
||||||
/// \param i The index to access
|
/// \param i The index to access
|
||||||
/// \returns A const qualified reference to the element at index `i`
|
/// \returns A const qualified reference to the element at index \f$i\f$
|
||||||
constexpr const TypeT& operator[](int i) const {
|
///
|
||||||
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
|
/// \par Complexity
|
||||||
return _alloc.data()[i];
|
/// \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
|
/// \returns Reference to the first element in the dynarray
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr TypeT& front() {
|
constexpr TypeT& front() {
|
||||||
return this->operator[](0);
|
return this->operator[](0);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns A const-qualified reference to the first element in the dynarray
|
/// \returns A const-qualified reference to the first element in the dynarray
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr const TypeT& front() const {
|
constexpr const TypeT& front() const {
|
||||||
return this->operator[](0);
|
return this->operator[](0);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns A reference to the last element in the dynarray
|
/// \returns A reference to the last element in the dynarray
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr TypeT& back() {
|
constexpr TypeT& back() {
|
||||||
return this->operator[](size() - 1);
|
return this->operator[](size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns A const-qualified reference to the last element in the dynarray
|
/// \returns A const-qualified reference to the last element in the dynarray
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr const TypeT& back() const {
|
constexpr const TypeT& back() const {
|
||||||
return this->operator[](size() - 1);
|
return this->operator[](size() - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief "Iterator" Begin Function
|
/// \returns A pointer to the underlying allocation
|
||||||
/// \returns A pointer to the first element in the dynarray
|
///
|
||||||
constexpr TypeT* begin() { return _alloc.data(); }
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
|
constexpr TypeT* data() {
|
||||||
|
return _alloc.data();
|
||||||
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief "Iterator" End Function
|
/// \returns A pointer to the underlying allocation
|
||||||
/// \return A pointer to the address after the last element in the dynarray
|
|
||||||
constexpr TypeT* end() { return begin() + _size; }
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Const "Iterator" Begin Function
|
/// \par Complexity
|
||||||
/// \returns A const qualified pointer to the first element in the dynarray
|
/// \f$O(1)\f$
|
||||||
constexpr const TypeT* begin() const { return _alloc; }
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Const "Iterator" End Function
|
constexpr const TypeT* data() const {
|
||||||
/// \return A const qualified pointer to the address after the last element in the dynarray
|
return _alloc.data();
|
||||||
constexpr const TypeT* end() const { return begin() + _size; }
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Insertion & Deletion ================================================================================================
|
// Modifiers ===========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Modifiers
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Move Insertion
|
/// \brief Move Insertion
|
||||||
/// \param i index to insert at
|
/// \param i index to insert at
|
||||||
/// \param val the value to initialize with
|
/// \param val the value to initialize with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
constexpr void insert(size_t i, TypeT&& val) {
|
constexpr void insert(size_t i, TypeT&& val) {
|
||||||
|
|
||||||
// Grow if the size has reached the capacity of the allocation
|
// Grow if the size has reached the capacity of the allocation
|
||||||
@@ -277,6 +549,10 @@ public:
|
|||||||
/// \brief Copy Insertion
|
/// \brief Copy Insertion
|
||||||
/// \param i index to insert at
|
/// \param i index to insert at
|
||||||
/// \param val the value to initialize with
|
/// \param val the value to initialize with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
constexpr void insert(size_t i, const TypeT& val) {
|
constexpr void insert(size_t i, const TypeT& val) {
|
||||||
|
|
||||||
// Grow if the size has reached the capacity of the allocation
|
// Grow if the size has reached the capacity of the allocation
|
||||||
@@ -303,6 +579,10 @@ public:
|
|||||||
/// \param i index to insert at
|
/// \param i index to insert at
|
||||||
/// \param args Arguments to construct with
|
/// \param args Arguments to construct with
|
||||||
/// \tparam ArgsT Argument types
|
/// \tparam ArgsT Argument types
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr void emplace(size_t i, ArgsT&&...args) {
|
constexpr void emplace(size_t i, ArgsT&&...args) {
|
||||||
|
|
||||||
@@ -327,6 +607,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Push Back Copy
|
/// \brief Push Back Copy
|
||||||
/// \param val Value to initialize with
|
/// \param val Value to initialize with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void push_back(const TypeT& val) {
|
constexpr void push_back(const TypeT& val) {
|
||||||
dynarray::insert(_size, val);
|
dynarray::insert(_size, val);
|
||||||
}
|
}
|
||||||
@@ -334,6 +618,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Push Back Move
|
/// \brief Push Back Move
|
||||||
/// \param val Value to initialize with
|
/// \param val Value to initialize with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void push_back(TypeT&& val) {
|
constexpr void push_back(TypeT&& val) {
|
||||||
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
||||||
}
|
}
|
||||||
@@ -342,6 +630,10 @@ public:
|
|||||||
/// \brief Emplace Back
|
/// \brief Emplace Back
|
||||||
/// \tparam ArgsT Argument Types
|
/// \tparam ArgsT Argument Types
|
||||||
/// \param args Arguments to construct with
|
/// \param args Arguments to construct with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr void emplace_back(ArgsT...args) {
|
constexpr void emplace_back(ArgsT...args) {
|
||||||
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
|
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
|
||||||
@@ -349,6 +641,10 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Erase last element
|
/// \brief Erase last element
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void pop_back() {
|
constexpr void pop_back() {
|
||||||
fennec::destruct(&_alloc[--_size]);
|
fennec::destruct(&_alloc[--_size]);
|
||||||
}
|
}
|
||||||
@@ -356,21 +652,135 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Resize the dynarray, invoking the default constructor for all new elements
|
/// \brief Resize the dynarray, invoking the default constructor for all new elements
|
||||||
/// \param n The new size in 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) {
|
constexpr void resize(size_t n) {
|
||||||
_alloc.creallocate(n);
|
_reduce(n);
|
||||||
|
_alloc.reallocate(n);
|
||||||
|
|
||||||
while (_size < n) {
|
for (size_t i = _size; i < n; ++i) {
|
||||||
emplace_back();
|
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.reallocate(_alloc.capacity() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper to destruct elements past n
|
||||||
|
constexpr void _reduce(size_t n) {
|
||||||
|
while (_size > n) {
|
||||||
|
fennec::destruct(&_alloc[--_size]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
constexpr void _grow() {
|
|
||||||
_alloc.creallocate(_alloc.capacity() * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
allocation<element_t, alloc_t> _alloc;
|
|
||||||
size_t _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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_GRAPH_H
|
||||||
#define FENNEC_CONTAINERS_GRAPH_H
|
#define FENNEC_CONTAINERS_GRAPH_H
|
||||||
|
|
||||||
@@ -28,136 +40,607 @@
|
|||||||
/*
|
/*
|
||||||
* With the directed tree we were able to cheat a little, the structure has more rules to it which allows
|
* 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
|
* 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
|
* 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
|
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 {
|
struct graph {
|
||||||
public:
|
public:
|
||||||
using weight_t = ConnectionT;
|
// Definitions =========================================================================================================
|
||||||
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>;
|
|
||||||
|
|
||||||
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;
|
constexpr graph() = default;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Destructor
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N + M)\f$
|
||||||
|
///
|
||||||
constexpr ~graph() = default;
|
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;
|
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;
|
constexpr graph& operator=(graph&& g) = default;
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Properties ==========================================================================================================
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
constexpr size_t num_nodes() const {
|
/// \name Properties
|
||||||
return _node_pool.size();
|
/// @{
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \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 {
|
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 ===============================================================================================================
|
/// \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
|
||||||
constexpr size_t insert(node_t&& node) {
|
/// \param b The second vertex
|
||||||
return this->_insert(fennec::forward<node_t>(node));
|
/// \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>
|
template<typename...ArgsT>
|
||||||
constexpr size_t emplace(ArgsT&&...args) {
|
constexpr size_t emplace(ArgsT&&...args) {
|
||||||
return this->_insert(fennec::forward<ArgsT>(args)...);
|
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void erase(size_t node) {
|
///
|
||||||
disconnect(node);
|
/// \brief Erase a vertex from the graph
|
||||||
_node_pool.erase(node);
|
/// \param vertex The id of the vertex to erase
|
||||||
}
|
///
|
||||||
|
/// \par Complexity
|
||||||
constexpr node_t& operator[](size_t node) {
|
/// \f$O(M)\f$
|
||||||
return _node_pool[node];
|
///
|
||||||
}
|
constexpr void erase(size_t vertex) {
|
||||||
|
cut(vertex);
|
||||||
constexpr const node_t& operator[](size_t node) const {
|
_vertex_pool.erase(vertex);
|
||||||
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 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>
|
template<typename...ArgsT>
|
||||||
constexpr void connect(size_t a, size_t b, ArgsT&&...args) {
|
constexpr void make_edge(size_t a, size_t b, ArgsT&&...args) {
|
||||||
if (_conn_map.size() < _node_pool.capacity()) {
|
if (a == b) {
|
||||||
_conn_map.resize(_node_pool.capacity());
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t conn = _conn_pool.emplace(fennec::forward<ArgsT>(args)...);
|
if (_edge_map.size() < _vertex_pool.capacity()) {
|
||||||
_conn_map[a].emplace(b, conn);
|
_edge_map.resize(_vertex_pool.capacity());
|
||||||
_conn_map[b].emplace(a, 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void disconnect(size_t a, size_t b) {
|
///
|
||||||
size_t c = *_conn_map[a][b];
|
/// \brief Form an undirected edge between vertex \f$a\f$ and vertex \f$b\f$
|
||||||
_conn_pool.erase(c);
|
/// \tparam ArgsT The argument types
|
||||||
_conn_map[a].erase(b);
|
/// \param a The first vertex id
|
||||||
_conn_map[b].erase(a);
|
/// \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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
_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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect(size_t n) {
|
///
|
||||||
list<size_t> conns = connections(n);
|
/// \brief Disconnect an edge from vertex \f$a\f$ to vertex \f$b\f$
|
||||||
for (size_t conn : conns) {
|
/// \param a The first vertex id
|
||||||
disconnect(n, conn);
|
/// \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:
|
private:
|
||||||
node_pool_t _node_pool;
|
vertex_pool_t _vertex_pool;
|
||||||
conn_pool_t _conn_pool;
|
edge_pool_t _edge_pool;
|
||||||
conn_map_t _conn_map;
|
edge_map_t _edge_map;
|
||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
size_t _insert(ArgsT&&...args) {
|
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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -17,14 +17,14 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \file list.h
|
/// \file fennec/containers/list.h
|
||||||
/// \brief List of elements
|
/// \brief A header containing the definition for a linked list of values
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
/// \details
|
||||||
/// This data-structure behaves like a linked list, but does not use pointers. Instead, it is in-array. This creates the
|
/// This data-structure behaves like a linked list, but does not use pointers. Instead, it is in-array. This creates the
|
||||||
/// following properties:
|
/// following properties:
|
||||||
///
|
///
|
||||||
/// | Property | Value |
|
/// | Property | Value |
|
||||||
/// |:----------|:-------------------------------------:|
|
/// |:-----------:|:----------:|
|
||||||
/// | stable | \emoji x |
|
/// | stable | ⛔ |
|
||||||
/// | access | \f$O(N)\f$ or \f$O(1)\f$ (front/back) |
|
/// | dynamic | ✅ |
|
||||||
/// | insertion | \f$O(N)\f$ or \f$O(1)\f$ (iterator) |
|
/// | homogeneous | ✅ |
|
||||||
/// | deletion | \f$O(N)\f$ or \f$O(1)\f$ (iterator) |
|
/// | distinct | ⛔ |
|
||||||
/// | space | \f$O(N)\f$ |
|
/// | 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
|
/// \tparam TypeT value type
|
||||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||||
struct list {
|
struct list {
|
||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct node;
|
struct node;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
|
||||||
using value_t = TypeT;
|
/// \name Definitions
|
||||||
using elem_t = node;
|
/// @{
|
||||||
static constexpr size_t npos = -1;
|
|
||||||
|
/// \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 iterator;
|
||||||
class const_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.
|
/// \brief Default Constructor, initializes an empty list.
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr list()
|
constexpr list()
|
||||||
: _table(), _freed(), _root(npos), _last(npos), _size(0) {
|
: _table(), _freed(), _root(npos), _last(npos), _size(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Destructor, destructs all elements then releases the allocation.
|
/// \brief Copy Constructor, copies all elements in \f$l\f$ with optimized layout
|
||||||
constexpr ~list() {
|
/// \param l The list to copy
|
||||||
for (size_t i = 0; i < capacity(); ++i) {
|
///
|
||||||
_table[i].value = nullopt;
|
/// \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() {
|
||||||
|
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 ==========================================================================================================
|
// Properties ==========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Properties
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns The size of the list in elements.
|
/// \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.
|
/// \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.
|
/// \returns \f$true\f$ when the list is empty, \f$false\f$ otherwise.
|
||||||
constexpr bool empty() const { return _root == npos; }
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
|
constexpr bool is_empty() const {
|
||||||
|
return _root == npos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Access ==============================================================================================================
|
// Access ==============================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Access
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Array Access Operator
|
/// \brief Array Access Operator
|
||||||
/// \param i Index to access
|
/// \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) {
|
constexpr value_t& operator[](int i) {
|
||||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||||
size_t n = _walk(i);
|
size_t n = _walk(i);
|
||||||
@@ -125,9 +259,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Const Array Access Operator
|
/// \brief Const Array Access Operator
|
||||||
/// \param i Index to access
|
/// \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 {
|
constexpr const value_t& operator[](int i) const {
|
||||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||||
size_t n = _walk(i);
|
size_t n = _walk(i);
|
||||||
@@ -138,6 +274,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Access Front Element
|
/// \brief Access Front Element
|
||||||
/// \returns A reference to the first element in the list
|
/// \returns A reference to the first element in the list
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr value_t& front() {
|
constexpr value_t& front() {
|
||||||
return *_table[_root].value;
|
return *_table[_root].value;
|
||||||
}
|
}
|
||||||
@@ -145,6 +285,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Const Access Front Element
|
/// \brief Const Access Front Element
|
||||||
/// \returns A const-qualified reference to the first element in the list
|
/// \returns A const-qualified reference to the first element in the list
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr const value_t& front() const {
|
constexpr const value_t& front() const {
|
||||||
return *_table[_root].value;
|
return *_table[_root].value;
|
||||||
}
|
}
|
||||||
@@ -152,6 +296,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Access Back Element
|
/// \brief Access Back Element
|
||||||
/// \returns A reference to the last element in the list
|
/// \returns A reference to the last element in the list
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr value_t& back() {
|
constexpr value_t& back() {
|
||||||
return *_table[_last].value;
|
return *_table[_last].value;
|
||||||
}
|
}
|
||||||
@@ -159,20 +307,33 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Const Access Back Element
|
/// \brief Const Access Back Element
|
||||||
/// \returns A const-qualified reference to the last element in the list
|
/// \returns A const-qualified reference to the last element in the list
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr const value_t& back() const {
|
constexpr const value_t& back() const {
|
||||||
return *_table[_last].value;
|
return *_table[_last].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
// Insertions & Deletions ==============================================================================================
|
|
||||||
|
// Modifiers ===========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Modifiers
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Copy Insertion
|
/// \brief Copy Insertion
|
||||||
/// \param it Location to insert at
|
/// \param it Location to insert at
|
||||||
/// \param x value to copy
|
/// \param x value to copy
|
||||||
|
/// \returns The id of the inserted node
|
||||||
///
|
///
|
||||||
/// \details \f$O(1)\f$
|
/// \par Complexity
|
||||||
constexpr size_t insert(const iterator& it, const value_t& x) {
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
|
constexpr iterator insert(const iterator& it, const value_t& x) {
|
||||||
return this->_insert(it._n, x);
|
return this->_insert(it._n, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,9 +341,12 @@ public:
|
|||||||
/// \brief Move Insertion
|
/// \brief Move Insertion
|
||||||
/// \param it Location to insert at
|
/// \param it Location to insert at
|
||||||
/// \param x value to move
|
/// \param x value to move
|
||||||
|
/// \returns The id of the inserted node
|
||||||
///
|
///
|
||||||
/// \details \f$O(1)\f$
|
/// \par Complexity
|
||||||
constexpr size_t insert(const iterator& it, value_t&& x) {
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
|
constexpr iterator insert(const iterator& it, value_t&& x) {
|
||||||
return this->_insert(it._n, fennec::forward<value_t>(x));
|
return this->_insert(it._n, fennec::forward<value_t>(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,9 +354,12 @@ public:
|
|||||||
/// \brief Copy Insertion
|
/// \brief Copy Insertion
|
||||||
/// \param i Index to insert at
|
/// \param i Index to insert at
|
||||||
/// \param x value to copy
|
/// \param x value to copy
|
||||||
|
/// \returns The id of the inserted node
|
||||||
///
|
///
|
||||||
/// \details \f$O(N)\f$
|
/// \par Complexity
|
||||||
constexpr size_t insert(size_t i, const value_t& x) {
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
|
constexpr iterator insert(size_t i, const value_t& x) {
|
||||||
assert(i <= size(), "Index out of Bounds");
|
assert(i <= size(), "Index out of Bounds");
|
||||||
|
|
||||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||||
@@ -203,9 +370,12 @@ public:
|
|||||||
/// \brief Move Insertion
|
/// \brief Move Insertion
|
||||||
/// \param i Index to insert at
|
/// \param i Index to insert at
|
||||||
/// \param x value to move
|
/// \param x value to move
|
||||||
|
/// \returns The id of the inserted node
|
||||||
///
|
///
|
||||||
/// \details \f$O(N)\f$
|
/// \par Complexity
|
||||||
constexpr size_t insert(size_t i, value_t&& x) {
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
|
constexpr iterator insert(size_t i, value_t&& x) {
|
||||||
assert(i <= size(), "Index out of Bounds");
|
assert(i <= size(), "Index out of Bounds");
|
||||||
|
|
||||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||||
@@ -217,27 +387,55 @@ public:
|
|||||||
/// \tparam ArgsT Argument types
|
/// \tparam ArgsT Argument types
|
||||||
/// \param i Index to insert at
|
/// \param i Index to insert at
|
||||||
/// \param args Arguments to construct with
|
/// \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>
|
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");
|
assert(i <= size(), "Index out of Bounds");
|
||||||
|
|
||||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||||
return this->_insert(n, fennec::forward<ArgsT>(args)...);
|
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
|
/// \brief Push Front Copy
|
||||||
/// \param x Value to 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);
|
return this->_insert(_root, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Push Front Move
|
/// \brief Push Front Move
|
||||||
/// \param x Value to 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));
|
return this->_insert(_root, fennec::forward<value_t>(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,22 +443,37 @@ public:
|
|||||||
/// \brief Emplace Front
|
/// \brief Emplace Front
|
||||||
/// \param args Arguments to construct with
|
/// \param args Arguments to construct with
|
||||||
/// \tparam ArgsT Argument types
|
/// \tparam ArgsT Argument types
|
||||||
|
/// \returns The id of the inserted node
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t emplace_front(ArgsT&&...args) {
|
constexpr iterator emplace_front(ArgsT&&...args) {
|
||||||
return this->_insert(_root, fennec::forward<ArgsT>(args)...);
|
return this->_insert(_root, fennec::forward<ArgsT>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Push Back Copy
|
/// \brief Push Back Copy
|
||||||
/// \param x Value to 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);
|
return this->_insert(npos, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Push Back Move
|
/// \brief Push Back Move
|
||||||
/// \param x Value to 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));
|
return this->_insert(npos, fennec::forward<value_t>(x));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,14 +481,23 @@ public:
|
|||||||
/// \brief Emplace Back
|
/// \brief Emplace Back
|
||||||
/// \param args Arguments to construct with
|
/// \param args Arguments to construct with
|
||||||
/// \tparam ArgsT Argument types
|
/// \tparam ArgsT Argument types
|
||||||
|
/// \returns The id of the inserted node
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t emplace_back(ArgsT&&...args) {
|
constexpr iterator emplace_back(ArgsT&&...args) {
|
||||||
return this->_insert(npos, fennec::forward<ArgsT>(args)...);
|
return this->_insert(npos, fennec::forward<ArgsT>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Erase Element
|
/// \brief Erase Element
|
||||||
/// \param i Index to erase
|
/// \param i Index to erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
constexpr void erase(size_t i) {
|
constexpr void erase(size_t i) {
|
||||||
assert(i < size(), "Index out of Bounds!");
|
assert(i < size(), "Index out of Bounds!");
|
||||||
size_t n = _walk(i);
|
size_t n = _walk(i);
|
||||||
@@ -285,59 +507,156 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Erase Element
|
/// \brief Erase Element
|
||||||
/// \param it Location to Erase
|
/// \param it Location to Erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void erase(const iterator& it) {
|
constexpr void erase(const iterator& it) {
|
||||||
_erase(it._n);
|
this->_erase(it._n);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Pop Front, erases first element
|
/// \brief Pop Front, erases first element
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void pop_front() {
|
constexpr void pop_front() {
|
||||||
_erase(_root);
|
_erase(_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Pop Back, erases first element
|
/// \brief Pop Back, erases first element
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void pop_back() {
|
constexpr void pop_back() {
|
||||||
_erase(_last);
|
_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 {
|
class iterator {
|
||||||
public:
|
public:
|
||||||
|
///
|
||||||
|
/// \brief destructor
|
||||||
~iterator() {
|
~iterator() {
|
||||||
_list = nullptr;
|
_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) {
|
constexpr friend iterator& operator++(iterator& rhs) {
|
||||||
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
|
rhs._n = rhs._list->_next(rhs._n);
|
||||||
return rhs;
|
|
||||||
}
|
|
||||||
rhs._n = npos;
|
|
||||||
return rhs;
|
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) {
|
constexpr friend iterator operator++(iterator& lhs, int) {
|
||||||
iterator prev = lhs;
|
iterator prev = lhs;
|
||||||
++lhs;
|
++lhs;
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief dereference operator
|
||||||
|
/// \returns a reference to the value pointed by the iterator
|
||||||
constexpr value_t& operator*() {
|
constexpr value_t& operator*() {
|
||||||
return *(_list->_table[_n].value);
|
return *(_list->_table[_n].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief pointer access operator
|
||||||
|
/// \returns a pointer to the value pointed by the iterator
|
||||||
constexpr value_t* operator->() {
|
constexpr value_t* operator->() {
|
||||||
return &*(_list->_table[_n].value);
|
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) {
|
constexpr bool operator==(const iterator& it) {
|
||||||
return _list == it._list and _n == it._n;
|
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) {
|
constexpr bool operator!=(const iterator& it) {
|
||||||
return _list != it._list or _n != it._n;
|
return _list != it._list or _n != it._n;
|
||||||
}
|
}
|
||||||
@@ -357,11 +676,16 @@ public:
|
|||||||
/// \brief Iterator Class for Const Access
|
/// \brief Iterator Class for Const Access
|
||||||
class const_iterator {
|
class const_iterator {
|
||||||
public:
|
public:
|
||||||
|
///
|
||||||
|
/// \brief destructor
|
||||||
~const_iterator() {
|
~const_iterator() {
|
||||||
_list = nullptr;
|
_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) {
|
constexpr friend const_iterator& operator++(const_iterator& rhs) {
|
||||||
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
|
if (rhs._list->_next(rhs._n) < rhs._list->capacity()) {
|
||||||
return rhs;
|
return rhs;
|
||||||
@@ -370,24 +694,42 @@ public:
|
|||||||
return rhs;
|
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) {
|
constexpr friend const_iterator operator++(const_iterator& lhs, int) {
|
||||||
const_iterator prev = lhs;
|
const_iterator prev = lhs;
|
||||||
++lhs;
|
++lhs;
|
||||||
return prev;
|
return prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief dereference operator
|
||||||
|
/// \returns a reference to the value pointed by the iterator
|
||||||
constexpr const value_t& operator*() {
|
constexpr const value_t& operator*() {
|
||||||
return *(_list->_table[_n].value);
|
return *(_list->_table[_n].value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief pointer access operator
|
||||||
|
/// \returns a pointer to the value pointed by the iterator
|
||||||
constexpr const value_t* operator->() {
|
constexpr const value_t* operator->() {
|
||||||
return &*(_list->_table[_n].value);
|
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) {
|
constexpr bool operator==(const const_iterator& it) {
|
||||||
return _list == it._list and _n == it._n;
|
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) {
|
constexpr bool operator!=(const const_iterator& it) {
|
||||||
return _list != it._list or _n != it._n;
|
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:
|
private:
|
||||||
allocation<elem_t, alloc_t> _table;
|
table_t _table;
|
||||||
dynarray<size_t> _freed;
|
freed_t _freed;
|
||||||
size_t _root, _last, _size;
|
size_t _root, _last, _size;
|
||||||
|
|
||||||
friend class iterator;
|
friend class iterator;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Helpers =====================================================================================================
|
||||||
|
private:
|
||||||
constexpr void _expand() {
|
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 {
|
struct node {
|
||||||
@@ -448,18 +772,6 @@ private:
|
|||||||
, next(npos) {
|
, 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 ~node() = default;
|
||||||
|
|
||||||
constexpr void clear() {
|
constexpr void clear() {
|
||||||
@@ -487,7 +799,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t _next_free() {
|
constexpr size_t _next_free() {
|
||||||
if (not _freed.empty()) {
|
if (not _freed.is_empty()) {
|
||||||
size_t n = _freed.back();
|
size_t n = _freed.back();
|
||||||
_freed.pop_back();
|
_freed.pop_back();
|
||||||
return n;
|
return n;
|
||||||
@@ -496,20 +808,20 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t _insert(size_t n, ArgsT&&...args) {
|
constexpr iterator _insert(size_t n, ArgsT&&...args) {
|
||||||
if (size() == capacity()) {
|
if (size() == capacity()) {
|
||||||
_expand();
|
_expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t i = _next_free();
|
size_t i = _next_free();
|
||||||
++_size;
|
++_size;
|
||||||
fennec::construct(&_table[i].value, fennec::forward<ArgsT>(args)...);
|
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
|
||||||
|
|
||||||
if (_root == npos) {
|
if (_root == npos) {
|
||||||
_table[i].prev = npos;
|
_table[i].prev = npos;
|
||||||
_table[i].next = npos;
|
_table[i].next = npos;
|
||||||
_root = _last = i;
|
_root = _last = i;
|
||||||
return i;
|
return iterator(this, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n == npos) {
|
if (n == npos) {
|
||||||
@@ -517,20 +829,20 @@ private:
|
|||||||
_table[i].prev = _last;
|
_table[i].prev = _last;
|
||||||
_table[i].next = npos;
|
_table[i].next = npos;
|
||||||
_last = i;
|
_last = i;
|
||||||
return i;
|
return iterator(this, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
_table[i].prev = _prev(n);
|
_table[i].prev = _prev(n);
|
||||||
_table[i].next = n;
|
_table[i].next = n;
|
||||||
_table[n].prev = i;
|
_table[n].prev = i;
|
||||||
_root = n == _root ? i : _root;
|
_root = n == _root ? i : _root;
|
||||||
return i;
|
return iterator(this, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr void _erase(size_t n) {
|
constexpr void _erase(size_t n) {
|
||||||
if (n == npos) return;
|
if (n == npos) return;
|
||||||
|
|
||||||
fennec::destruct(&_table[n].value);
|
_table[n].value = nullopt;
|
||||||
_freed.push_back(n);
|
_freed.push_back(n);
|
||||||
--_size;
|
--_size;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_MAP_H
|
||||||
#define 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 KeyT The Key Type
|
||||||
/// \tparam ValueT The Value Type
|
/// \tparam ValueT The Value Type
|
||||||
/// \tparam Hash The Hash to Use
|
/// \tparam Hash The Hash to Use
|
||||||
@@ -54,32 +84,53 @@ struct map {
|
|||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
public:
|
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 {
|
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 {
|
constexpr size_t operator()(const elem_t& p) const {
|
||||||
return hash_t::operator()(p.first);
|
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 {
|
constexpr bool operator()(const elem_t& a, const elem_t& b) const {
|
||||||
return equality<KeyT>::operator()(a.first, b.first);
|
return equality<KeyT>::operator()(a.first, b.first);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
// Constructors ========================================================================================================
|
|
||||||
|
// Constructors & Destructor ===========================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Constructors & Destructor
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Default Constructor, initializes empty map
|
/// \brief Default Constructor, initializes empty map
|
||||||
@@ -89,13 +140,50 @@ public:
|
|||||||
/// \brief Destructor, Destructs all elements and releases the allocation
|
/// \brief Destructor, Destructs all elements and releases the allocation
|
||||||
constexpr ~map() = default;
|
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 ==============================================================================================================
|
// Access ==============================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Access
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Key Access Operator
|
/// \brief Key Access Operator
|
||||||
/// \param key Key value to access
|
/// \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) {
|
constexpr value_t* operator[](const KeyT& key) {
|
||||||
auto it = _set.at(this->_find(key));
|
auto it = _set.at(this->_find(key));
|
||||||
return it ? &it->second : nullptr;
|
return it ? &it->second : nullptr;
|
||||||
@@ -104,7 +192,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Key Const Access Operator
|
/// \brief Key Const Access Operator
|
||||||
/// \param key Key value to access
|
/// \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 {
|
constexpr const value_t* operator[](const KeyT& key) const {
|
||||||
auto it = _set.at(this->_find(key));
|
auto it = _set.at(this->_find(key));
|
||||||
return it ? &it->second : nullptr;
|
return it ? &it->second : nullptr;
|
||||||
@@ -112,9 +204,13 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Argument Key Access Operator
|
/// \brief Argument Key Access Operator
|
||||||
/// \tparam ArgT Argument Type
|
/// \tparam ArgsT Argument Types
|
||||||
/// \param arg Argument to construct the key with
|
/// \param args Arguments to construct the key with
|
||||||
/// \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$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr value_t* operator[](ArgsT&&...args) {
|
constexpr value_t* operator[](ArgsT&&...args) {
|
||||||
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
||||||
@@ -125,26 +221,45 @@ public:
|
|||||||
/// \brief Argument Key Const Access Operator
|
/// \brief Argument Key Const Access Operator
|
||||||
/// \tparam ArgsT Argument Type
|
/// \tparam ArgsT Argument Type
|
||||||
/// \param args Argument to construct the key with
|
/// \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>
|
template<typename...ArgsT>
|
||||||
constexpr const value_t* operator[](ArgsT&&...args) const {
|
constexpr const value_t* operator[](ArgsT&&...args) const {
|
||||||
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
|
||||||
return it ? &it->second : nullptr;
|
return it ? &it->second : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
// Insertion & Deletion ================================================================================================
|
|
||||||
|
// Modifiers ===========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Modifiers
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Key-Value Insertion
|
/// \brief Key-Value Insertion
|
||||||
/// \param pair a pair containing the key and its value
|
/// \param pair a pair containing the key and its value
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void insert(elem_t&& pair) {
|
constexpr void insert(elem_t&& pair) {
|
||||||
this->_insert(fennec::forward<elem_t>(pair));
|
this->_insert(fennec::forward<elem_t>(pair));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Key-Value Insertion
|
/// \brief Key-Value Insertion
|
||||||
|
/// \param key key to insert
|
||||||
/// \param args Arguments for constructing the key-value pair
|
/// \param args Arguments for constructing the key-value pair
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr void emplace(const KeyT& key, ArgsT&&...args) {
|
constexpr void emplace(const KeyT& key, ArgsT&&...args) {
|
||||||
this->_insert(key, fennec::forward<ArgsT>(args)...);
|
this->_insert(key, fennec::forward<ArgsT>(args)...);
|
||||||
@@ -153,6 +268,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Key-Value Insertion
|
/// \brief Key-Value Insertion
|
||||||
/// \param args Arguments for constructing the key-value pair
|
/// \param args Arguments for constructing the key-value pair
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr void emplace(ArgsT&&...args) {
|
constexpr void emplace(ArgsT&&...args) {
|
||||||
this->_insert(fennec::forward<ArgsT>(args)...);
|
this->_insert(fennec::forward<ArgsT>(args)...);
|
||||||
@@ -161,6 +280,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Erase a key
|
/// \brief Erase a key
|
||||||
/// \param key key to erase
|
/// \param key key to erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void erase(KeyT&& key) {
|
constexpr void erase(KeyT&& key) {
|
||||||
_set.erase(this->_find(fennec::forward<KeyT>(key)));
|
_set.erase(this->_find(fennec::forward<KeyT>(key)));
|
||||||
}
|
}
|
||||||
@@ -168,6 +291,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Erase a key
|
/// \brief Erase a key
|
||||||
/// \param key key to erase
|
/// \param key key to erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void erase(const KeyT& key) {
|
constexpr void erase(const KeyT& key) {
|
||||||
_set.erase(this->_find(key));
|
_set.erase(this->_find(key));
|
||||||
}
|
}
|
||||||
@@ -176,28 +303,69 @@ public:
|
|||||||
/// \brief Argument Erase
|
/// \brief Argument Erase
|
||||||
/// \tparam ArgsT Argument Types
|
/// \tparam ArgsT Argument Types
|
||||||
/// \param args Arguments to construct a key to erase
|
/// \param args Arguments to construct a key to erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr void erase(ArgsT&&...args) {
|
constexpr void erase(ArgsT&&...args) {
|
||||||
_set.erase(this->_find(fennec::forward<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 ===========================================================================================================
|
// 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() {
|
constexpr iterator begin() {
|
||||||
return _set.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() {
|
constexpr iterator end() {
|
||||||
return _set.end();
|
return _set.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
|
// Private Member Variables ============================================================================================
|
||||||
private:
|
private:
|
||||||
set_t _set;
|
set_t _set;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Helpers =====================================================================================================
|
||||||
|
private:
|
||||||
template<typename...ArgsT>
|
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
|
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||||
pair<KeyT, ValueT> val;
|
pair<KeyT, ValueT> val;
|
||||||
@@ -205,7 +373,9 @@ private:
|
|||||||
~U() {
|
~U() {
|
||||||
fennec::destruct(&root);
|
fennec::destruct(&root);
|
||||||
}
|
}
|
||||||
} trick = { .root = { KeyT(fennec::forward<ArgsT>(args)...), 0 } };
|
} trick = {
|
||||||
|
.root = { KeyT(fennec::forward<ArgsT>(args)...), 0 }
|
||||||
|
};
|
||||||
return _set.find(trick.val);
|
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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||||
#define FENNEC_CONTAINERS_OBJECT_POOL_H
|
#define FENNEC_CONTAINERS_OBJECT_POOL_H
|
||||||
|
|
||||||
#include <fennec/containers/dynarray.h>
|
#include <fennec/containers/dynarray.h>
|
||||||
#include <fennec/containers/list.h>
|
#include <fennec/containers/list.h>
|
||||||
#include <fennec/containers/optional.h>
|
#include <fennec/containers/optional.h>
|
||||||
@@ -27,82 +40,485 @@ namespace fennec
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Struct which holds a pool of objects associated with ids
|
/// \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 TypeT The value type
|
||||||
/// \tparam AllocT The allocator type
|
/// \tparam AllocT The allocator type
|
||||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||||
struct object_pool {
|
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()
|
constexpr object_pool()
|
||||||
: _size(0) {
|
: _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
|
/// \returns The number of active objects in the pool
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t size() const {
|
constexpr size_t size() const {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns The capacity of the underlying allocation
|
/// \returns The capacity of the underlying allocation
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t capacity() const {
|
constexpr size_t capacity() const {
|
||||||
return _table.capacity();
|
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;
|
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) {
|
constexpr value_t& operator[](size_t i) {
|
||||||
assert(i < capacity(), "Index out of Bounds!");
|
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];
|
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 {
|
constexpr const value_t& operator[](size_t i) const {
|
||||||
assert(i < capacity(), "Index out of Bounds!");
|
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];
|
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) {
|
constexpr size_t insert(value_t&& x) {
|
||||||
return this->_insert(fennec::forward<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) {
|
constexpr size_t insert(const value_t& x) {
|
||||||
return this->_insert(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>
|
template<typename...ArgsT>
|
||||||
constexpr size_t emplace(ArgsT&&...args) {
|
constexpr size_t emplace(ArgsT&&...args) {
|
||||||
return this->_insert(fennec::forward<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) {
|
constexpr void erase(size_t i) {
|
||||||
_table[i] = nullopt;
|
_table[i] = nullopt;
|
||||||
_freed.push_back(i);
|
_freed.push_back(i);
|
||||||
--_size;
|
--_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr size_t next_id() const {
|
///
|
||||||
size_t next = _size;
|
/// \brief Clear the object pool
|
||||||
if (not _freed.empty()) {
|
///
|
||||||
next = _freed.front();
|
/// \par Complexity
|
||||||
}
|
/// \f$O(N)\f$
|
||||||
return next;
|
///
|
||||||
|
constexpr void clear() {
|
||||||
|
_table.clear();
|
||||||
|
_freed.clear();
|
||||||
|
_size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
|
// 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:
|
||||||
|
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:
|
private:
|
||||||
dynarray<elem_t, AllocT> _table;
|
table_t _table;
|
||||||
list<size_t> _freed;
|
freed_t _freed;
|
||||||
size_t _size;
|
size_t _size;
|
||||||
|
|
||||||
size_t _next_free() {
|
size_t _next_free() {
|
||||||
size_t next = _size;
|
size_t next = _size;
|
||||||
if (not _freed.empty()) {
|
if (not _freed.is_empty()) {
|
||||||
next = _freed.front();
|
next = _freed.front();
|
||||||
_freed.pop_front();
|
_freed.pop_front();
|
||||||
}
|
}
|
||||||
@@ -114,7 +530,7 @@ private:
|
|||||||
size_t _insert(ArgsT&&...args) {
|
size_t _insert(ArgsT&&...args) {
|
||||||
size_t i = _next_free();
|
size_t i = _next_free();
|
||||||
if (i >= _table.size()) {
|
if (i >= _table.size()) {
|
||||||
_table.emplace_back();
|
_table.resize(fennec::max(_table.size() * 2, size_t(8)));
|
||||||
}
|
}
|
||||||
_table[i].emplace(fennec::forward<ArgsT>(args)...);
|
_table[i].emplace(fennec::forward<ArgsT>(args)...);
|
||||||
return i;
|
return i;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_OPTIONAL_H
|
||||||
#define FENNEC_CONTAINERS_OPTIONAL_H
|
#define FENNEC_CONTAINERS_OPTIONAL_H
|
||||||
|
|
||||||
@@ -26,30 +38,65 @@
|
|||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
///
|
||||||
|
/// \brief struct to represent a \f$null\f$ `fennec::optional`
|
||||||
struct nullopt_t {};
|
struct nullopt_t {};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief value representing a \f$null\f$ `fennec::optional`
|
||||||
constexpr nullopt_t nullopt_v = {};
|
constexpr nullopt_t nullopt_v = {};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief alias for representing a \f$null\f$ `fennec::optional`
|
||||||
#define nullopt nullopt_v
|
#define nullopt nullopt_v
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Structure to hold an optional value.
|
/// \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
|
/// \tparam T
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct optional {
|
struct optional {
|
||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
public:
|
public:
|
||||||
using reference_t = T&;
|
|
||||||
using pointer_t = T*;
|
/// \name Definitions
|
||||||
using const_reference_t = T&;
|
/// @{
|
||||||
using const_pointer_t = const T*;
|
|
||||||
|
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 ========================================================================================================
|
// Constructors ========================================================================================================
|
||||||
|
|
||||||
|
/// \name Constructors & Destructor
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Default Constructor
|
/// \brief Default Constructor
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr optional()
|
constexpr optional()
|
||||||
: _root(0)
|
: _root(0)
|
||||||
, _set(false) {
|
, _set(false) {
|
||||||
@@ -57,6 +104,10 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Default Constructor
|
/// \brief Default Constructor
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr optional(nullopt_t)
|
constexpr optional(nullopt_t)
|
||||||
: _root(0)
|
: _root(0)
|
||||||
, _set(false) {
|
, _set(false) {
|
||||||
@@ -65,6 +116,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Type Copy Constructor
|
/// \brief Type Copy Constructor
|
||||||
/// \param val the value to initialize the underlying object with
|
/// \param val the value to initialize the underlying object with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr optional(const T& val)
|
constexpr optional(const T& val)
|
||||||
: _val(val)
|
: _val(val)
|
||||||
, _set(true) {
|
, _set(true) {
|
||||||
@@ -73,6 +128,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Type Move Constructor
|
/// \brief Type Move Constructor
|
||||||
/// \param val the value to initialize the underlying object with
|
/// \param val the value to initialize the underlying object with
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr optional(T&& val)
|
constexpr optional(T&& val)
|
||||||
: _val(fennec::forward<T>(val))
|
: _val(fennec::forward<T>(val))
|
||||||
, _set(true) {
|
, _set(true) {
|
||||||
@@ -81,6 +140,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Copy Constructor
|
/// \brief Copy Constructor
|
||||||
/// \param opt the optional to copy
|
/// \param opt the optional to copy
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr optional(const optional& opt) requires is_copy_assignable_v<T>
|
constexpr optional(const optional& opt) requires is_copy_assignable_v<T>
|
||||||
: optional() {
|
: optional() {
|
||||||
_set = opt._set;
|
_set = opt._set;
|
||||||
@@ -92,6 +155,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Move Constructor
|
/// \brief Move Constructor
|
||||||
/// \param opt the optional to move
|
/// \param opt the optional to move
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr optional(optional&& opt) noexcept requires is_move_assignable_v<T>
|
constexpr optional(optional&& opt) noexcept requires is_move_assignable_v<T>
|
||||||
: optional() {
|
: optional() {
|
||||||
_set = opt._set;
|
_set = opt._set;
|
||||||
@@ -101,12 +168,26 @@ public:
|
|||||||
opt = nullopt;
|
opt = nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Emplace Constructor
|
||||||
|
/// \tparam ArgsT The argument types
|
||||||
|
/// \param args The argument values
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr optional(ArgsT&&...args)
|
constexpr optional(ArgsT&&...args)
|
||||||
: _val(fennec::forward<ArgsT>(args)...)
|
: _val(fennec::forward<ArgsT>(args)...)
|
||||||
, _set(true) {
|
, _set(true) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief destructor
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr ~optional() {
|
constexpr ~optional() {
|
||||||
if constexpr(is_fundamental_v<T>) {
|
if constexpr(is_fundamental_v<T>) {
|
||||||
return;
|
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 ================================================================================================
|
// 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) {
|
constexpr optional& operator=(nullopt_t) {
|
||||||
if constexpr(not is_fundamental_v<T>) {
|
if constexpr(not is_fundamental_v<T>) {
|
||||||
if (_set) {
|
if (_set) {
|
||||||
@@ -136,6 +255,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Type Copy Assignment
|
/// \brief Type Copy Assignment
|
||||||
/// \param val The value to set with
|
/// \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> {
|
constexpr optional& operator=(const T& val) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||||
if (_set) {
|
if (_set) {
|
||||||
_val = val;
|
_val = val;
|
||||||
@@ -149,6 +273,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Type Move Assignment
|
/// \brief Type Move Assignment
|
||||||
/// \param val The value to set with
|
/// \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> {
|
constexpr optional& operator=(T&& val) requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||||
if (_set) {
|
if (_set) {
|
||||||
_val = fennec::forward<T>(val);
|
_val = fennec::forward<T>(val);
|
||||||
@@ -162,6 +291,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Copy Assignment
|
/// \brief Copy Assignment
|
||||||
/// \param opt The optional to copy
|
/// \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> {
|
constexpr optional& operator=(const optional& opt) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||||
if (_set != opt._set) {
|
if (_set != opt._set) {
|
||||||
_set = opt._set;
|
_set = opt._set;
|
||||||
@@ -180,6 +314,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Move Assignment
|
/// \brief Move Assignment
|
||||||
/// \param opt The optional to move
|
/// \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> {
|
constexpr optional& operator=(optional&& opt) noexcept requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||||
if (_set != opt._set) {
|
if (_set != opt._set) {
|
||||||
_set = opt._set;
|
_set = opt._set;
|
||||||
@@ -195,9 +334,133 @@ public:
|
|||||||
return *this;
|
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
|
/// \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>
|
template<typename...ArgsT>
|
||||||
constexpr T& emplace(ArgsT&&...args) {
|
constexpr T& emplace(ArgsT&&...args) {
|
||||||
if (_set) {
|
if (_set) {
|
||||||
@@ -211,62 +474,15 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Reset the Optional
|
/// \brief Reset the Optional
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
void reset() {
|
void reset() {
|
||||||
this->operator=(nullopt);
|
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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_PAIR_H
|
||||||
#define FENNEC_CONTAINERS_PAIR_H
|
#define FENNEC_CONTAINERS_PAIR_H
|
||||||
|
|
||||||
@@ -30,26 +42,70 @@ namespace fennec
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Struct for holding a pair of values
|
/// \brief Struct for holding a pair of values
|
||||||
/// \tparam T0 The type of the first value
|
/// \details
|
||||||
/// \tparam T1 The type of the second value
|
/// | Property | Value |
|
||||||
template<typename T0, typename T1>
|
/// |:-----------:|:----------:|
|
||||||
|
/// | 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 {
|
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 ========================================================================================================
|
// Constructors ========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Constructors & Destructor
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Default Constructor, invokes default constructor for both elements
|
/// \brief Default Constructor, invokes default constructor for both elements
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr pair() = default;
|
constexpr pair() = default;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Destructor, invokes destructor for both elements
|
/// \brief Destructor, invokes destructor for both elements
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr ~pair() = default;
|
constexpr ~pair() = default;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Pair Copy Constructor
|
/// \brief Pair Copy Constructor
|
||||||
/// \param x Value to copy for the first element
|
/// \param x Value to copy for the first element
|
||||||
/// \param y 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)
|
: first(x)
|
||||||
, second(y) {
|
, second(y) {
|
||||||
}
|
}
|
||||||
@@ -58,15 +114,23 @@ struct pair {
|
|||||||
/// \brief Pair Move Constructor
|
/// \brief Pair Move Constructor
|
||||||
/// \param x Value to move for the first element
|
/// \param x Value to move for the first element
|
||||||
/// \param y 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))
|
/// \par Complexity
|
||||||
, second(fennec::forward<T1>(y)) {
|
/// \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
|
/// \brief Pair Implicit Constructor
|
||||||
/// \param arg1 Value to initialize the first element
|
/// \param arg1 Value to initialize the first element
|
||||||
/// \param arg2 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>
|
template<typename Arg1T, typename Arg2T>
|
||||||
constexpr pair(Arg1T&& arg1, Arg2T&& arg2)
|
constexpr pair(Arg1T&& arg1, Arg2T&& arg2)
|
||||||
: first(fennec::forward<Arg1T>(arg1))
|
: first(fennec::forward<Arg1T>(arg1))
|
||||||
@@ -75,27 +139,73 @@ struct pair {
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Copy Constructor, copies both elements
|
/// \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
|
/// \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
|
/// \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
|
/// \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 ==========================================================================================================
|
// Comparison ==========================================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \name Comparison
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Equality Operator
|
/// \brief Equality Operator
|
||||||
/// \param p Pair to compare with
|
/// \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 {
|
constexpr bool operator==(const pair& p) const {
|
||||||
return first == p.first and second == p.second;
|
return first == p.first and second == p.second;
|
||||||
}
|
}
|
||||||
@@ -103,7 +213,11 @@ struct pair {
|
|||||||
///
|
///
|
||||||
/// \brief Inequality Operator
|
/// \brief Inequality Operator
|
||||||
/// \param p Pair to compare with
|
/// \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 {
|
constexpr bool operator!=(const pair& p) const {
|
||||||
return first != p.first or second != p.second;
|
return first != p.first or second != p.second;
|
||||||
}
|
}
|
||||||
@@ -111,8 +225,12 @@ struct pair {
|
|||||||
///
|
///
|
||||||
/// \brief Less Than Operator
|
/// \brief Less Than Operator
|
||||||
/// \param p Pair to compare with
|
/// \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
|
/// equal and the second element is less
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr bool operator<(const pair& p) const {
|
constexpr bool operator<(const pair& p) const {
|
||||||
return first < p.first or (first == p.first and second < p.second);
|
return first < p.first or (first == p.first and second < p.second);
|
||||||
}
|
}
|
||||||
@@ -120,8 +238,12 @@ struct pair {
|
|||||||
///
|
///
|
||||||
/// \brief Less Equal Operator
|
/// \brief Less Equal Operator
|
||||||
/// \param p Pair to compare with
|
/// \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
|
/// equal and the second element is less or equal
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr bool operator<=(const pair& p) const {
|
constexpr bool operator<=(const pair& p) const {
|
||||||
return first < p.first or (first == p.first and second <= p.second);
|
return first < p.first or (first == p.first and second <= p.second);
|
||||||
}
|
}
|
||||||
@@ -129,8 +251,12 @@ struct pair {
|
|||||||
///
|
///
|
||||||
/// \brief Greater Than Operator
|
/// \brief Greater Than Operator
|
||||||
/// \param p Pair to compare with
|
/// \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
|
/// equal and the second element is greater
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr bool operator>(const pair& p) const {
|
constexpr bool operator>(const pair& p) const {
|
||||||
return first > p.first or (first == p.first and second > p.second);
|
return first > p.first or (first == p.first and second > p.second);
|
||||||
}
|
}
|
||||||
@@ -138,24 +264,33 @@ struct pair {
|
|||||||
///
|
///
|
||||||
/// \brief Greater Equal Operator
|
/// \brief Greater Equal Operator
|
||||||
/// \param p Pair to compare with
|
/// \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
|
/// equal and the second element is greater or equal
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr bool operator>=(const pair& p) const {
|
constexpr bool operator>=(const pair& p) const {
|
||||||
return first > p.first or (first == p.first and second >= p.second);
|
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> {
|
/// \brief C++ 11 Hash Specification for `fennec::pair<TypeT0, TypeT1>`
|
||||||
constexpr size_t operator()(const pair<T0, T1>& p) const {
|
/// \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
|
return fennec::pair_hash( // pair the hashes of both elements
|
||||||
hash<T0>::operator()(p.first),
|
hash<TypeT0>::operator()(p.first),
|
||||||
hash<T1>::operator()(p.second)
|
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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_RDTREE_H
|
||||||
#define FENNEC_CONTAINERS_RDTREE_H
|
#define FENNEC_CONTAINERS_RDTREE_H
|
||||||
|
|
||||||
#include <fennec/containers/list.h>
|
#include <fennec/containers/list.h>
|
||||||
#include <fennec/containers/optional.h>
|
#include <fennec/containers/optional.h>
|
||||||
#include <fennec/containers/traversal.h>
|
#include <fennec/containers/traversal.h>
|
||||||
|
#include <fennec/containers/pair.h>
|
||||||
|
|
||||||
#include <fennec/memory/allocator.h>
|
#include <fennec/memory/allocator.h>
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
@@ -29,26 +43,62 @@ namespace fennec
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Rooted-Directed Tree
|
/// \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 TypeT Data type
|
||||||
/// \tparam AllocT Allocator Type
|
/// \tparam AllocT Allocator Type
|
||||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||||
struct rdtree {
|
struct rdtree {
|
||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
protected:
|
private:
|
||||||
struct node;
|
struct node;
|
||||||
|
|
||||||
public:
|
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 {
|
struct node {
|
||||||
optional<TypeT> value;
|
value_t value;
|
||||||
size_t parent, child, prev, next;
|
size_t parent, child, prev, next;
|
||||||
size_t depth, num_children;
|
size_t depth, num_children;
|
||||||
|
|
||||||
|
constexpr node()
|
||||||
|
: value()
|
||||||
|
, parent(npos), child(npos)
|
||||||
|
, prev(npos), next(npos)
|
||||||
|
, depth(0), num_children(0) {
|
||||||
|
}
|
||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
|
constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args)
|
||||||
@@ -62,74 +112,156 @@ protected:
|
|||||||
child = npos;
|
child = npos;
|
||||||
prev = npos;
|
prev = npos;
|
||||||
next = npos;
|
next = npos;
|
||||||
depth = npos;
|
depth = 0;
|
||||||
num_children = 0;
|
num_children = 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using table_t = allocation<node, alloc_t>;
|
||||||
|
using freed_t = list<size_t>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
// Constructors ========================================================================================================
|
// 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>
|
template<typename...ArgsT>
|
||||||
explicit constexpr rdtree(ArgsT&&...args)
|
explicit constexpr rdtree(ArgsT&&...args)
|
||||||
: _table(), _freed(), _size(1) {
|
: _table(), _freed(), _size(1) {
|
||||||
_table.callocate(8);
|
_table.reallocate(8);
|
||||||
fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward<ArgsT>(args)...);
|
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)
|
constexpr rdtree(const rdtree& tree)
|
||||||
: _table(tree._table), _freed(tree._freed), _size(tree._size) {
|
: _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
|
constexpr rdtree(rdtree&& tree) noexcept
|
||||||
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
|
: _table(fennec::move(tree._table)), _freed(fennec::move(tree._freed)), _size(tree._size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Assignment ==========================================================================================================
|
// 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) {
|
constexpr rdtree& operator=(const rdtree& rhs) {
|
||||||
|
// TODO: properly invoke copy constructor for all elements
|
||||||
for (value_t* it : this->_table) {
|
for (value_t* it : this->_table) {
|
||||||
fennec::destruct(it);
|
fennec::destruct(it);
|
||||||
}
|
}
|
||||||
_table = rhs._table;
|
_table = rhs._table;
|
||||||
_freed = rhs._freed;
|
_freed = rhs._freed;
|
||||||
_size = rhs._size;
|
_size = rhs._size;
|
||||||
return *this;
|
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 {
|
constexpr rdtree& operator=(rdtree&& rhs) noexcept {
|
||||||
for (value_t* it : _table) {
|
for (value_t* it : _table) {
|
||||||
fennec::destruct(it);
|
fennec::destruct(it);
|
||||||
}
|
}
|
||||||
_table = fennec::move(rhs._table);
|
_table = fennec::move(rhs._table);
|
||||||
_freed = fennec::move(rhs._freed);
|
_freed = fennec::move(rhs._freed);
|
||||||
_size = rhs._size;
|
_size = rhs._size;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
// Properties ==========================================================================================================
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
|
/// \name Properties
|
||||||
|
/// @{
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns The number of nodes in the tree
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t size() const {
|
constexpr size_t size() const {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \returns The capacity of the underlying allocation
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t capacity() const {
|
constexpr size_t capacity() const {
|
||||||
return _table.capacity();
|
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;
|
return _size == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Access ==============================================================================================================
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
|
/// \name Access
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \param i The id of the node to check
|
/// \param i The id of the node to check
|
||||||
/// \returns The id of the parent node
|
/// \returns The id of the parent node
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t parent(size_t i) const {
|
constexpr size_t parent(size_t i) const {
|
||||||
if (i >= _table.capacity()) return npos;
|
if (i >= _table.capacity()) return npos;
|
||||||
return i == npos ? npos : _table[i].parent;
|
return i == npos ? npos : _table[i].parent;
|
||||||
@@ -137,9 +269,14 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \param i The id of the node to check
|
/// \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
|
/// \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 {
|
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;
|
size_t c = i == npos ? npos : _table[i].child;
|
||||||
if (n != 0)
|
if (n != 0)
|
||||||
return next(c, n == npos ? npos : n - 1);
|
return next(c, n == npos ? npos : n - 1);
|
||||||
@@ -148,9 +285,14 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \param i The id of the node to check
|
/// \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
|
/// \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 {
|
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) {
|
if (i == npos) {
|
||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
@@ -172,7 +314,12 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \param i The id of the node to check
|
/// \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
|
/// \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 {
|
constexpr size_t prev(size_t i, size_t n = 0) const {
|
||||||
if (i >= _table.capacity()) return npos;
|
if (i >= _table.capacity()) return npos;
|
||||||
if (i == npos) {
|
if (i == npos) {
|
||||||
@@ -196,7 +343,11 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \param i the node to start at
|
/// \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 {
|
constexpr size_t left_most(size_t i) const {
|
||||||
if (i >= _table.capacity()) return npos;
|
if (i >= _table.capacity()) return npos;
|
||||||
size_t n = i;
|
size_t n = i;
|
||||||
@@ -213,7 +364,11 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \param i the node to start at
|
/// \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 {
|
constexpr size_t right_most(size_t i) const {
|
||||||
if (i >= _table.capacity()) return npos;
|
if (i >= _table.capacity()) return npos;
|
||||||
if ((i = child(i)) == npos) {
|
if ((i = child(i)) == npos) {
|
||||||
@@ -234,6 +389,10 @@ public:
|
|||||||
///
|
///
|
||||||
/// \param i The id of the node to check
|
/// \param i The id of the node to check
|
||||||
/// \returns The depth of the node
|
/// \returns The depth of the node
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t depth(size_t i) const {
|
constexpr size_t depth(size_t i) const {
|
||||||
if (i >= _table.capacity()) return npos;
|
if (i >= _table.capacity()) return npos;
|
||||||
return i == npos ? npos : _table[i].depth;
|
return i == npos ? npos : _table[i].depth;
|
||||||
@@ -242,16 +401,24 @@ public:
|
|||||||
///
|
///
|
||||||
/// \param i The id of the node to check
|
/// \param i The id of the node to check
|
||||||
/// \returns The number of children the node has
|
/// \returns The number of children the node has
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t num_children(size_t i) const {
|
constexpr size_t num_children(size_t i) const {
|
||||||
if (i >= _table.capacity()) return 0;
|
if (i >= _table.capacity()) return 0;
|
||||||
return i == npos ? 0 : _table[i].num_children;
|
return i == npos ? 0 : _table[i].num_children;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns The next node id were `insert` or `emplace` to be called
|
/// \returns The next node id were \f$insert\f$ or \f$emplace\f$ to be called
|
||||||
constexpr size_t next_free() const {
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
|
constexpr size_t next_id() const {
|
||||||
size_t i = _size;
|
size_t i = _size;
|
||||||
if (not _freed.empty()) {
|
if (not _freed.is_empty()) {
|
||||||
i = _freed.front();
|
i = _freed.front();
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
@@ -260,56 +427,104 @@ public:
|
|||||||
///
|
///
|
||||||
/// \param i The id of the node to access
|
/// \param i The id of the node to access
|
||||||
/// \returns A reference to the value of the node wrapped in an optional
|
/// \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;
|
/// \par Complexity
|
||||||
if (it) {
|
/// \f$O(1)\f$
|
||||||
return &*_table[i].value;
|
///
|
||||||
} else {
|
constexpr value_t& operator[](size_t i) {
|
||||||
return nullptr;
|
return _table[i].value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \param i The id of the node to access
|
/// \param i The id of the node to access
|
||||||
/// \returns A const-qualified reference to the value of the node wrapped in an optional
|
/// \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;
|
/// \par Complexity
|
||||||
if (it) {
|
/// \f$O(1)\f$
|
||||||
return &*_table[i].value;
|
///
|
||||||
} else {
|
constexpr const value_t& operator[](size_t i) const {
|
||||||
return nullptr;
|
return _table[i].value;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
// Insertion & Deletion ================================================================================================
|
|
||||||
|
// Modifiers ===========================================================================================================
|
||||||
|
|
||||||
|
/// \name Modifiers
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Insertion, creates a node in the tree with parent `parent`
|
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||||
/// \param parent the parent node, if `npos` sets the value of the root node
|
/// \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 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
|
/// \param val the value to insert
|
||||||
/// \returns the index of the created node
|
/// \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) {
|
constexpr size_t insert(size_t parent, size_t next, const value_t& val) {
|
||||||
return this->_insert(parent, next, val);
|
return this->_insert(parent, next, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Insertion, creates a node in the tree with parent `parent`
|
/// \brief Insertion, creates a node in the tree with parent \f$parent\f$
|
||||||
/// \param parent the parent node, if `npos` sets the value of the root node
|
/// \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 next the next node, as an index relative to the parent
|
||||||
/// \param val the value to insert
|
/// \param val the value to insert
|
||||||
/// \returns the index of the created node
|
/// \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) {
|
constexpr size_t insert(size_t parent, size_t next, value_t&& val) {
|
||||||
return this->_insert(parent, next, fennec::forward<value_t>(val));
|
return this->_insert(parent, next, fennec::forward<value_t>(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Insertion, creates a node in the tree with parent `parent`
|
/// \brief tree insertion, copies the contents of tree into self with the root at the specified node location
|
||||||
/// \param parent the parent node, if `npos` sets the value of the root node
|
/// \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 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
|
/// \param args the args to construct the value to insert
|
||||||
/// \returns the index of the created node
|
/// \returns the index of the created node
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
|
constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) {
|
||||||
return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
|
return this->_insert(parent, next, fennec::forward<ArgsT>(args)...);
|
||||||
@@ -319,6 +534,10 @@ public:
|
|||||||
/// \brief Swap two nodes
|
/// \brief Swap two nodes
|
||||||
/// \param i0 The id of the first node
|
/// \param i0 The id of the first node
|
||||||
/// \param i1 The id of the second node
|
/// \param i1 The id of the second node
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void swap(size_t i0, size_t i1) {
|
constexpr void swap(size_t i0, size_t i1) {
|
||||||
assertf(i0 != root and i1 != root, "Cannot Swap With Root");
|
assertf(i0 != root and i1 != root, "Cannot Swap With Root");
|
||||||
|
|
||||||
@@ -344,19 +563,146 @@ public:
|
|||||||
_erase(i);
|
_erase(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Traversal ===========================================================================================================
|
// Traversal ===========================================================================================================
|
||||||
|
|
||||||
struct pre_order {
|
/// \name Traversal
|
||||||
list<size_t> visit;
|
/// @{
|
||||||
size_t head;
|
|
||||||
|
|
||||||
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;
|
head = start;
|
||||||
return 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) {
|
if (node == npos) {
|
||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
@@ -372,7 +718,7 @@ public:
|
|||||||
visit.push_front(chd);
|
visit.push_front(chd);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not visit.empty()) {
|
if (not visit.is_empty()) {
|
||||||
node = visit.front();
|
node = visit.front();
|
||||||
visit.pop_front();
|
visit.pop_front();
|
||||||
} else {
|
} else {
|
||||||
@@ -381,18 +727,32 @@ public:
|
|||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
struct in_order {
|
private:
|
||||||
list<size_t> visit;
|
list<size_t> visit;
|
||||||
size_t head;
|
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;
|
head = start;
|
||||||
return tree.left_most(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) {
|
if (node == npos) {
|
||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
@@ -410,7 +770,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not visit.empty()) {
|
if (not visit.is_empty()) {
|
||||||
node = visit.front();
|
node = visit.front();
|
||||||
visit.pop_front();
|
visit.pop_front();
|
||||||
} else {
|
} else {
|
||||||
@@ -419,18 +779,32 @@ public:
|
|||||||
|
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
struct post_order {
|
private:
|
||||||
list<size_t> visit;
|
list<size_t> visit;
|
||||||
size_t head;
|
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;
|
head = start;
|
||||||
return tree.left_most(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) {
|
if (node == npos) {
|
||||||
return npos;
|
return npos;
|
||||||
}
|
}
|
||||||
@@ -446,7 +820,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not visit.empty()) {
|
if (not visit.is_empty()) {
|
||||||
node = visit.front();
|
node = visit.front();
|
||||||
visit.pop_front();
|
visit.pop_front();
|
||||||
} else {
|
} else {
|
||||||
@@ -455,34 +829,32 @@ public:
|
|||||||
|
|
||||||
return node;
|
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:
|
// Private Member Variables ============================================================================================
|
||||||
allocation<node, alloc_t> _table;
|
private:
|
||||||
list<size_t> _freed;
|
table_t _table;
|
||||||
size_t _size;
|
freed_t _freed;
|
||||||
|
size_t _size;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Helpers =====================================================================================================
|
||||||
|
private:
|
||||||
|
|
||||||
void _expand() {
|
void _expand() {
|
||||||
_table.creallocate(_table.capacity() * 2);
|
_table.reallocate(_table.capacity() * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t _next_free() {
|
size_t _next_free() {
|
||||||
size_t next = _size;
|
size_t next = _size;
|
||||||
if (not _freed.empty()) {
|
if (not _freed.is_empty()) {
|
||||||
next = _freed.front();
|
next = _freed.front();
|
||||||
_freed.pop_front();
|
_freed.pop_front();
|
||||||
}
|
}
|
||||||
@@ -536,7 +908,7 @@ protected:
|
|||||||
constexpr void _erase(size_t i) {
|
constexpr void _erase(size_t i) {
|
||||||
list<size_t> queue;
|
list<size_t> queue;
|
||||||
queue.push_back(child(i));
|
queue.push_back(child(i));
|
||||||
while (not queue.empty()) {
|
while (not queue.is_empty()) {
|
||||||
size_t n = queue.front(); queue.pop_front();
|
size_t n = queue.front(); queue.pop_front();
|
||||||
if (n == npos) continue;
|
if (n == npos) continue;
|
||||||
queue.push_back(next(n));
|
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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_SET_H
|
||||||
#define FENNEC_CONTAINERS_SET_H
|
#define FENNEC_CONTAINERS_SET_H
|
||||||
|
|
||||||
// https://programming.guide/robin-hood-hashing.html
|
// https://programming.guide/robin-hood-hashing.html
|
||||||
|
|
||||||
#include <fennec/containers/multiset.h>
|
|
||||||
#include <fennec/containers/optional.h>
|
#include <fennec/containers/optional.h>
|
||||||
#include <fennec/containers/set.h>
|
#include <fennec/containers/set.h>
|
||||||
#include <fennec/lang/compare.h>
|
#include <fennec/lang/compare.h>
|
||||||
@@ -38,45 +49,73 @@ namespace fennec
|
|||||||
/// \details
|
/// \details
|
||||||
/// This data-structure behaves like a set, but does not use pointers, instead storing the table in-array
|
/// This data-structure behaves like a set, but does not use pointers, instead storing the table in-array
|
||||||
///
|
///
|
||||||
/// | Property | Value |
|
/// | Property | Value |
|
||||||
/// |:----------|:----------:|
|
/// |:-----------:|:----------:|
|
||||||
/// | stable | \emoji x |
|
/// | stable | ⛔ |
|
||||||
/// | access | \f$O(1)\f$ |
|
/// | dynamic | ✅ |
|
||||||
/// | insertion | \f$O(1)\f$ |
|
/// | homogeneous | ✅ |
|
||||||
/// | deletion | \f$O(1)\f$ |
|
/// | distinct | ✅ |
|
||||||
/// | space | \f$O(1)\f$ |
|
/// | 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$ |
|
||||||
///
|
///
|
||||||
/// \tparam TypeT The type to contain
|
/// \tparam TypeT The type to contain
|
||||||
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
|
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
|
||||||
struct set {
|
struct set {
|
||||||
|
|
||||||
// Definitions =========================================================================================================
|
// Definitions =========================================================================================================
|
||||||
|
private:
|
||||||
|
struct node;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
|
||||||
using hash_t = Hash;
|
/// \name Definitions
|
||||||
using equal_t = Equals;
|
/// @{
|
||||||
using elem_t = TypeT;
|
|
||||||
|
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;
|
class iterator;
|
||||||
static constexpr size_t npos = -1;
|
|
||||||
static constexpr double default_load = 0.8;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct node {
|
static constexpr size_t npos = -1;
|
||||||
optional<elem_t> value;
|
|
||||||
int psl;
|
using table_t = dynarray<node, alloc_t>;
|
||||||
|
|
||||||
constexpr node() = default;
|
|
||||||
constexpr ~node() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Constructors ========================================================================================================
|
// Constructors ========================================================================================================
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// \name Constructors & Destructor
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Default Constructor, initializes empty set
|
/// \brief Default Constructor, initializes empty set
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr set()
|
constexpr set()
|
||||||
: _alloc()
|
: _table()
|
||||||
, _hash()
|
, _hash()
|
||||||
, _size(0)
|
, _size(0)
|
||||||
, _sumpsl(0)
|
, _sumpsl(0)
|
||||||
@@ -85,18 +124,13 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Hash Copy Constructor, initializes empty set with a hash
|
/// \brief Hash Copy Constructor, initializes empty set with a hash
|
||||||
constexpr set(const hash_t& hash)
|
/// \param hash the hash object
|
||||||
: _alloc()
|
|
||||||
, _hash(hash)
|
|
||||||
, _size(0)
|
|
||||||
, _sumpsl(0)
|
|
||||||
, _load(default_load) {
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Hash Move Constructor, initializes empty set with a hash
|
/// \par Complexity
|
||||||
constexpr set(hash_t&& hash) noexcept
|
/// \f$O(1)\f$
|
||||||
: _alloc()
|
///
|
||||||
|
constexpr set(const hash_t& hash)
|
||||||
|
: _table()
|
||||||
, _hash(hash)
|
, _hash(hash)
|
||||||
, _size(0)
|
, _size(0)
|
||||||
, _sumpsl(0)
|
, _sumpsl(0)
|
||||||
@@ -105,18 +139,13 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
|
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
|
||||||
constexpr set(const alloc_t& alloc)
|
/// \param alloc the allocator object
|
||||||
: _alloc(alloc)
|
|
||||||
, _hash()
|
|
||||||
, _size(0)
|
|
||||||
, _sumpsl(0)
|
|
||||||
, _load(default_load) {
|
|
||||||
}
|
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Alloc Move Constructor, initializes empty set with an allocator
|
/// \par Complexity
|
||||||
constexpr set(alloc_t&& alloc) noexcept
|
/// \f$O(N)\f$
|
||||||
: _alloc(alloc)
|
///
|
||||||
|
constexpr set(const alloc_t& alloc)
|
||||||
|
: _table(alloc)
|
||||||
, _hash()
|
, _hash()
|
||||||
, _size(0)
|
, _size(0)
|
||||||
, _sumpsl(0)
|
, _sumpsl(0)
|
||||||
@@ -125,38 +154,14 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
|
/// \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)
|
constexpr set(const hash_t& hash, const alloc_t& alloc)
|
||||||
: _alloc(alloc)
|
: _table(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)
|
, _hash(hash)
|
||||||
, _size(0)
|
, _size(0)
|
||||||
, _sumpsl(0)
|
, _sumpsl(0)
|
||||||
@@ -166,8 +171,12 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Set Copy Constructor
|
/// \brief Set Copy Constructor
|
||||||
/// \param set Set to copy
|
/// \param set Set to copy
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr set(const set& set)
|
constexpr set(const set& set)
|
||||||
: _alloc(set._alloc)
|
: _table(set._table)
|
||||||
, _hash(set._hash)
|
, _hash(set._hash)
|
||||||
, _size(set._size)
|
, _size(set._size)
|
||||||
, _sumpsl(set._sumpsl)
|
, _sumpsl(set._sumpsl)
|
||||||
@@ -177,8 +186,12 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Set Move Constructor
|
/// \brief Set Move Constructor
|
||||||
/// \param set Set to move
|
/// \param set Set to move
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr set(set&& set) noexcept
|
constexpr set(set&& set) noexcept
|
||||||
: _alloc(fennec::move(set._alloc))
|
: _table(fennec::move(set._table))
|
||||||
, _hash(fennec::move(set._hash))
|
, _hash(fennec::move(set._hash))
|
||||||
, _size(fennec::move(set._size))
|
, _size(fennec::move(set._size))
|
||||||
, _sumpsl(set._sumpsl)
|
, _sumpsl(set._sumpsl)
|
||||||
@@ -187,34 +200,70 @@ public:
|
|||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Destructor, destructs all elements and releases the allocation
|
/// \brief Destructor, destructs all elements and releases the allocation
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(N)\f$
|
||||||
|
///
|
||||||
constexpr ~set() {
|
constexpr ~set() {
|
||||||
for (size_t i = 0; i < capacity(); ++i) {
|
for (size_t i = 0; i < capacity(); ++i) {
|
||||||
_alloc[i].value = nullopt;
|
_table[i].value = nullopt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @}
|
||||||
|
|
||||||
|
|
||||||
// Properties ==========================================================================================================
|
// Properties ==========================================================================================================
|
||||||
|
|
||||||
|
/// \name Properties
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns Size of the set in elements
|
/// \returns Size of the set in elements
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr size_t size() const {
|
constexpr size_t size() const {
|
||||||
return _size;
|
return _size;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \returns Capacity of the set in elements
|
/// \returns \f$true\f$ when the set is empty, \f$false\f$ otherwise
|
||||||
constexpr size_t capacity() const {
|
///
|
||||||
return _alloc.capacity();
|
/// \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 ==============================================================================================================
|
// Access ==============================================================================================================
|
||||||
|
|
||||||
|
/// \name Access
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Find an Element
|
/// \brief Find an Element
|
||||||
/// \param val Value to find
|
/// \param val Value to find
|
||||||
/// \returns An iterator at the location of the value
|
/// \returns An iterator at the location of the value
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr iterator find(const elem_t& val) const {
|
constexpr iterator find(const elem_t& val) const {
|
||||||
if (capacity() == 0) {
|
if (capacity() == 0) {
|
||||||
return end();
|
return end();
|
||||||
@@ -225,8 +274,8 @@ public:
|
|||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
|
|
||||||
// Check the first element;
|
// Check the first element;
|
||||||
if (_alloc[i].psl >= psl && _alloc[i].value) {
|
if (_table[i].psl >= psl && _table[i].value) {
|
||||||
if (*_alloc[i].value == val) {
|
if (_equal(*_table[i].value, val)) {
|
||||||
return iterator(this, i);
|
return iterator(this, i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -237,16 +286,16 @@ public:
|
|||||||
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
|
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
|
||||||
size_t i1 = (i + n) % capacity();
|
size_t i1 = (i + n) % capacity();
|
||||||
int p0 = psl - n, p1 = psl + n;
|
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 (c0 && _table[i0].value) {
|
||||||
if (*_alloc[i0].value == val) {
|
if (_equal(*_table[i0].value, val)) {
|
||||||
return iterator(this, i0);
|
return iterator(this, i0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c1 && _alloc[i1].value) {
|
if (c1 && _table[i1].value) {
|
||||||
if (*_alloc[i1].value == val) {
|
if (_equal(*_table[i1].value, val)) {
|
||||||
return iterator(this, i1);
|
return iterator(this, i1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +311,11 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Check if a set contains a value
|
/// \brief Check if a set contains a value
|
||||||
/// \param val Value to check
|
/// \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 {
|
constexpr bool contains(const elem_t& val) const {
|
||||||
return this->find(val) != end();
|
return this->find(val) != end();
|
||||||
}
|
}
|
||||||
@@ -270,73 +323,105 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Iterator Access
|
/// \brief Iterator Access
|
||||||
/// \param it Location to 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.
|
/// 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) {
|
constexpr elem_t* at(const iterator& it) {
|
||||||
if (it == end()) {
|
if (it == end()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (not _alloc[it._i].value) {
|
if (not _table[it._i].value) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return &*_alloc[it._i].value;
|
return &*_table[it._i].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Iterator Const Access
|
/// \brief Iterator Const Access
|
||||||
/// \param it Location to 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 {
|
constexpr const elem_t* at(const iterator& it) const {
|
||||||
if (not _alloc[it._i].value) return nullptr;
|
if (not _table[it._i].value) return nullptr;
|
||||||
return &*_alloc[it._i].value;
|
return &*_table[it._i].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insertion & Deletion ================================================================================================
|
/// @}
|
||||||
|
|
||||||
|
// Modifiers ===========================================================================================================
|
||||||
|
|
||||||
|
/// \name Modifiers
|
||||||
|
/// @{
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Move Insertion
|
/// \brief Move Insertion
|
||||||
/// \param val Value to insert
|
/// \param val Value to insert
|
||||||
|
/// \returns An iterator at the held value
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr iterator insert(elem_t&& val) {
|
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
|
/// \brief Copy Insertion
|
||||||
/// \param val Value to insert
|
/// \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) {
|
constexpr iterator insert(const elem_t& val) {
|
||||||
return fennec::move(this->_insert(val));
|
return this->_insert(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Emplace Insertion
|
/// \brief Emplace Insertion
|
||||||
/// \tparam ArgsT Argument types
|
/// \tparam ArgsT Argument types
|
||||||
/// \param args Arguments to construct with
|
/// \param args Arguments to construct with
|
||||||
|
/// \returns An iterator at the held value
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr iterator emplace(ArgsT&&...args) {
|
constexpr iterator emplace(ArgsT&&...args) {
|
||||||
return fennec::move(this->_insert(fennec::forward<ArgsT>(args)...));
|
return this->_insert(fennec::forward<ArgsT>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Element Erase
|
/// \brief Element Erase
|
||||||
/// \param it Location to erase
|
/// \param it Location to erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void erase(iterator it) {
|
constexpr void erase(iterator it) {
|
||||||
size_t i = it._i;
|
size_t i = it._i;
|
||||||
if (i >= capacity()) {
|
if (i >= capacity()) {
|
||||||
return;
|
return;
|
||||||
} // These are separated due to compilers being inconsistent
|
} // These are separated due to compilers being inconsistent
|
||||||
if (not _alloc[i].value) {
|
if (not _table[i].value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_alloc[i].value = nullopt;
|
_table[i].value = nullopt;
|
||||||
_sumpsl -= _alloc[i].psl;
|
_sumpsl -= _table[i].psl;
|
||||||
--_size;
|
--_size;
|
||||||
size_t p = i;
|
size_t p = i;
|
||||||
while (_alloc[i = (i + 1) % capacity()].value) {
|
while (_table[i = (i + 1) % capacity()].value) {
|
||||||
if (_alloc[i].psl == 0) break;
|
if (_table[i].psl == 0) break;
|
||||||
|
|
||||||
fennec::swap(_alloc[i - 1].value, _alloc[i].value);
|
fennec::swap(_table[p].value, _table[i].value);
|
||||||
--_alloc[p].psl, --_sumpsl;
|
--_table[p].psl, --_sumpsl;
|
||||||
p = i;
|
p = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -344,133 +429,207 @@ public:
|
|||||||
///
|
///
|
||||||
/// \brief Element Erase
|
/// \brief Element Erase
|
||||||
/// \param val Value to erase
|
/// \param val Value to erase
|
||||||
|
///
|
||||||
|
/// \par Complexity
|
||||||
|
/// \f$O(1)\f$
|
||||||
|
///
|
||||||
constexpr void erase(const elem_t& val) {
|
constexpr void erase(const elem_t& val) {
|
||||||
this->erase(this->find(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 ============================================================================================================
|
// 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 {
|
constexpr iterator begin() const {
|
||||||
iterator it(this, 0);
|
iterator it(this, 0);
|
||||||
if (not _alloc[it._i].value) {
|
while (not _table[it._i].value) {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
return 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 {
|
constexpr iterator end() const {
|
||||||
return iterator(this, npos);
|
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:
|
private:
|
||||||
constexpr void _expand() {
|
constexpr void _expand() {
|
||||||
set cpy; // Create a new set
|
set cpy; // Create a new set
|
||||||
cpy._alloc.callocate(
|
cpy._table.resize(
|
||||||
fennec::next_prime2(_alloc.capacity())
|
fennec::next_prime2(_table.capacity())
|
||||||
);
|
);
|
||||||
|
|
||||||
// rehash
|
// rehash
|
||||||
for (size_t i = 0; i < capacity(); ++i) {
|
for (size_t i = 0; i < capacity(); ++i) {
|
||||||
if (_alloc[i].value) {
|
if (_table[i].value) {
|
||||||
cpy.insert(fennec::move(*_alloc[i].value));
|
cpy.insert(fennec::move(*_table[i].value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Swap buffers
|
// Swap buffers
|
||||||
fennec::swap(_alloc, cpy._alloc);
|
fennec::swap(_table, cpy._table);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename...ArgsT>
|
template<typename...ArgsT>
|
||||||
constexpr iterator _insert(ArgsT&&...args) {
|
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();
|
_expand();
|
||||||
}
|
}
|
||||||
|
|
||||||
elem_t value(fennec::forward<ArgsT>(args)...);
|
elem_t value(fennec::forward<ArgsT>(args)...);
|
||||||
size_t i = _hash(value) % capacity(); // Initial search index
|
size_t i = _hash(value) % capacity(); // Initial search index
|
||||||
int psl = 0;
|
int psl = 0;
|
||||||
while (_alloc[i].value) { // Search for empty cell
|
while (_table[i].value) { // Search for empty cell
|
||||||
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
|
if (_equal(*_table[i].value, value)) { // Check to see if this element is already inserted
|
||||||
return iterator(this, i);
|
return iterator(this, i);
|
||||||
}
|
}
|
||||||
if (psl > _alloc[i].psl) { // When psl is higher, swap
|
if (psl > _table[i].psl) { // When psl is higher, swap
|
||||||
_sumpsl += psl - _alloc[i].psl;
|
_sumpsl += psl - _table[i].psl;
|
||||||
fennec::swap(_alloc[i].psl, psl);
|
fennec::swap(_table[i].psl, psl);
|
||||||
fennec::swap(*_alloc[i].value, value);
|
fennec::swap(*_table[i].value, value);
|
||||||
}
|
}
|
||||||
i = (i + 1) % capacity(); ++psl;
|
i = (i + 1) % capacity(); ++psl;
|
||||||
}
|
}
|
||||||
_alloc[i].value = fennec::move(value);
|
_table[i].value = fennec::move(value);
|
||||||
_sumpsl += (_alloc[i].psl = psl);
|
_sumpsl += (_table[i].psl = psl);
|
||||||
++_size;
|
++_size;
|
||||||
return iterator(this, npos);
|
return iterator(this, npos);
|
||||||
}
|
}
|
||||||
|
|
||||||
allocation<node, alloc_t> _alloc;
|
|
||||||
hash_t _hash;
|
// Private Definitions =================================================================================================
|
||||||
equal_t _equal;
|
private:
|
||||||
size_t _size;
|
struct node {
|
||||||
size_t _sumpsl;
|
optional<elem_t> value;
|
||||||
double _load;
|
int psl;
|
||||||
|
|
||||||
|
constexpr node() = default;
|
||||||
|
constexpr ~node() = default;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_TRAVERSAL_H
|
||||||
#define FENNEC_CONTAINERS_TRAVERSAL_H
|
#define FENNEC_CONTAINERS_TRAVERSAL_H
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief A set of constants used in the traverser-visitor pattern
|
||||||
enum traversal_control_ {
|
enum traversal_control_ {
|
||||||
traversal_control_continue = 0,
|
traversal_control_continue = 0, //!< Continue to the next element
|
||||||
traversal_control_jump_over,
|
traversal_control_break = 1, //!< Break the traversal loop
|
||||||
traversal_control_break
|
traversal_control_jump_over = 2, //!< Jump over the next element
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
#ifndef FENNEC_CONTAINERS_TUPLE_H
|
||||||
#define 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
|
/// \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
|
/// \tparam TypesT The types to store
|
||||||
template<typename...TypesT> struct tuple;
|
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>
|
template<size_t i, typename...TypesT>
|
||||||
constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
|
constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
|
||||||
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
|
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;
|
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>
|
template<size_t i, typename...TypesT>
|
||||||
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const tuple<TypesT...>& x) {
|
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(const tuple<TypesT...>& x) {
|
||||||
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
|
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>
|
template<typename ...TypesT>
|
||||||
struct tuple : public detail::_tuple<make_index_sequence_t<sizeof...(TypesT)>, TypesT...>
|
struct tuple : public detail::_tuple<make_index_metasequence_t<sizeof...(TypesT)>, TypesT...> {
|
||||||
{
|
|
||||||
using base_t = detail::_tuple<make_index_sequence_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>
|
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>
|
template<typename...ArgsT>
|
||||||
tuple(ArgsT&&...args)
|
tuple(ArgsT&&...args)
|
||||||
: base_t(fennec::forward<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)
|
tuple(const tuple& cpy)
|
||||||
: base_t(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>
|
template<typename...TypesT>
|
||||||
tuple(TypesT...) -> tuple<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
|
// 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
|
// 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
|
// 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
|
/// \brief fennec::engine definition
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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 introduction "Introduction"
|
||||||
/// 1. \ref coding-standards "Coding Standards"
|
/// 1. \ref coding-standards "Coding Standards"
|
||||||
/// 2. \ref building-from-source "Building from Source"
|
/// 2. \ref building-from-source "Building from Source"
|
||||||
/// 1. \ref building-from-terminal "Building from Terminal"
|
/// 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"
|
/// 2. \ref building-on-windows "Building on Windows"
|
||||||
/// 3. \ref running-the-test-suite "Running the Test Suite"
|
/// 3. \ref running-the-test-suite "Running the Test Suite"
|
||||||
/// 4. \ref usage "Usage"
|
/// 4. \ref usage "Usage"
|
||||||
|
/// 1. \ref licensing "Licensing"
|
||||||
/// 5. \ref contribution "Contribution"
|
/// 5. \ref contribution "Contribution"
|
||||||
/// 6. \subpage libraries
|
/// 6. \subpage libraries
|
||||||
/// 1. \ref fennec_lang "C++ Language Library"
|
/// 1. \ref fennec_lang "C++ Language Library"
|
||||||
/// 2. \ref fennec_math "Math 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
|
/// \page libraries Libraries
|
||||||
///
|
///
|
||||||
/// | Library | Brief |
|
/// | 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_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_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_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
|
#ifndef FENNEC_CORE_ENGINE_H
|
||||||
#define FENNEC_CORE_ENGINE_H
|
#define FENNEC_CORE_ENGINE_H
|
||||||
|
|
||||||
|
namespace fennec
|
||||||
|
{
|
||||||
|
|
||||||
|
class engine {
|
||||||
|
public:
|
||||||
|
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#endif // FENNEC_CORE_ENGINE_H
|
#endif // FENNEC_CORE_ENGINE_H
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,40 +20,148 @@
|
|||||||
#define FENNEC_CORE_EVENT_H
|
#define FENNEC_CORE_EVENT_H
|
||||||
|
|
||||||
#include <fennec/lang/types.h>
|
#include <fennec/lang/types.h>
|
||||||
#include <fennec/lang/typeuuid.h>
|
#include <fennec/rtti/enable.h>
|
||||||
|
|
||||||
|
#include <fennec/rtti/typeid.h>
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
struct event;
|
struct event;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Class outlining the interface for an object that listens for events
|
||||||
class event_listener {
|
class event_listener {
|
||||||
|
// Constructors & Destructor ===========================================================================================
|
||||||
public:
|
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;
|
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 {
|
struct event {
|
||||||
const uint64_t type;
|
|
||||||
|
|
||||||
event() = delete;
|
// Constructor & Destructor ============================================================================================
|
||||||
|
public:
|
||||||
|
|
||||||
template<typename EventT>
|
/// \name Constructors & Destructor
|
||||||
event() : type(typeuuid<EventT>()) { }
|
/// @{
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \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>
|
template<typename EventT>
|
||||||
static void add_listener(event_listener* listener) {
|
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) {
|
/// \brief Removes a listener from the event system
|
||||||
dispatch(event);
|
///
|
||||||
}
|
/// \details Blocking
|
||||||
|
/// \param listener the listener to remove
|
||||||
static void add_listener(event_listener* listener, uint64_t type);
|
|
||||||
static void remove_listener(event_listener* listener);
|
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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
#ifndef FENNEC_CORE_SYSTEM_H
|
#ifndef FENNEC_CORE_SYSTEM_H
|
||||||
#define FENNEC_CORE_SYSTEM_H
|
#define FENNEC_CORE_SYSTEM_H
|
||||||
#include <fennec/langproc/strings/string.h>
|
#include <fennec/string/string.h>
|
||||||
|
|
||||||
namespace fennec
|
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
|
// 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
|
// 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
|
// 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/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
|
|
||||||
#ifndef FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
#ifndef FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||||
#define FENNEC_LANGPROC_STRINGS_DETAIL_CTYPE_H
|
#define FENNEC_FILESYSTEM_DETAIL_CTYPE_H
|
||||||
|
|
||||||
#include <stdio.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
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang_assert
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
#ifndef FENNEC_LANG_ASSERT_H
|
||||||
#define FENNEC_LANG_ASSERT_H
|
#define FENNEC_LANG_ASSERT_H
|
||||||
|
|
||||||
|
#include <fennec/lang/types.h>
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \page fennec_lang_assert Assertions
|
/// \page fennec_lang_assert Assertions
|
||||||
///
|
///
|
||||||
@@ -46,18 +48,18 @@
|
|||||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// assert(expr, desc)
|
/// assert(expr, desc)
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// assertf(expr, desc)
|
/// assertf(expr, desc)
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// assertd(expr, desc)
|
/// assertd(expr, desc)
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <td width="50%" style="vertical-align: top">
|
||||||
/// Make an assertion, ***only in debug mode***, with expression `expr` and provide a description `desc`.
|
/// 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 `assert` would hinder performance in release mode.
|
/// This should be used when the branching caused by \f$assert\f$ would hinder performance in release mode.
|
||||||
///
|
///
|
||||||
/// </table>
|
/// </table>
|
||||||
///
|
///
|
||||||
@@ -68,23 +70,46 @@
|
|||||||
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||||
#endif
|
#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) \
|
#define assert(expression, description) \
|
||||||
if(not(expression)) [[unlikely]] { \
|
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) \
|
#define assertf(expression, description) \
|
||||||
if(not(expression)) [[unlikely]] { \
|
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
|
#if FENNEC_RELEASE
|
||||||
#define assertd(expression, description) (0)
|
#define assertd(expression, description)
|
||||||
#else
|
#else
|
||||||
#define assertd(expression, description) assert(expression, description)
|
#define assertd(expression, description) assert(expression, description)
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang_bit_manipulation
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,27 +26,58 @@ namespace fennec
|
|||||||
|
|
||||||
// equality ============================================================================================================
|
// 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;
|
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>
|
template<typename T0, typename T1> requires has_equals_v<T0, T1>
|
||||||
struct equality<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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return x == y;
|
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>
|
template<typename T0, typename T1> requires(not has_equals_v<T0, T1>
|
||||||
and has_less_v<T0, T1> and has_less_v<T1, T0>)
|
and has_less_v<T0, T1> and has_less_v<T1, T0>)
|
||||||
struct equality<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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return not(x < y) and not(y < x);
|
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>)
|
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(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>))
|
and(has_greater_v<T0, T1> and has_greater_v<T1, T0>))
|
||||||
struct equality<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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return not(x > y) and not(y > x);
|
return not(x > y) and not(y > x);
|
||||||
}
|
}
|
||||||
@@ -55,24 +86,72 @@ struct equality<T0, T1> {
|
|||||||
|
|
||||||
// inequality ==========================================================================================================
|
// 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;
|
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>
|
template<typename T0, typename T1> requires has_nequals_v<T0, T1>
|
||||||
struct inequality<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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return x != y;
|
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>
|
template<typename T0, typename T1> requires has_less_v<T0, T1> and has_less_v<T1, T0>
|
||||||
struct inequality<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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return (x < y) or (y < x);
|
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>
|
template<typename T0, typename T1> requires has_greater_v<T0, T1> and has_greater_v<T1, T0>
|
||||||
struct inequality<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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return (x > y) or (y > x);
|
return (x > y) or (y > x);
|
||||||
}
|
}
|
||||||
@@ -81,8 +160,17 @@ struct inequality<T0, T1> {
|
|||||||
|
|
||||||
// less ================================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0> requires has_less_v<T0, T1>
|
||||||
struct less {
|
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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return x < y;
|
return x < y;
|
||||||
}
|
}
|
||||||
@@ -91,30 +179,57 @@ struct less {
|
|||||||
|
|
||||||
// less_equal ==========================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0> requires has_less_equals_v<T0, T1>
|
||||||
struct less_equals {
|
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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return x <= y;
|
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>
|
template<typename T0, typename T1 = T0> requires has_greater_v<T0, T1>
|
||||||
struct greater {
|
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 {
|
constexpr bool operator()(const T0& x, const T1& y) const {
|
||||||
return x < y;
|
return x > y;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// less_equal ==========================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0> requires has_greater_equals_v<T0, T1>
|
||||||
struct greater_equals {
|
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 {
|
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
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang_conditional_types
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
/// \brief select between two types based on a condition
|
||||||
///
|
///
|
||||||
/// \details Selects between \p TrueT and \p FalseT based on the boolean value \p b.
|
/// \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 B the value of the condition
|
||||||
/// \tparam TrueT type to use when \f$B == true\f$
|
/// \tparam TrueT type to use when \f$B == true\f$
|
||||||
/// \tparam FalseT type to use when \f$B == false\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>
|
template<bool B, typename TrueT, typename FalseT>
|
||||||
using conditional_t
|
using conditional_t
|
||||||
= typename conditional<B, TrueT, FalseT>::type;
|
= typename conditional<B, TrueT, FalseT>::type;
|
||||||
|
|
||||||
|
#ifndef FENNEC_DOXYGEN
|
||||||
// specialization of fennec::conditional for `true` case
|
// specialization of fennec::conditional for \f$true\f$ case
|
||||||
template<typename T, typename F>
|
template<typename T, typename F>
|
||||||
struct conditional<true, T, F> : type_identity<T>{};
|
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>
|
template<typename T, typename F>
|
||||||
struct conditional<false, T, F> : type_identity<F>{};
|
struct conditional<false, T, F> : type_identity<F>{};
|
||||||
|
#endif
|
||||||
|
|
||||||
// fennec::detect ======================================================================================================
|
// 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
|
/// \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 `detect::is_detected` representing whether `DetectT<ArgsT...>` is found.
|
/// a boolean value is stored in `fennec::detect::is_detected` representing whether \f$DetectT<ArgsT...>\f$ is found.
|
||||||
/// \tparam DefaultT Default type
|
/// \tparam DefaultT Default type
|
||||||
/// \tparam DetectT Type to detect
|
/// \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>
|
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||||
struct detect
|
struct detect
|
||||||
{
|
{
|
||||||
using type = DefaultT;
|
using type = DefaultT; //!< the detected type
|
||||||
static constexpr bool is_detected = false;
|
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>
|
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||||
using detect_t = typename detect<DefaultT, DetectT, ArgsT...>::type;
|
using detect_t = typename detect<DefaultT, DetectT, ArgsT...>::type;
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FENNEC_DOXYGEN
|
||||||
// true case
|
// true case
|
||||||
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
template<typename DefaultT, template<typename...> typename DetectT, typename...ArgsT>
|
||||||
requires requires { typename DetectT<ArgsT...>; }
|
requires requires { typename DetectT<ArgsT...>; }
|
||||||
@@ -131,6 +132,7 @@ struct detect<DefaultT, DetectT, ArgsT...>
|
|||||||
using type = DetectT<ArgsT...>;
|
using type = DetectT<ArgsT...>;
|
||||||
static constexpr bool is_detected = true;
|
static constexpr bool is_detected = true;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
// fennec::enable_if ===================================================================================================
|
// 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
|
/// \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**
|
/// **Example Usage**
|
||||||
/// \code{.cpp}
|
/// \code{.cpp}
|
||||||
/// template<typename TypeT,
|
/// template<typename TypeT,
|
||||||
@@ -152,13 +154,15 @@ template<bool B, typename T = void>
|
|||||||
struct enable_if {};
|
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>
|
template<bool B, typename T = void>
|
||||||
using enable_if_t = typename enable_if<B, T>::type;
|
using enable_if_t = typename enable_if<B, T>::type;
|
||||||
|
|
||||||
|
#ifndef FENNEC_DOXYGEN
|
||||||
// true case
|
// true case
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct enable_if<true, T> { using type = 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
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang_constants
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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/>.
|
// 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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,6 +26,8 @@
|
|||||||
#define __PTRDIFF_TYPE__ ptrdiff_t
|
#define __PTRDIFF_TYPE__ ptrdiff_t
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Include math since stdint will define its own versions of isinf and isnan
|
||||||
|
#include <math.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,13 +19,19 @@
|
|||||||
#ifndef FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
#ifndef FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||||
#define FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
#define FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||||
|
|
||||||
|
#include <fennec/math/common.h>
|
||||||
#include <fennec/lang/type_transforms.h>
|
#include <fennec/lang/type_transforms.h>
|
||||||
|
|
||||||
namespace fennec::detail
|
namespace fennec::detail
|
||||||
{
|
{
|
||||||
|
template<typename...TypesT> struct _type_sequence {};
|
||||||
|
|
||||||
template<typename FirstT, typename... RestT> struct _first_element : type_identity<FirstT> {};
|
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, typename...TypesT> struct _nth_element;
|
||||||
|
|
||||||
template<size_t n, size_t i> struct _nth_element<n, i> : type_identity<void> {};
|
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,
|
n == i, HeadT,
|
||||||
typename _nth_element<n, i + 1, RestT...>::type
|
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
|
#endif // FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,8 +19,9 @@
|
|||||||
#ifndef FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
#ifndef FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||||
#define 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/constants.h>
|
||||||
#include <fennec/lang/float.h>
|
#include <fennec/lang/declval.h>
|
||||||
|
|
||||||
namespace fennec::detail
|
namespace fennec::detail
|
||||||
{
|
{
|
||||||
@@ -54,6 +55,12 @@ namespace fennec::detail
|
|||||||
template<> struct _is_integral<llong_t> : true_type {};
|
template<> struct _is_integral<llong_t> : true_type {};
|
||||||
template<> struct _is_integral<ullong_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
|
// 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_signed : bool_constant<TypeT(-1) < TypeT(0)> {};
|
||||||
template<typename TypeT> struct _is_unsigned : bool_constant<TypeT(-1) >= TypeT(0)> {};
|
template<typename TypeT> struct _is_unsigned : bool_constant<TypeT(-1) >= TypeT(0)> {};
|
||||||
@@ -62,14 +69,83 @@ namespace fennec::detail
|
|||||||
template<> struct _is_floating_point<float_t> : true_type {};
|
template<> struct _is_floating_point<float_t> : true_type {};
|
||||||
template<> struct _is_floating_point<double_t> : true_type {};
|
template<> struct _is_floating_point<double_t> : true_type {};
|
||||||
|
|
||||||
template<typename> struct _is_pointer : false_type {};
|
template<typename> struct _is_pointer : false_type {};
|
||||||
template<typename T> struct _is_pointer<T*> : true_type {};
|
template<typename T> struct _is_pointer<T*> : true_type {};
|
||||||
|
|
||||||
template<typename T, typename U = T&&> U _declval(int);
|
template<typename> struct _is_reference : false_type {};
|
||||||
template<typename T> T _declval(long);
|
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
|
#endif // FENNEC_LANG_DETAIL_TYPE_TRAITS_H
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,29 +20,72 @@
|
|||||||
#define FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
|
#define FENNEC_LANG_DETAIL_TYPE_TRANSFORMS_H
|
||||||
|
|
||||||
#include <fennec/lang/types.h>
|
#include <fennec/lang/types.h>
|
||||||
|
#include <fennec/lang/detail/_type_traits.h>
|
||||||
|
|
||||||
namespace fennec::detail
|
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 {
|
struct _add_lvalue_reference {
|
||||||
using type = _Tp;
|
using type = TypeT;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename _Tp>
|
template<typename TypeT>
|
||||||
struct _add_lvalue_reference<_Tp, void_t<_Tp&>> {
|
struct _add_lvalue_reference<TypeT, void_t<TypeT&>> {
|
||||||
using type = _Tp&;
|
using type = TypeT&;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<typename _Tp, typename = void>
|
template<typename TypeT, typename = void>
|
||||||
struct _add_rvalue_reference {
|
struct _add_rvalue_reference {
|
||||||
using type = _Tp;
|
using type = TypeT;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename _Tp>
|
template<typename TypeT>
|
||||||
struct _add_rvalue_reference<_Tp, void_t<_Tp&&>> {
|
struct _add_rvalue_reference<TypeT, void_t<TypeT&&>> {
|
||||||
using type = _Tp&&;
|
using type = TypeT&&;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
/// \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_DENORM_MIN
|
||||||
#undef FLT_ROUND_ERR
|
#undef FLT_ROUND_ERR
|
||||||
|
|
||||||
|
/// \brief Does \f$float\f$ have an infinity?
|
||||||
#define FLT_HAS_INFINITY 1
|
#define FLT_HAS_INFINITY 1
|
||||||
|
/// \brief Does \f$float\f$ have a quiet NaN?
|
||||||
#define FLT_HAS_QUIET_NAN 1
|
#define FLT_HAS_QUIET_NAN 1
|
||||||
|
/// \brief Does \f$float\f$ have a signaling NaN?
|
||||||
#define FLT_HAS_SIGNALING_NAN 1
|
#define FLT_HAS_SIGNALING_NAN 1
|
||||||
|
/// \brief Does \f$float\f$ use denormalization?
|
||||||
#define FLT_HAS_DENORM 1
|
#define FLT_HAS_DENORM 1
|
||||||
|
/// \brief Does \f$float\f$ have loss with denormalization?
|
||||||
#define FLT_HAS_DENORM_LOSS 0
|
#define FLT_HAS_DENORM_LOSS 0
|
||||||
|
/// \brief What rounding style does \f$float\f$ use?
|
||||||
#define FLT_ROUNDS 1
|
#define FLT_ROUNDS 1
|
||||||
|
/// \brief Does \f$float\f$ use the IEEE floating point specification?
|
||||||
#define FLT_IS_IEC559 1
|
#define FLT_IS_IEC559 1
|
||||||
|
/// \brief The number of mantissa bits in \f$float\f$.
|
||||||
#define FLT_MANT_DIG 24
|
#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
|
#define FLT_DIG 6
|
||||||
|
/// \brief The decimal precision required to serialize and deserialize a \f$float\f$.
|
||||||
#define FLT_DECIMAL_DIG 9
|
#define FLT_DECIMAL_DIG 9
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$float\f$.
|
||||||
#define FLT_RADIX 2
|
#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
|
#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
|
#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
|
#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
|
#define FLT_MAX_10_EXP 38
|
||||||
|
/// \brief Do arithmetics operations with \f$float\f$ trap?
|
||||||
#define FLT_TRAPS 0
|
#define FLT_TRAPS 0
|
||||||
|
/// \brief Do arithmetics operations with \f$float\f$ check for underflow?
|
||||||
#define FLT_TINYNESS_BEFORE 0
|
#define FLT_TINYNESS_BEFORE 0
|
||||||
|
/// \brief Smallest positive, finite, normal value of \f$float\f$.
|
||||||
#define FLT_MIN fennec::bit_cast<float>(0x800000)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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)
|
#define FLT_ROUND_ERR fennec::bit_cast<float>(0x3f000000)
|
||||||
|
|
||||||
#undef DBL_HAS_INFINITY
|
#undef DBL_HAS_INFINITY
|
||||||
@@ -110,30 +137,55 @@
|
|||||||
#undef DBL_DENORM_MIN
|
#undef DBL_DENORM_MIN
|
||||||
#undef DBL_ROUND_ERR
|
#undef DBL_ROUND_ERR
|
||||||
|
|
||||||
|
/// \brief Does \f$double\f$ have an infinity?
|
||||||
#define DBL_HAS_INFINITY 1
|
#define DBL_HAS_INFINITY 1
|
||||||
|
/// \brief Does \f$double\f$ have a quiet NaN?
|
||||||
#define DBL_HAS_QUIET_NAN 1
|
#define DBL_HAS_QUIET_NAN 1
|
||||||
|
/// \brief Does \f$double\f$ have a signaling NaN?
|
||||||
#define DBL_HAS_SIGNALING_NAN 1
|
#define DBL_HAS_SIGNALING_NAN 1
|
||||||
|
/// \brief Does \f$double\f$ use denormalization?
|
||||||
#define DBL_HAS_DENORM 1
|
#define DBL_HAS_DENORM 1
|
||||||
|
/// \brief Does \f$double\f$ have loss with denormalization?
|
||||||
#define DBL_HAS_DENORM_LOSS 0
|
#define DBL_HAS_DENORM_LOSS 0
|
||||||
|
/// \brief What rounding style does \f$double\f$ use?
|
||||||
#define DBL_ROUNDS 1
|
#define DBL_ROUNDS 1
|
||||||
|
/// \brief Does \f$double\f$ use the IEEE doubleing point specification?
|
||||||
#define DBL_IS_IEC559 1
|
#define DBL_IS_IEC559 1
|
||||||
|
/// \brief The number of mantissa bits in \f$double\f$.
|
||||||
#define DBL_MANT_DIG 53
|
#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
|
#define DBL_DIG 15
|
||||||
|
/// \brief The decimal precision required to serialize and deserialize a \f$double\f$.
|
||||||
#define DBL_DECIMAL_DIG 17
|
#define DBL_DECIMAL_DIG 17
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$double\f$.
|
||||||
#define DBL_RADIX 2
|
#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
|
#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
|
#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
|
#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
|
#define DBL_MAX_10_EXP 308
|
||||||
|
/// \brief Do arithmetics operations with \f$double\f$ trap?
|
||||||
#define DBL_TRAPS 0
|
#define DBL_TRAPS 0
|
||||||
|
/// \brief Do arithmetics operations with \f$double\f$ check for underflow?
|
||||||
#define DBL_TINYNESS_BEFORE 0
|
#define DBL_TINYNESS_BEFORE 0
|
||||||
|
/// \brief Smallest positive, finite, normal value of \f$double\f$.
|
||||||
#define DBL_MIN fennec::bit_cast<double>(0x10000000000000ll)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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)
|
#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)
|
#define DBL_ROUND_ERR fennec::bit_cast<double>(0x3fe0000000000000ll)
|
||||||
|
|
||||||
#endif // FENNEC_LANG_FLOAT_H
|
#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
|
// 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
|
// 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
|
// 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/types.h>
|
||||||
#include <fennec/lang/type_traits.h>
|
#include <fennec/lang/type_traits.h>
|
||||||
|
#include <fennec/lang/bits.h>
|
||||||
|
|
||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
@@ -30,10 +31,13 @@ namespace fennec
|
|||||||
/// \tparam Key The type to hash
|
/// \tparam Key The type to hash
|
||||||
template<typename Key> struct hash;
|
template<typename Key> struct hash;
|
||||||
|
|
||||||
// Murmur3 Hash for 64-bit ints
|
/// \brief Murmur3 Hash for 64-bit ints
|
||||||
template<>
|
template<>
|
||||||
struct hash<uint64_t> {
|
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 {
|
constexpr size_t operator()(uint64_t x) const {
|
||||||
// Murmur3
|
// Murmur3
|
||||||
x ^= x >> 33U;
|
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>
|
template<typename IntT>
|
||||||
requires is_integral_v<IntT>
|
requires is_integral_v<IntT>
|
||||||
struct hash<IntT> : hash<uint64_t> {
|
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>
|
template<typename PtrT>
|
||||||
struct hash<PtrT*> : hash<uintptr_t> {
|
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 {
|
constexpr size_t operator()(PtrT* ptr) const {
|
||||||
return hash<uintptr_t>::operator()((uintptr_t)(const void*)ptr);
|
return hash<uintptr_t>::operator()((uintptr_t)(const void*)ptr);
|
||||||
}
|
}
|
||||||
@@ -63,6 +73,10 @@ struct hash<PtrT*> : hash<uintptr_t> {
|
|||||||
// Float
|
// Float
|
||||||
template<>
|
template<>
|
||||||
struct hash<float> : hash<uint32_t> {
|
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 {
|
constexpr size_t operator()(float x) const {
|
||||||
return hash<uint32_t>::operator()(bit_cast<uint32_t>(x));
|
return hash<uint32_t>::operator()(bit_cast<uint32_t>(x));
|
||||||
}
|
}
|
||||||
@@ -70,6 +84,10 @@ struct hash<float> : hash<uint32_t> {
|
|||||||
|
|
||||||
template<>
|
template<>
|
||||||
struct hash<double> : hash<uint64_t> {
|
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 {
|
constexpr size_t operator()(double x) const {
|
||||||
return hash<uint64_t>::operator()(bit_cast<uint64_t>(x));
|
return hash<uint64_t>::operator()(bit_cast<uint64_t>(x));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
/// \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_MIN
|
||||||
#undef ULLONG_MAX
|
#undef ULLONG_MAX
|
||||||
|
|
||||||
|
/// \brief Is \f$char\f$ signed?
|
||||||
#define CHAR_IS_SIGNED true
|
#define CHAR_IS_SIGNED true
|
||||||
|
/// \brief Rounding style of type \f$char\f$.
|
||||||
#define CHAR_ROUNDS 0x0
|
#define CHAR_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$char\f$.
|
||||||
#define CHAR_RADIX_DIG 0x7
|
#define CHAR_RADIX_DIG 0x7
|
||||||
|
/// \brief Number of decimal digits represented by \f$char\f$.
|
||||||
#define CHAR_DIG 0x2
|
#define CHAR_DIG 0x2
|
||||||
|
/// \brief Number of decimal digits necessary to differentiate all values of type \f$char\f$.
|
||||||
#define CHAR_DECIMAL_DIG 0x0
|
#define CHAR_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$char\f$.
|
||||||
#define CHAR_RADIX 0x2
|
#define CHAR_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$char\f$ trap?
|
||||||
#define CHAR_TRAPS 0xtrue
|
#define CHAR_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$char\f$.
|
||||||
#define CHAR_MIN 0x80
|
#define CHAR_MIN 0x80
|
||||||
|
/// \brief Largest finite value of \f$char\f$.
|
||||||
#define CHAR_MAX 0x7f
|
#define CHAR_MAX 0x7f
|
||||||
|
|
||||||
|
/// \brief Is \f$wchar_t\f$ signed?
|
||||||
#define WCHAR_IS_SIGNED true
|
#define WCHAR_IS_SIGNED true
|
||||||
|
/// \brief Rounding style of type \f$wchar_t\f$.
|
||||||
#define WCHAR_ROUNDS 0x0
|
#define WCHAR_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$wchar_t\f$.
|
||||||
#define WCHAR_RADIX_DIG 0x1f
|
#define WCHAR_RADIX_DIG 0x1f
|
||||||
|
/// \brief Number of decimal digits represented by \f$wchar_t\f$.
|
||||||
#define WCHAR_DIG 0x9
|
#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
|
#define WCHAR_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$wchar_t\f$.
|
||||||
#define WCHAR_RADIX 0x2
|
#define WCHAR_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$wchar_t\f$ trap?
|
||||||
#define WCHAR_TRAPS 0xtrue
|
#define WCHAR_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$wchar_t\f$.
|
||||||
#define WCHAR_MIN 0x80000000
|
#define WCHAR_MIN 0x80000000
|
||||||
|
/// \brief Largest finite value of \f$wchar_t\f$.
|
||||||
#define WCHAR_MAX 0x7fffffff
|
#define WCHAR_MAX 0x7fffffff
|
||||||
|
|
||||||
|
/// \brief Is \f$signed char\f$ signed?
|
||||||
#define SCHAR_ROUNDS 0x0
|
#define SCHAR_ROUNDS 0x0
|
||||||
|
/// \brief Rounding style of type \f$signed char\f$.
|
||||||
#define SCHAR_RADIX_DIG 0x7
|
#define SCHAR_RADIX_DIG 0x7
|
||||||
|
/// \brief Number of radix digits represented by \f$signed char\f$.
|
||||||
#define SCHAR_DIG 0x2
|
#define SCHAR_DIG 0x2
|
||||||
|
/// \brief Number of decimal digits represented by \f$signed char\f$.
|
||||||
#define SCHAR_DECIMAL_DIG 0x0
|
#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
|
#define SCHAR_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$signed char\f$ trap?
|
||||||
#define SCHAR_TRAPS 0xtrue
|
#define SCHAR_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$signed char\f$.
|
||||||
#define SCHAR_MIN 0x80
|
#define SCHAR_MIN 0x80
|
||||||
|
/// \brief Largest finite value of \f$signed char\f$.
|
||||||
#define SCHAR_MAX 0x7f
|
#define SCHAR_MAX 0x7f
|
||||||
|
|
||||||
|
/// \brief Is \f$unsigned char\f$ unsigned?
|
||||||
#define UCHAR_ROUNDS 0x0
|
#define UCHAR_ROUNDS 0x0
|
||||||
|
/// \brief Rounding style of type \f$unsigned char\f$.
|
||||||
#define UCHAR_RADIX_DIG 0x8
|
#define UCHAR_RADIX_DIG 0x8
|
||||||
|
/// \brief Number of radix digits represented by \f$unsigned char\f$.
|
||||||
#define UCHAR_DIG 0x2
|
#define UCHAR_DIG 0x2
|
||||||
|
/// \brief Number of decimal digits represented by \f$unsigned char\f$.
|
||||||
#define UCHAR_DECIMAL_DIG 0x0
|
#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
|
#define UCHAR_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$unsigned char\f$ trap?
|
||||||
#define UCHAR_TRAPS 0xtrue
|
#define UCHAR_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$unsigned char\f$.
|
||||||
#define UCHAR_MIN 0x0
|
#define UCHAR_MIN 0x0
|
||||||
|
/// \brief Largest finite value of \f$unsigned char\f$.
|
||||||
#define UCHAR_MAX 0xff
|
#define UCHAR_MAX 0xff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$short\f$.
|
||||||
#define SHORT_ROUNDS 0x0
|
#define SHORT_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$short\f$.
|
||||||
#define SHORT_RADIX_DIG 0xf
|
#define SHORT_RADIX_DIG 0xf
|
||||||
|
/// \brief Number of decimal digits represented by \f$short\f$.
|
||||||
#define SHORT_DIG 0x4
|
#define SHORT_DIG 0x4
|
||||||
|
/// \brief Number of decimal digits necessary to differentiate all values of type \f$short\f$.
|
||||||
#define SHORT_DECIMAL_DIG 0x0
|
#define SHORT_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$short\f$.
|
||||||
#define SHORT_RADIX 0x2
|
#define SHORT_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$short\f$ trap?
|
||||||
#define SHORT_TRAPS 0xtrue
|
#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
|
#define SHORT_MAX 0x7fff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$unsigned short\f$.
|
||||||
#define USHORT_ROUNDS 0x0
|
#define USHORT_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$unsigned short\f$.
|
||||||
#define USHORT_RADIX_DIG 0x10
|
#define USHORT_RADIX_DIG 0x10
|
||||||
|
/// \brief Number of decimal digits represented by \f$unsigned short\f$.
|
||||||
#define USHORT_DIG 0x4
|
#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
|
#define USHORT_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$unsigned short\f$.
|
||||||
#define USHORT_RADIX 0x2
|
#define USHORT_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$unsigned short\f$ trap?
|
||||||
#define USHORT_TRAPS 0xtrue
|
#define USHORT_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$unsigned short\f$.
|
||||||
#define USHORT_MIN 0x0
|
#define USHORT_MIN 0x0
|
||||||
|
/// \brief Largest finite value of \f$unsigned short\f$.
|
||||||
#define USHORT_MAX 0xffff
|
#define USHORT_MAX 0xffff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$int\f$.
|
||||||
#define INT_ROUNDS 0x0
|
#define INT_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$int\f$.
|
||||||
#define INT_RADIX_DIG 0x1f
|
#define INT_RADIX_DIG 0x1f
|
||||||
|
/// \brief Number of decimal digits represented by \f$int\f$.
|
||||||
#define INT_DIG 0x9
|
#define INT_DIG 0x9
|
||||||
|
/// \brief Number of decimal digits necessary to differentiate all values of type \f$int\f$.
|
||||||
#define INT_DECIMAL_DIG 0x0
|
#define INT_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or integer base, used to represent a \f$int\f$.
|
||||||
#define INT_RADIX 0x2
|
#define INT_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$int\f$ trap?
|
||||||
#define INT_TRAPS 0xtrue
|
#define INT_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$int\f$.
|
||||||
#define INT_MIN 0x80000000
|
#define INT_MIN 0x80000000
|
||||||
|
/// \brief Largest finite value of \f$int\f$.
|
||||||
#define INT_MAX 0x7fffffff
|
#define INT_MAX 0x7fffffff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$unsigned int\f$.
|
||||||
#define UINT_ROUNDS 0x0
|
#define UINT_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$unsigned int\f$.
|
||||||
#define UINT_RADIX_DIG 0x20
|
#define UINT_RADIX_DIG 0x20
|
||||||
|
/// \brief Number of decimal digits represented by \f$unsigned int\f$.
|
||||||
#define UINT_DIG 0x9
|
#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
|
#define UINT_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or unsigned integer base, used to represent a \f$unsigned int\f$.
|
||||||
#define UINT_RADIX 0x2
|
#define UINT_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$unsigned int\f$ trap?
|
||||||
#define UINT_TRAPS 0xtrue
|
#define UINT_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$unsigned int\f$.
|
||||||
#define UINT_MIN 0x0
|
#define UINT_MIN 0x0
|
||||||
|
/// \brief Largest finite value of \f$unsigned int\f$.
|
||||||
#define UINT_MAX 0xffffffff
|
#define UINT_MAX 0xffffffff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$long int\f$.
|
||||||
#define LONG_ROUNDS 0x0
|
#define LONG_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$long int\f$.
|
||||||
#define LONG_RADIX_DIG 0x3f
|
#define LONG_RADIX_DIG 0x3f
|
||||||
|
/// \brief Number of decimal digits represented by \f$long int\f$.
|
||||||
#define LONG_DIG 0x12
|
#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
|
#define LONG_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or long integer base, used to represent a \f$long int\f$.
|
||||||
#define LONG_RADIX 0x2
|
#define LONG_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$long int\f$ trap?
|
||||||
#define LONG_TRAPS 0xtrue
|
#define LONG_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$long int\f$.
|
||||||
#define LONG_MIN 0x8000000000000000
|
#define LONG_MIN 0x8000000000000000
|
||||||
|
/// \brief Largest finite value of \f$long int\f$.
|
||||||
#define LONG_MAX 0x7fffffffffffffff
|
#define LONG_MAX 0x7fffffffffffffff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$unsigned long int\f$.
|
||||||
#define ULONG_ROUNDS 0x0
|
#define ULONG_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$unsigned long int\f$.
|
||||||
#define ULONG_RADIX_DIG 0x40
|
#define ULONG_RADIX_DIG 0x40
|
||||||
|
/// \brief Number of decimal digits represented by \f$unsigned long int\f$.
|
||||||
#define ULONG_DIG 0x13
|
#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
|
#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
|
#define ULONG_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$unsigned long int\f$ trap?
|
||||||
#define ULONG_TRAPS 0xtrue
|
#define ULONG_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$unsigned long int\f$.
|
||||||
#define ULONG_MIN 0x0
|
#define ULONG_MIN 0x0
|
||||||
|
/// \brief Largest finite value of \f$unsigned long int\f$.
|
||||||
#define ULONG_MAX 0xffffffffffffffff
|
#define ULONG_MAX 0xffffffffffffffff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$long long\f$.
|
||||||
#define LLONG_ROUNDS 0x0
|
#define LLONG_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$long long\f$.
|
||||||
#define LLONG_RADIX_DIG 0x3f
|
#define LLONG_RADIX_DIG 0x3f
|
||||||
|
/// \brief Number of decimal digits represented by \f$long long\f$.
|
||||||
#define LLONG_DIG 0x12
|
#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
|
#define LLONG_DECIMAL_DIG 0x0
|
||||||
|
/// \brief The radix, or long longeger base, used to represent a \f$long long\f$.
|
||||||
#define LLONG_RADIX 0x2
|
#define LLONG_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$long long\f$ trap?
|
||||||
#define LLONG_TRAPS 0xtrue
|
#define LLONG_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$long long\f$.
|
||||||
#define LLONG_MIN 0x8000000000000000
|
#define LLONG_MIN 0x8000000000000000
|
||||||
|
/// \brief Largest finite value of \f$long long\f$.
|
||||||
#define LLONG_MAX 0x7fffffffffffffff
|
#define LLONG_MAX 0x7fffffffffffffff
|
||||||
|
|
||||||
|
/// \brief Rounding style of type \f$unsigned long long\f$.
|
||||||
#define ULLONG_ROUNDS 0x0
|
#define ULLONG_ROUNDS 0x0
|
||||||
|
/// \brief Number of radix digits represented by \f$unsigned long long\f$.
|
||||||
#define ULLONG_RADIX_DIG 0x40
|
#define ULLONG_RADIX_DIG 0x40
|
||||||
|
/// \brief Number of decimal digits represented by \f$unsigned long long\f$.
|
||||||
#define ULLONG_DIG 0x13
|
#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
|
#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
|
#define ULLONG_RADIX 0x2
|
||||||
|
/// \brief Do arithmetics operations with \f$unsigned long long\f$ trap?
|
||||||
#define ULLONG_TRAPS 0xtrue
|
#define ULLONG_TRAPS 0xtrue
|
||||||
|
/// \brief Smallest finite value of \f$unsigned long long\f$.
|
||||||
#define ULLONG_MIN 0x0
|
#define ULLONG_MIN 0x0
|
||||||
|
/// \brief Largest finite value of \f$unsigned long long\f$.
|
||||||
#define ULLONG_MAX 0xffffffffffffffff
|
#define ULLONG_MAX 0xffffffffffffffff
|
||||||
|
|
||||||
#endif // FENNEC_LANG_INTEGER_H
|
#endif // FENNEC_LANG_INTEGER_H
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
/// \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
|
/// <tr><th style="vertical-align: top">Syntax
|
||||||
/// <th style="vertical-align: top">Description
|
/// <th style="vertical-align: top">Description
|
||||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_BIT_CAST` <br>
|
/// \f$FENNEC_HAS_BUILTIN_BIT_CAST\f$ <br>
|
||||||
/// `Y FENNEC_BUILTIN_BIT_CAST(X)`
|
/// \f$Y FENNEC_BUILTIN_BIT_CAST(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_ADDRESSOF` <br>
|
/// \f$FENNEC_HAS_BUILTIN_ADDRESSOF\f$ <br>
|
||||||
/// `Y FENNEC_BUILTIN_ADDRESSOF(X)`
|
/// \f$Y FENNEC_BUILTIN_ADDRESSOF(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_CONVERTIBLE` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_CONVERTIBLE\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)`
|
/// \f$B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_EMPTY` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_EMPTY\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_EMPTY(X)`
|
/// \f$B FENNEC_BUILTIN_IS_EMPTY(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_POLYMORPHIC` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_POLYMORPHIC\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_POLYMORPHIC(X)`
|
/// \f$B FENNEC_BUILTIN_IS_POLYMORPHIC(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_FINAL` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_FINAL\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_FINAL(X)`
|
/// \f$B FENNEC_BUILTIN_IS_FINAL(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_ABSTRACT` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_ABSTRACT\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_ABSTRACT(X)`
|
/// \f$B FENNEC_BUILTIN_IS_ABSTRACT(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)`
|
/// \f$B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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
|
/// for this trait
|
||||||
///
|
///
|
||||||
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
/// <tr><td width="50%" style="vertical-align: top"> <br>
|
||||||
/// `FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE` <br>
|
/// \f$FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE\f$ <br>
|
||||||
/// `B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)`
|
/// \f$B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)\f$
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <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>
|
/// </table>
|
||||||
///
|
///
|
||||||
@@ -102,6 +102,9 @@
|
|||||||
// Most major compilers support __has_builtin, notably GCC, MINGW, and CLANG
|
// Most major compilers support __has_builtin, notably GCC, MINGW, and CLANG
|
||||||
#if defined(__has_builtin)
|
#if defined(__has_builtin)
|
||||||
|
|
||||||
|
|
||||||
|
// UTILITIES ===========================================================================================================
|
||||||
|
|
||||||
// addressof is very difficult to implement without intrinsics.
|
// addressof is very difficult to implement without intrinsics.
|
||||||
#if __has_builtin(__builtin_addressof)
|
#if __has_builtin(__builtin_addressof)
|
||||||
# define FENNEC_HAS_BUILTIN_ADDRESSOF 1
|
# define FENNEC_HAS_BUILTIN_ADDRESSOF 1
|
||||||
@@ -118,6 +121,37 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_BIT_CAST 0
|
# define FENNEC_HAS_BUILTIN_BIT_CAST 0
|
||||||
#endif
|
#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
|
// Inconsistent without intrinsics
|
||||||
#if __has_builtin(__is_abstract)
|
#if __has_builtin(__is_abstract)
|
||||||
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 1
|
# define FENNEC_HAS_BUILTIN_IS_ABSTRACT 1
|
||||||
@@ -134,6 +168,14 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_IS_ARRAY
|
# define FENNEC_HAS_BUILTIN_IS_ARRAY
|
||||||
#endif
|
#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
|
// Inconsistent without intrinsics
|
||||||
#if __has_builtin(__is_class)
|
#if __has_builtin(__is_class)
|
||||||
# define FENNEC_HAS_BUILTIN_IS_CLASS 1
|
# define FENNEC_HAS_BUILTIN_IS_CLASS 1
|
||||||
@@ -142,6 +184,36 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_IS_CLASS
|
# define FENNEC_HAS_BUILTIN_IS_CLASS
|
||||||
#endif
|
#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 ========================================================================================================
|
// CONSTRUCTORS ========================================================================================================
|
||||||
|
|
||||||
// Difficult and Inconsistent without intrinsics
|
// Difficult and Inconsistent without intrinsics
|
||||||
@@ -155,11 +227,19 @@
|
|||||||
// Difficult and Inconsistent without intrinsics
|
// Difficult and Inconsistent without intrinsics
|
||||||
#if __has_builtin(__is_trivially_constructible)
|
#if __has_builtin(__is_trivially_constructible)
|
||||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 1
|
# 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
|
#else
|
||||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0
|
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_CONSTRUCTIBLE 0
|
||||||
#endif
|
#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
|
// Difficult and Inconsistent without intrinsics
|
||||||
#if __has_builtin(__has_trivial_destructor)
|
#if __has_builtin(__has_trivial_destructor)
|
||||||
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1
|
# define FENNEC_HAS_BUILTIN_IS_TRIVIALLY_DESTRUCTIBLE 1
|
||||||
@@ -179,6 +259,54 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 0
|
# define FENNEC_HAS_BUILTIN_IS_ASSIGNABLE 0
|
||||||
#endif
|
#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
|
// Type Traits
|
||||||
// can_convert is also very difficult to implement without intrinsics
|
// can_convert is also very difficult to implement without intrinsics
|
||||||
@@ -213,6 +341,14 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_IS_ENUM 0
|
# define FENNEC_HAS_BUILTIN_IS_ENUM 0
|
||||||
#endif
|
#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
|
// Inconsistent without intrinsics
|
||||||
#if __has_builtin(__is_final)
|
#if __has_builtin(__is_final)
|
||||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
|
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
|
||||||
@@ -221,12 +357,36 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
|
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Inconsistent with dynamic intrinsics, requires a massive table for static intrinsics
|
// Inconsistent without intrinsics
|
||||||
#if __has_builtin(__is_fundamental)
|
#if __has_builtin(__is_aggregate)
|
||||||
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 1
|
# define FENNEC_HAS_BUILTIN_IS_AGGREGATE 1
|
||||||
# define FENNEC_BUILTIN_IS_FUNDAMENTAL(arg) __is_fundamental(arg)
|
# define FENNEC_BUILTIN_IS_AGGREGATE(arg) __is_aggregate(arg)
|
||||||
#else
|
#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
|
#endif
|
||||||
|
|
||||||
// Inconsistent without intrinsics
|
// Inconsistent without intrinsics
|
||||||
@@ -237,14 +397,6 @@
|
|||||||
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 0
|
# define FENNEC_HAS_BUILTIN_IS_POLYMORPHIC 0
|
||||||
#endif
|
#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
|
// For compilers without or differently named builtins
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang_limits
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
/// \tparam TypeT Numeric types, may be overloaded for custom types
|
||||||
template<typename TypeT> struct numeric_limits
|
template<typename TypeT> struct numeric_limits
|
||||||
{
|
{
|
||||||
static constexpr bool is_specialized = false; ///< Check if the template is specialized for TypeT
|
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_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_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 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_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_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_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 = false; //!< Check if TypeT denormalizes
|
||||||
static constexpr bool has_denorm_loss = false; ///< Check if TypeT has precision loss when denormalized
|
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_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_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 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 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 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 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 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 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 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_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 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_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 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
|
// 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 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 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 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 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 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 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 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 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 denorm_min() { return TypeT(); } //!< \returns a value of TypeT holding the smallest positive subnormal
|
||||||
};
|
};
|
||||||
|
|
||||||
// Overload definitions for basic types
|
// Overload definitions for basic types
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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_constants
|
||||||
/// - \subpage fennec_lang_conditional_types
|
/// - \subpage fennec_lang_conditional_types
|
||||||
/// - \subpage fennec_lang_numeric_transforms
|
/// - \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_sequences
|
||||||
/// - \subpage fennec_lang_type_traits
|
/// - \subpage fennec_lang_type_traits
|
||||||
/// - \subpage fennec_lang_type_transforms
|
/// - \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
|
// 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
|
// This program is free software: you can redistribute it and/or modify
|
||||||
// it under the terms of the GNU General License as published by
|
// 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
|
/// \brief \ref fennec_lang_numeric_transforms
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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
|
#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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -19,10 +19,19 @@
|
|||||||
#ifndef FENNEC_LANG_STARTUP_H
|
#ifndef FENNEC_LANG_STARTUP_H
|
||||||
#define 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); \
|
inline static void f(void); \
|
||||||
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
|
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
|
||||||
inline static void f(void)
|
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
|
#endif // FENNEC_LANG_STARTUP_H
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// =====================================================================================================================
|
// =====================================================================================================================
|
||||||
// fennec, a free and open source game engine
|
// 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
|
// 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
|
// 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
|
// 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
|
// 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
|
// it under the terms of the GNU General Public License as published by
|
||||||
@@ -26,26 +26,38 @@ namespace fennec
|
|||||||
|
|
||||||
// has_equals ==========================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0>
|
||||||
struct has_equals {
|
struct has_equals {
|
||||||
|
private:
|
||||||
// Use SFINAE to check for the operator
|
// Use SFINAE to check for the operator
|
||||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() == declval<V>());
|
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() == declval<V>());
|
||||||
template<typename, typename> static auto test(...) -> false_type;
|
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;
|
template<typename T0, typename T1 = T0> constexpr bool has_equals_v = has_equals<T0, T1>::value;
|
||||||
|
|
||||||
// has_nequals =========================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0>
|
||||||
struct has_nequals {
|
struct has_nequals {
|
||||||
|
private:
|
||||||
// Use SFINAE to check for the operator
|
// Use SFINAE to check for the operator
|
||||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() != declval<V>());
|
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() != declval<V>());
|
||||||
template<typename, typename> static auto test(...) -> false_type;
|
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;
|
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 ============================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0>
|
||||||
struct has_less {
|
struct has_less {
|
||||||
|
private:
|
||||||
// Use SFINAE to check for the operator
|
// Use SFINAE to check for the operator
|
||||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() < declval<V>());
|
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() < declval<V>());
|
||||||
template<typename, typename> static auto test(...) -> false_type;
|
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;
|
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 =====================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0>
|
||||||
struct has_less_equals {
|
struct has_less_equals {
|
||||||
|
private:
|
||||||
// Use SFINAE to check for the operator
|
// Use SFINAE to check for the operator
|
||||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() <= declval<V>());
|
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() <= declval<V>());
|
||||||
template<typename, typename> static auto test(...) -> false_type;
|
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;
|
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 =========================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0>
|
||||||
struct has_greater {
|
struct has_greater {
|
||||||
|
private:
|
||||||
// Use SFINAE to check for the operator
|
// Use SFINAE to check for the operator
|
||||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() > declval<V>());
|
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() > declval<V>());
|
||||||
template<typename, typename> static auto test(...) -> false_type;
|
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;
|
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 ==================================================================================================
|
// 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>
|
template<typename T0, typename T1 = T0>
|
||||||
struct has_greater_equals {
|
struct has_greater_equals {
|
||||||
|
private:
|
||||||
// Use SFINAE to check for the operator
|
// Use SFINAE to check for the operator
|
||||||
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() >= declval<V>());
|
template<typename U, typename V> static auto test(U*) -> decltype(declval<U>() >= declval<V>());
|
||||||
template<typename, typename> static auto test(...) -> false_type;
|
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;
|
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
|
// 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
|
// 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
|
// 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
|
/// \brief \ref fennec_lang_type_sequences
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
/// \details
|
/// \details
|
||||||
/// \author Medusa Slockbower
|
/// \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>
|
/// \ref fennec::replace_first_element "typename replace_first_element<ClassT, SubT, OriginT, RestT...>::type"<br>
|
||||||
/// <td width="50%" style="vertical-align: top">
|
/// <td width="50%" style="vertical-align: top">
|
||||||
/// \copydoc fennec::replace_first_element
|
/// \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>
|
/// </table>
|
||||||
///
|
///
|
||||||
|
|
||||||
@@ -59,23 +65,47 @@
|
|||||||
namespace fennec
|
namespace fennec
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
template<typename...TypesT> struct type_sequence {};
|
||||||
|
|
||||||
|
|
||||||
|
// fennec::first_element ===============================================================================================
|
||||||
|
|
||||||
///
|
///
|
||||||
/// \brief Get the first element of a template parameter pack
|
/// \brief Get the first element of a template parameter pack
|
||||||
/// \tparam TypesT the Parameter Pack
|
/// \tparam TypesT the Parameter Pack
|
||||||
template<typename...TypesT> struct first_element : detail::_first_element<TypesT...> {};
|
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<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 { };
|
template<typename ClassT, typename SubT> struct replace_first_element { };
|
||||||
|
|
||||||
|
#ifndef FENNEC_DOXYGEN
|
||||||
// Implementation
|
// Implementation
|
||||||
template<
|
template<
|
||||||
template<typename, typename...> class ClassT // The Base Template
|
template<typename, typename...> class ClassT // The Base Template
|
||||||
@@ -83,6 +113,106 @@ template<
|
|||||||
, typename... RestT> // The Rest of the Parameter Pack
|
, typename... RestT> // The Rest of the Parameter Pack
|
||||||
struct replace_first_element<ClassT<OriginT, RestT...>, SubT> // Specialization
|
struct replace_first_element<ClassT<OriginT, RestT...>, SubT> // Specialization
|
||||||
{ using type = ClassT<SubT, RestT...>; }; // Definition
|
{ 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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user