Compare commits

...

41 Commits

Author SHA1 Message Date
1f6637408d - testing for current threading structures 2025-12-20 17:35:54 -05:00
9f499c933d - more threading things
TODO: documentation
2025-12-19 20:58:19 -05:00
88e33bdcc8 - More documentation 2025-12-18 16:18:07 -05:00
9e6f00eb60 - Documentation of containers, core, and format 2025-12-18 00:00:36 -05:00
e7503ed92f - Documentation and logic fixes for various structures 2025-12-17 12:40:10 -05:00
aee4e340dd - Started setting up a thread safe window manager
- Created thread & atomic structures
2025-12-17 01:11:28 -05:00
520a0e1363 - Setup libdecor, which is used automatically when available.
TODO:
 - xdg decorations
 - threading
 - thread-safe window manager
2025-12-15 23:40:06 -05:00
97f5bbfe00 - Windows now use libdecor when present. 2025-12-15 16:29:00 -05:00
b64ce44d4e - Setup dynamic linking to libdecor 2025-12-15 15:23:45 -05:00
1acf00138a - Setup EGL context for Wayland. Test window now opens as black rectangle. 2025-12-15 13:20:08 -05:00
5dcb58f53c - Setup wayland display and window. Window surface is created and appears in hotbar. Window is not visible. 2025-12-14 15:47:11 -05:00
a1bdc077b1 - fennec::variant & fennec::generic, TODO: Test 2025-12-13 19:00:43 -05:00
9553f9b662 - formatting implemented for floating point types
- fixed some bugs with width and precision specifiers:
    * Evaluation order of nested replacement fields
2025-12-08 12:13:51 -05:00
9645856554 - Refactored fennec::format to use to_chars for ints. floats TODO but will also use to_chars. 2025-12-06 02:19:52 -05:00
a2abb58705 - Fixed header include guards 2025-12-05 10:30:03 -05:00
7f1dd245dc - fennec::format refactor. Strings partially implemented. Integers and bools fully implemented. 2025-12-04 20:39:10 -05:00
6d58105734 - Switched back to custom window management, taking another stab
- Refactored lang yet again, `fennec/lang` is now C++ language. `fennec/string` `fennec/filesystem` and `fennec/format` are now independent.
2025-12-04 01:04:36 -05:00
d928d86014 - Some underlying features for RTTI
- Macro for automatically generating this_t
 - Semantics with static_constructor.h, now FENNEC_PRIVATE_STATIC_CONSTRUCTOR for .cpp files and FENNEC_CLASS_STATIC_CONSTRUCTOR for a class in any source file type.
2025-12-03 01:41:30 -05:00
0b76b06a1b - RTTI properties for types for iterators, indexing, and mapping 2025-11-30 20:58:56 -05:00
6f09c3f7fe - Bug fixing for RTTI
- Fixes for declval + separated into own file
 - is_iterable
 - fixes for doxygen generation
2025-11-29 23:43:18 -05:00
fe8c3a4602 - Basic RTTI type data with inheritance. 2025-11-28 12:58:23 -05:00
b9026ec8da - Adjustments to component system design.
- Added indexed parameters to format strings
2025-10-06 12:47:11 -04:00
7b87828f06 - Adjusted Material/Texture/Lighting outline. 2025-09-29 18:39:14 -04:00
8925b3f2f0 - Refactor on component system to support multiple scenes.
- node2d for 2d scenes
2025-09-25 19:30:08 -04:00
f636feb4f1 - Rough First Pass implementation for format.h
- Started 2D Transform Component and relevant math extensions
 - Switched sequence to use pointers instead of arrays
2025-09-23 18:07:54 -04:00
1a9a80e37f - Some last minute performance improvements I noticed. 2025-09-18 21:56:46 -04:00
18c8171847 - More optimization on fennec::sequence. There are areas that could be improved, but the running time is now within margin. It can be revisited later if this data-structure becomes the focus of a performance critical task. 2025-09-18 21:34:29 -04:00
d546519180 - Fixed rb-tree violations 2025-09-18 08:26:57 -04:00
f208141b5b - Minor performance adjustments 2025-09-17 23:35:48 -04:00
52d6c62f76 Merge branch 'main' of https://git.mslockbo.org/fennec-org/fennec 2025-09-17 17:21:05 -04:00
037e2554a1 - Renamed Doxyfile -> Doxyfile.out 2025-09-17 17:19:52 -04:00
8867576a2e Delete doxy/Doxyfile 2025-09-17 21:17:52 +00:00
788f63d092 - Added generated Doxyfile to .gitignore 2025-09-17 17:16:47 -04:00
a35f2a699d - Fixed some missing and erroneous testing logic for containers
- Lots of bug-fixing for containers
 - Performance optimization for containers
2025-09-17 17:13:52 -04:00
80925965d4 - GCC ARM64 Support, i.e. natively compiled with gcc on arm64 2025-09-15 02:35:34 +00:00
f2a45aa913 - Began outlining tokenizer.h and priority_queue.h
- Began outlining sdl implementation
- Added some helper definitions to various classes
- Added contains to string.h and wstring.h
2025-09-13 20:33:53 -04:00
3565bbbc52 - Fixed some logic errors with assertions for wide vs. byte files.
- Changed getline and getwline to use gets instead of the experimental functions. These, in theory, will be slightly faster.
2025-09-11 16:58:32 -04:00
375492ef7b - "Finished" sequence.h, there's more to do, but the basic functionality is there
- bintree.h is implemented according to the needs of sequence.h at present
2025-08-31 14:38:05 -04:00
dbcb50349d - Binary Tree (Partial)
- Sequence (Partial)
2025-08-30 22:11:41 -04:00
992a02db3e - Changed directory structure significantly, moving gfx api implementations to fennec/renderers
- Began new overarching window interface
 - Began outlining renderer interfaces
 - Began a binary tree implementation in bintree.h, this will act as a generalized binary tree, then red-black tree will be implemented on top of it for sequences (ordered sets)
2025-08-28 00:01:54 -04:00
e1eaf97961 - Switched to SDL for main branch, will revisit custom implementation later. 2025-08-23 20:09:53 -04:00
229 changed files with 17226 additions and 6809 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@
/bin/ /bin/
/lib/ /lib/
/doxy/README.md /doxy/README.md
/doxy/Doxyfile.out

View File

@@ -16,23 +16,13 @@
# 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)
# External dependencies should be loaded here
add_subdirectory(external/cpptrace)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_C_STANDARD 23)
set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
add_custom_target(fennec-dependencies
COMMAND ${CMAKE_COMMAND} -E echo "Running dependencies."
COMMENT "Running dependencies."
)
macro(fennec_add_sources) macro(fennec_add_sources)
list(APPEND FENNEC_EXTRA_SOURCES ${ARGN}) list(APPEND FENNEC_SOURCES ${ARGN})
endmacro() endmacro()
macro(fennec_add_definitions) macro(fennec_add_definitions)
@@ -43,6 +33,29 @@ macro(fennec_add_link_libraries)
list(APPEND FENNEC_LINK_LIBRARIES ${ARGN}) list(APPEND FENNEC_LINK_LIBRARIES ${ARGN})
endmacro() 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
add_subdirectory(external/cpptrace)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_C_STANDARD 23)
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")
include("${FENNEC_SOURCE_DIR}/cmake/platform.cmake") include("${FENNEC_SOURCE_DIR}/cmake/platform.cmake")
@@ -62,41 +75,58 @@ 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(
# CORE =================================================================================================================
add_library(fennec STATIC
# 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
# CONTAINERS ===========================================================================================================
# RENDERERS ============================================================================================================
include/fennec/renderers/interface/forward.h
include/fennec/renderers/interface/gfxcontext.h
include/fennec/renderers/interface/gfxsubpass.h
include/fennec/renderers/interface/gfxresourcepool.h
# CONTAINERS ===========================================================================================================
include/fennec/containers/containers.h 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/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/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/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/traversal.h
include/fennec/containers/tuple.h include/fennec/containers/tuple.h
@@ -106,40 +136,60 @@ add_library(fennec STATIC
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/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
# 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 ===============================================================================================================
@@ -183,8 +233,8 @@ 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/_fwd.h
@@ -194,51 +244,94 @@ add_library(fennec STATIC
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
# Filesystem include/fennec/threading/detail/_thread.h
include/fennec/langproc/filesystem/file.h source/langproc/filesystem/file.cpp
include/fennec/langproc/filesystem/path.h source/langproc/filesystem/path.cpp
# 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
# PLATFORM ============================================================================================================= # PLATFORM =============================================================================================================
include/fennec/platform/interface/fwd.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
include/fennec/platform/interface/gfxsurface.h
# GRAPHICS =============================================================================================================
# EXTRA SOURCES ======================================================================================================== )
# add the test suite as a sub-project
add_subdirectory(test)
add_library(fennec STATIC
${FENNEC_SOURCES}
${FENNEC_EXTRA_SOURCES}
) )
add_dependencies(fennec metaprogramming fennec-dependencies) add_dependencies(fennec metaprogramming fennec-dependencies)
target_compile_definitions(fennec PUBLIC target_compile_definitions(fennec PUBLIC ${FENNEC_COMPILE_DEFINITIONS})
${FENNEC_COMPILE_DEFINITIONS} target_compile_options(fennec PRIVATE ${FENNEC_PRIVATE_COMPILE_OPTIONS})
)
# Do not compile base fennec library with c++ stdlib
target_link_options(fennec PRIVATE ${FENNEC_PRIVATE_LINK_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
${FENNEC_LINK_LIBRARIES} ${FENNEC_LINK_LIBRARIES}
${FENNEC_SHARED_LIBRARIES}
cpptrace::cpptrace 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 ==============================================================================================================
@@ -252,7 +345,7 @@ if(DOXYGEN_FOUND)
set(DOXY_EXAMPLES_DIR "${FENNEC_SOURCE_DIR}/examples") 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.")

View File

@@ -49,9 +49,11 @@ Some main areas where the engine strays from the GNU standard includes the follo
* [Section 4.7, Standards for Graphical Interfaces](https://www.gnu.org/prep/standards/html_node/Graphical-Interfaces.html). * [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.

View File

@@ -18,6 +18,8 @@
# 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}")
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")

View File

@@ -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 _GLIBCXX_INCLUDE_NEXT_C_HEADERS=1 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__
)

View File

@@ -16,42 +16,32 @@
# 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) find_package(OpenGL REQUIRED COMPONENTS EGL)
find_package(GLEW REQUIRED)
message(STATUS "EGL Requested") message(STATUS "EGL Requested")
else()
find_package(OpenGL)
find_package(GLEW REQUIRED)
endif()
if(TARGET OpenGL::GL AND TARGET GLEW::GLEW)
message(STATUS "Found OpenGL: ${OPENGL_gl_LIBRARY}")
fennec_add_link_libraries(OpenGL::GL GLEW::GLEW)
fennec_add_definitions(FENNEC_GRAPHICS_OPENGL=1)
else()
message(FATAL_ERROR "No Suitable OpenGL implementation found.")
endif()
if(FENNEC_GRAPHICS_WANT_EGL)
if(NOT TARGET OpenGL::EGL)
message(FATAL_ERROR "EGL Library not found.")
endif()
message(STATUS "Found EGL: ${OPENGL_egl_LIBRARY}")
fennec_add_link_libraries(OpenGL::EGL) fennec_add_link_libraries(OpenGL::EGL)
fennec_add_definitions(FENNEC_GRAPHICS_EGL=1)
fennec_add_sources( fennec_add_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/fwd.h include/fennec/platform/opengl/egl/fwd.h
include/fennec/platform/opengl/egl/error.h
include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp include/fennec/platform/opengl/egl/context.h source/platform/opengl/egl/context.cpp
include/fennec/platform/opengl/egl/surface.h source/platform/opengl/egl/surface.cpp include/fennec/platform/opengl/egl/surface.h source/platform/opengl/egl/surface.cpp
) )
endif() endif()
find_package(GLEW REQUIRED)
if(TARGET OpenGL::GL AND TARGET GLEW::GLEW)
message(STATUS "Found OpenGL: ${OPENGL_LIBRARIES}")
fennec_add_link_libraries(OpenGL::GL GLEW::GLEW)
fennec_add_definitions(FENNEC_GRAPHICS_OPENGL=1)
fennec_add_sources(
include/fennec/renderers/opengl/glcontext.h source/renderers/opengl/glcontext.cpp
)
else()
message(FATAL_ERROR "No Suitable OpenGL implementation found.")
endif()

View File

@@ -21,6 +21,17 @@
# some of this code is based on SDL3's use of wayland-scanner # 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) macro(fennec_wayland_get_header _SCANNER _XML _FILE)
set(_WAYLAND_PROT_H_CODE "${WAYLAND_HEADERS_DIR}/${_FILE}-client-protocols.h") set(_WAYLAND_PROT_H_CODE "${WAYLAND_HEADERS_DIR}/${_FILE}-client-protocols.h")
set(_WAYLAND_PROT_C_CODE "${WAYLAND_SOURCES_DIR}/${_FILE}-client.c") set(_WAYLAND_PROT_C_CODE "${WAYLAND_SOURCES_DIR}/${_FILE}-client.c")
@@ -59,21 +70,18 @@ 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_SCANNER) if( (WAYLAND_CLIENT_INCLUDE_DIR AND WAYLAND_CLIENT_LIBRARY AND WAYLAND_SCANNER)
AND (WAYLAND_EGL_INCLUDE_DIR AND WAYLAND_EGL_LIBRARY)) 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_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_HEADERS_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/headers)
set(WAYLAND_SOURCES_DIR ${FENNEC_SOURCE_DIR}/include/fennec/platform/linux/wayland/lib/sources) set(WAYLAND_SOURCES_DIR ${FENNEC_SOURCE_DIR}/source/platform/linux/wayland/lib/sources)
# Search for base protocol xml # Search for base protocol xml
find_file(WAYLAND_PROTOCOL NAMES wayland.xml PATHS /usr/share/wayland /usr/share/wayland-protocols) fennec_wayland_get_protocol(NAMES "wayland.xml" PATHS "/usr/share/wayland" "/usr/share/wayland-protocols")
file(COPY ${WAYLAND_PROTOCOL} DESTINATION ${WAYLAND_PROTOCOLS_DIR}) fennec_wayland_get_protocol(NAMES "xdg-shell.xml" PATHS "/usr/share/wayland/stable/xdg-shell" "/usr/share/wayland-protocols/stable/xdg-shell")
# search for xdg protocols
find_file(XDG_SHELL_PROTOCOL NAMES xdg-shell.xml PATHS /usr/share/wayland-protocols/stable/xdg-shell)
file(COPY ${XDG_SHELL_PROTOCOL} DESTINATION ${WAYLAND_PROTOCOLS_DIR})
# include sub-dependencies # include sub-dependencies
include("${FENNEC_SOURCE_DIR}/cmake/xkb.cmake") include("${FENNEC_SOURCE_DIR}/cmake/xkb.cmake")
@@ -109,8 +117,13 @@ macro(fennec_check_wayland)
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 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
) )
fennec_add_definitions( fennec_add_definitions(
@@ -118,5 +131,38 @@ macro(fennec_check_wayland)
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}"
) )
# 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()

File diff suppressed because it is too large Load Diff

View File

@@ -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
@@ -944,7 +944,6 @@ WARN_LOGFILE =
# 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,9 @@ 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"
# 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
@@ -2435,8 +2436,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 +2511,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:

67
examples/assert.cpp Normal file
View 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");
}

View File

@@ -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

View File

@@ -371,7 +371,7 @@ class RDTreePrinter:
index += '' index += ''
index += '' index += ''
index += '[{}, {}]'.format(node, i) index += '[{}]'.format(node)
return index, value return index, value
@@ -389,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:
@@ -475,6 +669,9 @@ def register_printers():
pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter) pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter) pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter) 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) pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
return pp return pp

View File

@@ -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)

View File

@@ -34,6 +34,7 @@
#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
{ {
@@ -44,7 +45,7 @@ namespace fennec
/// ///
/// \details /// \details
/// | Property | Value | /// | Property | Value |
/// |:----------:|:----------:| /// |:----------:|:-----------:|
/// | stable | ✅ | /// | stable | ✅ |
/// | dynamic | ⛔ | /// | dynamic | ⛔ |
/// | homogenous | ✅ | /// | homogenous | ✅ |
@@ -64,7 +65,7 @@ struct array {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
public: public:
using value_t = ValueT; ///< Alias for `ValueT` using value_t = ValueT; ///< Alias for \f$ValueT\f$
// Public Members ====================================================================================================== // Public Members ======================================================================================================
@@ -104,7 +105,15 @@ public:
/// @{ /// @{
/// ///
/// \copydetails array::operator[](size_t) const /// \details Returns a reference to the element at \c i
/// \param i index of the element to return
/// \return reference to the requested element
///
/// \par Time-Complexity
/// Constant
///
/// \par Space-Complexity
/// Constant
constexpr value_t& operator[](size_t i) { constexpr value_t& operator[](size_t i) {
assertd(i < ElemV, "Array Out of Bounds"); assertd(i < ElemV, "Array Out of Bounds");
return data[i]; return data[i];
@@ -166,13 +175,13 @@ public:
/// ///
/// \brief Checks if all elements in the arrays are equal /// \brief Checks if all elements in the arrays are equal
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<ElemV>{});
} }
/// ///
/// \brief Checks if any element in the arrays is not equal /// \brief Checks if any element in the arrays is not equal
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<ElemV>{});
} }
/// @} /// @}
@@ -217,8 +226,8 @@ public:
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 ...);
} }
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,203 @@
// =====================================================================================================================
// 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 bitfield.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
/// \tparam N The number of bits in the bitfield
template<size_t N>
struct bitfield {
// Constants ===========================================================================================================
public:
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 ========================================================================================================
public:
///
/// \brief default constructor, initializes with \f$0\f$
constexpr bitfield()
: _bytes() {
}
///
/// \brief boolean array constructor
/// \param arr An array of boolean values resembling each bit.
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 values resembling which bits to set.
template<size_t I>
explicit constexpr bitfield(const size_t (&arr)[I])
: _bytes() {
for (size_t i : arr) {
this->set(i);
}
}
///
/// \brief variadic array constructor
/// \param args A set of indices values resembling which bits to set.
template<typename...ArgsT>
constexpr bitfield(ArgsT&&...args)
: _bytes() {
(this->store(fennec::forward<ArgsT>(args), true), ...);
}
///
/// \param args A set of boolean values resembling each bit.
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
bitfield(const bitfield& bf)
: _bytes(bf._bytes) {
}
///
/// \brief move constructor
/// \param bf bitfield to move
bitfield(bitfield&& bf) noexcept
: _bytes(bf._bytes) {
}
///
/// \brief destructor
constexpr ~bitfield() = default;
///
/// \brief copy assignment
/// \param bf bitfield to copy
/// \returns a reference to self
bitfield& operator=(const bitfield& bf) = default;
///
/// \brief move assignment
/// \param bf bitfield to move
/// \returns a reference to self
bitfield& operator=(bitfield&& bf) noexcept = default;
///
/// \brief test a bit
/// \param i the index of the bit
/// \returns the value stored in the bit as a boolean
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
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
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
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
/// \param v
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
bitfield operator~() const {
bitfield res = *this;
res._inv_helper(make_index_metasequence_t<bytes>{});
return res;
}
private:
array<uint8_t, bytes> _bytes;
template<size_t...I>
void _inv_helper(index_metasequence<I...>) {
((_bytes[I] = ~_bytes[I]), ...);
}
};
}
#endif // FENNEC_CONTAINERS_BITFIELD_H

View File

@@ -44,10 +44,10 @@
/// ///
/// | Symbol | Implemented | Passed | /// | Symbol | Implemented | Passed |
/// |:----------------------------------------------------------------------------|:-----------:|:------:| /// |:----------------------------------------------------------------------------|:-----------:|:------:|
/// | \ref fennec::any "fennec::any" | | | /// | \ref fennec::generic "fennec::generic" `std::any` | 🚧 | 🚧 |
/// | \ref fennec::array "fennec::array" | ✅ | ✅ | /// | \ref fennec::array "fennec::array" | ✅ | ✅ |
/// | \ref fennec::bitset "fennec::bitset" | | | /// | \ref fennec::bitfield "fennec::bitfield" `std::bitset` | 🚧 | 🚧 |
/// | \ref fennec::deque "fennec::deque" | | | /// | \ref fennec::deque "fennec::deque" | 🚧 | 🚧 |
/// | \ref fennec::dynarray "fennec::dynarray" `std::vector` | 🚧 | 🚧 | /// | \ref fennec::dynarray "fennec::dynarray" `std::vector` | 🚧 | 🚧 |
/// | \ref fennec::list "fennec::list" | ✅ | ✅ | /// | \ref fennec::list "fennec::list" | ✅ | ✅ |
/// | \ref fennec::map "fennec::map" `std::unordered_map` | ✅ | ✅ | /// | \ref fennec::map "fennec::map" `std::unordered_map` | ✅ | ✅ |
@@ -58,10 +58,10 @@
/// | \ref fennec::multimap_sequence "fennec::multimap_sequence" `std::multimap` | ⛔ | ⛔ | /// | \ref fennec::multimap_sequence "fennec::multimap_sequence" `std::multimap` | ⛔ | ⛔ |
/// | \ref fennec::optional "fennec::optional" | ✅ | ✅ | /// | \ref fennec::optional "fennec::optional" | ✅ | ✅ |
/// | \ref fennec::pair "fennec::pair" | ✅ | ✅ | /// | \ref fennec::pair "fennec::pair" | ✅ | ✅ |
/// | \ref fennec::sequence "fennec::sequence" `std::set` | | | /// | \ref fennec::sequence "fennec::sequence" `std::set` | 🚧 | 🚧 |
/// | \ref fennec::set "fennec::set" `std::unordered_set` | ✅ | ✅ | /// | \ref fennec::set "fennec::set" `std::unordered_set` | ✅ | ✅ |
/// | \ref fennec::tuple "fennec::tuple" | 🚧 | 🚧 | /// | \ref fennec::tuple "fennec::tuple" | 🚧 | 🚧 |
/// | \ref fennec::variant "fennec::variant" | | | /// | \ref fennec::variant "fennec::variant" | 🚧 | 🚧 |
/// ///
/// ///
/// \section fennec_containers_fennec fennec /// \section fennec_containers_fennec fennec

View File

@@ -84,8 +84,8 @@ private:
}; };
public: public:
using alloc_t = allocator_traits<AllocT>::template rebind<node>; using alloc_t = allocator_traits<AllocT>::template rebind<node>; //!< The underlying allocator type
using elem_t = node*; using elem_t = node*; //!< The underlying element type
// Constructors ======================================================================================================== // Constructors ========================================================================================================
@@ -153,7 +153,7 @@ public:
/// @{ /// @{
/// ///
/// \returns `true` when the deque is empty, `false` otherwise /// \returns \f$true\f$ when the deque is empty, \f$false\f$ otherwise
constexpr bool empty() const { constexpr bool empty() const {
return _size == 0; return _size == 0;
} }

View File

@@ -18,14 +18,14 @@
#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>
@@ -43,7 +43,7 @@ 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>
constexpr _tuple(ArgsT&&... args) constexpr _tuple(ArgsT&&... args)

View File

@@ -31,6 +31,7 @@
#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>
@@ -60,7 +61,7 @@ namespace fennec
/// ///
/// \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: public:
// Definitions ========================================================================================================= // Definitions =========================================================================================================
@@ -100,7 +101,7 @@ public:
} }
/// ///
/// \brief Sized Allocation, initializes a dynarray with `n` elements using the default constructor. /// \brief Sized Allocation, initializes a dynarray with \f$n\f$ elements using the default constructor.
/// \param n The number of elements. /// \param n The number of elements.
explicit constexpr dynarray(size_t n) explicit constexpr dynarray(size_t n)
: _alloc(n) : _alloc(n)
@@ -113,7 +114,7 @@ public:
} }
/// ///
/// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator `alloc` and `n` elements /// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator \f$alloc\f$ and \f$n\f$ elements
/// using the default constructor. /// 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
@@ -127,7 +128,7 @@ public:
} }
/// ///
/// \brief Sized Allocation Alloc Move Constructor, initializes a dynarray with allocator `alloc` and `n` elements /// \brief Sized Allocation Alloc Move Constructor, initializes a dynarray with allocator \f$alloc\f$ and \f$n\f$ elements
/// using the default constructor. /// 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
@@ -141,7 +142,7 @@ public:
} }
/// ///
/// \brief Sized Allocation Copy Constructor, Create an allocation of size `n` elements, with each element /// \brief Sized Allocation Copy Constructor, Create an allocation of size \f$n\f$ elements, with each element
/// constructed using the copy constructor /// constructed using the copy constructor
/// \param n the number of elements /// \param n the number of elements
/// \param val the value to copy /// \param val the value to copy
@@ -156,7 +157,7 @@ public:
// This constructor should not be invokable since moving is a single object operation and will cause undefined // This constructor should not be invokable since moving is a single object operation and will cause undefined
// behaviour when moving to multiple elements // behaviour when moving to multiple elements
constexpr dynarray(size_t n, TypeT&&) = delete; constexpr dynarray(size_t n, TypeT&& val) = delete;
/// ///
/// \brief Emplace Constructor /// \brief Emplace Constructor
@@ -172,6 +173,60 @@ public:
} }
} }
///
/// \brief Array Copy Constructor
/// \tparam N The length of the array, automatically deduced
/// \param arr The array to copy
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
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 `value_t`
/// \tparam OTypeT The other value type
/// \tparam OAlloc The other allocator type
/// \param conv The dynarray to convert
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
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 /// \brief Copy Constructor, uses the copy constructor to copy each element
/// \param arr the dynarray to copy /// \param arr the dynarray to copy
@@ -212,7 +267,7 @@ public:
/// ///
/// \brief Copy Assignment Operator /// \brief Copy Assignment Operator
/// \param arr the array to copy /// \param arr the array to copy
/// \returns A dynarray after having copied each element of `arr` /// \returns A dynarray after having copied each element of \f$arr\f$
constexpr dynarray& operator=(const dynarray& arr) { constexpr dynarray& operator=(const dynarray& arr) {
this->clear(); this->clear();
_alloc.creallocate(_size = arr._size); _alloc.creallocate(_size = arr._size);
@@ -225,7 +280,7 @@ public:
/// ///
/// \brief Move Assignment Operator /// \brief Move Assignment Operator
/// \param arr the array to move /// \param arr the array to move
/// \returns A dynarray after having taken ownership of the contents of `arr` /// \returns A dynarray after having taken ownership of the contents of \f$arr\f$
constexpr dynarray& operator=(dynarray&& arr) noexcept { constexpr dynarray& operator=(dynarray&& arr) noexcept {
this->clear(); this->clear();
_alloc = fennec::move(arr._alloc); _alloc = fennec::move(arr._alloc);
@@ -234,6 +289,36 @@ public:
return *this; 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$
template<size_t N>
constexpr dynarray& operator=(const TypeT (&arr)[N]) {
this->clear();
_alloc.creallocate(_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$
template<size_t N>
constexpr dynarray& operator=(TypeT (&&arr)[N]) {
this->clear();
_alloc.creallocate(_size = N);
for (size_t i = 0; i < _size; ++i) {
fennec::construct(&_alloc[i], fennec::move(arr[i]));
}
return *this;
}
/// @} /// @}
// Properties ========================================================================================================== // Properties ==========================================================================================================
@@ -270,7 +355,7 @@ public:
/// ///
/// \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[](size_t i) { constexpr TypeT& operator[](size_t i) {
assertd(i < _size, "Array Out of Bounds"); assertd(i < _size, "Array Out of Bounds");
return _alloc.data()[i]; return _alloc.data()[i];
@@ -279,7 +364,7 @@ public:
/// ///
/// \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[](size_t i) const { constexpr const TypeT& operator[](size_t i) const {
assertd(i < _size, "Array Out of Bounds"); assertd(i < _size, "Array Out of Bounds");
return _alloc.data()[i]; return _alloc.data()[i];
@@ -432,6 +517,18 @@ public:
} }
} }
///
/// \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
constexpr void resize(size_t n, const TypeT& val) {
_alloc.creallocate(n);
while (_size < n) {
emplace_back(val);
}
}
/// ///
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation. /// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
constexpr void clear() { constexpr void clear() {
@@ -450,22 +547,21 @@ public:
/// @{ /// @{
/// ///
/// \brief "Iterator" Begin Function
/// \returns A pointer to the first element in the dynarray /// \returns A pointer to the first element in the dynarray
constexpr TypeT* begin() { return _alloc.data(); } constexpr TypeT* begin() { return _alloc.data(); }
/// ///
/// \brief "Iterator" End Function /// \brief C++ Iterator Specification `begin()`
/// \returns A const qualified pointer to the first element in the dynarray
constexpr const TypeT* begin() const { return _alloc; }
///
/// \return A pointer to the address after the last element in the dynarray /// \return A pointer to the address after the last element in the dynarray
constexpr TypeT* end() { return begin() + _size; } constexpr TypeT* end() { return begin() + _size; }
/// ///
/// \brief Const "Iterator" Begin Function /// \brief C++ Iterator Specification `end()`
/// \returns A const qualified pointer to the first element in the dynarray
constexpr const TypeT* begin() const { return _alloc; }
///
/// \brief Const "Iterator" End Function
/// \return A const qualified pointer to the address after the last element in the dynarray /// \return A const qualified pointer to the address after the last element in the dynarray
constexpr const TypeT* end() const { return begin() + _size; } constexpr const TypeT* end() const { return begin() + _size; }

View File

@@ -0,0 +1,251 @@
// =====================================================================================================================
// 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 generic.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
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 ========================================================================================================
public:
///
/// \brief Default Constructor
generic()
: _handle(nullptr)
, _manage(nullptr) {
}
///
/// \brief Copy Constructor
/// \param gen The generic object to copy
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
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
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
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 ==========================================================================================================
///
/// \brief runtime type acquisition
/// \returns a runtime type struct referencing the held type
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
bool has_value() const {
return _handle != nullptr;
}
// Assignment ==========================================================================================================
///
/// \brief copy assignment
/// \param gen the generic to copy
/// \returns a reference to self after copying the contents of \f$gen\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$
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$
template<typename T>
generic& operator=(T&& x) {
reset();
_handle = new T(fennec::forward<T>(x));
_manage = _manage_impl<T>();
return *this;
}
// Utility =============================================================================================================
///
/// \brief emplace value
///
/// \details constructs a new value of type \f$T\f$ using \f$args...\f$
/// \tparam T the type to construct
/// \tparam ArgsT the argument types
/// \param args the argument values
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
void reset() {
if (_manage) {
_handle = _manage(op_destroy, _handle);
_manage = nullptr;
}
}
///
/// \brief C++ 11 Swap Specification
/// \param gen the generic to swap with
void swap(generic& gen) noexcept {
fennec::swap(_handle, gen._handle);
fennec::swap(_manage, gen._manage);
}
///
/// \brief cast value
///
/// \details equivalent to `reinterpret_cast`
/// \tparam T The type to cast to
/// \returns The contents of generic after having cast to \f$T\f$
template<typename T, typename U = remove_cvref_t<T>>
T cast() {
return static_cast<T>(*static_cast<U*>(_handle));
}
///
/// \details equivalent to `reinterpret_cast`
/// \tparam T The type to cast to
/// \returns The contents of generic after having cast to \f$T\f$
template<typename T, typename U = remove_cvref_t<T>>
T cast() const {
return static_cast<T>(*static_cast<U*>(_handle));
}
private:
void* _handle;
manager_t _manage;
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

View File

@@ -79,10 +79,10 @@ namespace fennec
/// A directed graph is weakly connected if replacing all of its directed edges with undirected edges would /// 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" /// produce a connected graph. We will call this "disjointed"
/// ///
/// A directed graph is semi-connected if there is a directed path p for `u` &rarr; `v` *or* `v` &rarr; `u` for every /// A directed graph is semi-connected if there is a directed path p for \f$u\f$ &rarr; \f$v\f$ *or* \f$v\f$ &rarr; \f$u\f$ for every
/// pair of vertices `u, v`. We will call this "unilateral" /// pair of vertices `u, v`. We will call this "unilateral"
/// ///
/// A directed graph is strongly-connected if there is a directed path p for `u` &rarr; `v` *and* `v` &rarr; `u` for every pair /// A directed graph is strongly-connected if there is a directed path p for \f$u\f$ &rarr; \f$v\f$ *and* \f$v\f$ &rarr; \f$u\f$ for every pair
/// of vertices `u, v`. We will call this "connected" /// of vertices `u, v`. We will call this "connected"
/// ///
/// \tparam VertexT The type associated with each vertex /// \tparam VertexT The type associated with each vertex
@@ -159,35 +159,35 @@ public:
} }
/// ///
/// \returns `true` when there are no vertices in the graph, `false` otherwise /// \returns \f$true\f$ when there are no vertices in the graph, \f$false\f$ otherwise
constexpr bool empty() const { constexpr bool empty() const {
return num_vertices() == 0; return num_vertices() == 0;
} }
/// ///
/// \brief Checks if there exists an edge `e` that starts from `a` and ends at `b` /// \brief Checks if there exists an edge \f$e\f$ that starts from \f$a\f$ and ends at \f$b\f$
/// \param a The first vertex /// \param a The first vertex
/// \param b The second vertex /// \param b The second vertex
/// \returns `true` if the edge exists, `false` otherwise /// \returns \f$true\f$ if the edge exists, \f$false\f$ otherwise
constexpr bool exists(size_t a, size_t b) const { constexpr bool exists(size_t a, size_t b) const {
return _edge_map[a][b] != nullptr; return _edge_map[a][b] != nullptr;
} }
/// ///
/// \brief Checks if there exists an edge `e0` that starts from `a` and ends at `b` and `e1` that starts from `b` /// \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 `a` /// and ends at \f$a\f$
/// \param a The first vertex /// \param a The first vertex
/// \param b The second vertex /// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise /// \returns \f$true\f$ if both edges exist, \f$false\f$ otherwise
constexpr bool is_symmetric(size_t a, size_t b) const { constexpr bool is_symmetric(size_t a, size_t b) const {
return exists(a, b) and exists(b, a); return exists(a, b) and exists(b, a);
} }
/// ///
/// \brief Checks if there exists an edge `e` between `a` and `b` /// \brief Checks if there exists an edge \f$e\f$ between \f$a\f$ and \f$b\f$
/// \param a The first vertex /// \param a The first vertex
/// \param b The second vertex /// \param b The second vertex
/// \returns `true` if both edges exist, `false` otherwise /// \returns \f$true\f$ if both edges exist, \f$false\f$ otherwise
constexpr bool is_undirected(size_t a, size_t b) const { constexpr bool is_undirected(size_t a, size_t b) const {
const auto* e0 = _edge_map[a][b]; const auto* e0 = _edge_map[a][b];
const auto* e1 = _edge_map[b][a]; const auto* e1 = _edge_map[b][a];
@@ -227,7 +227,7 @@ public:
/// \brief edge Access Operator /// \brief edge Access Operator
/// \param a The id of the first vertex /// \param a The id of the first vertex
/// \param b The id of the second vertex /// \param b The id of the second vertex
/// \returns A pointer to the value stored in the edge, `nullptr` if not found /// \returns A pointer to the value stored in the edge, \f$nullptr\f$ if not found
constexpr edge_t* operator[](size_t a, size_t b) { constexpr edge_t* operator[](size_t a, size_t b) {
if (empty()) { if (empty()) {
return nullptr; return nullptr;
@@ -243,7 +243,7 @@ public:
/// \brief edge Const Access Operator /// \brief edge Const Access Operator
/// \param a The id of the first vertex /// \param a The id of the first vertex
/// \param b The id of the second vertex /// \param b The id of the second vertex
/// \returns A const-qualified pointer to the value stored in the edge, `nullptr` if not found /// \returns A const-qualified pointer to the value stored in the edge, \f$nullptr\f$ if not found
constexpr const edge_t* operator[](size_t a, size_t b) const { constexpr const edge_t* operator[](size_t a, size_t b) const {
if (empty()) { if (empty()) {
return nullptr; return nullptr;
@@ -256,9 +256,9 @@ public:
} }
/// ///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge to `x...` /// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to `x...`
/// \param vertex The id of the vertex /// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` with edges from `vertex` to `x...` /// \returns A list containing all vertices \f$x\f$ with edges from \f$vertex\f$ to `x...`
list<size_t> outgoing(size_t vertex) { list<size_t> outgoing(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || vertex >= _edge_map.size()) { if (empty() || vertex >= _edge_map.size()) {
@@ -271,9 +271,9 @@ public:
} }
/// ///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge from `x...` /// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge from `x...`
/// \param vertex The id of the vertex /// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` with edges from `x...` to `vertex` /// \returns A list containing all vertices \f$x\f$ with edges from `x...` to \f$vertex\f$
list<size_t> incoming(size_t vertex) { list<size_t> incoming(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || vertex >= _edge_map.size()) { if (empty() || vertex >= _edge_map.size()) {
@@ -288,9 +288,9 @@ public:
} }
/// ///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge to and from `x...` /// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to and from `x...`
/// \param vertex The id of the vertex /// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` that have symmetric edges with `vertex` /// \returns A list containing all vertices \f$x\f$ that have symmetric edges with \f$vertex\f$
list<size_t> symmetric(size_t vertex) { list<size_t> symmetric(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || vertex >= _edge_map.size()) { if (empty() || vertex >= _edge_map.size()) {
@@ -305,13 +305,13 @@ public:
} }
/// ///
/// \brief Getter for a list of vertices `x` that `vertex` has an edge to and from `x...` and share the same value /// \brief Getter for a list of vertices \f$x\f$ that \f$vertex\f$ has an edge to and from `x...` and share the same value
/// \details /// \details
/// "Joined" edges may also be referred to as "undirected." A joined, or undirected, edge may be /// "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 /// turned into a directed edge by changing the weight object associated with the edge, or by
/// removing one of the sub-edges. /// removing one of the sub-edges.
/// \param vertex The id of the vertex /// \param vertex The id of the vertex
/// \returns A list containing all vertices `x` that have symmetric edges with `vertex` /// \returns A list containing all vertices \f$x\f$ that have symmetric edges with \f$vertex\f$
list<size_t> undirected(size_t vertex) { list<size_t> undirected(size_t vertex) {
list<size_t> res; list<size_t> res;
if (empty() || vertex >= _edge_map.size()) { if (empty() || vertex >= _edge_map.size()) {
@@ -381,7 +381,7 @@ public:
} }
/// ///
/// \brief Form an edge from vertex `a` to vertex `b` /// \brief Form an edge from vertex \f$a\f$ to vertex \f$b\f$
/// \tparam ArgsT The argument types /// \tparam ArgsT The argument types
/// \param a The first vertex id /// \param a The first vertex id
/// \param b The second vertex id /// \param b The second vertex id
@@ -408,7 +408,7 @@ public:
} }
/// ///
/// \brief Form an undirected edge between vertex `a` and vertex `b` /// \brief Form an undirected edge between vertex \f$a\f$ and vertex \f$b\f$
/// \tparam ArgsT The argument types /// \tparam ArgsT The argument types
/// \param a The first vertex id /// \param a The first vertex id
/// \param b The second vertex id /// \param b The second vertex id
@@ -437,7 +437,7 @@ public:
} }
/// ///
/// \brief Disconnect an edge from vertex `a` to vertex `b` /// \brief Disconnect an edge from vertex \f$a\f$ to vertex \f$b\f$
/// \param a The first vertex id /// \param a The first vertex id
/// \param b The second vertex id /// \param b The second vertex id
constexpr void cut_edge(size_t a, size_t b) { constexpr void cut_edge(size_t a, size_t b) {
@@ -460,7 +460,7 @@ public:
} }
/// ///
/// \brief Disconnect both directed edges between vertices `a` and `b` /// \brief Disconnect both directed edges between vertices \f$a\f$ and \f$b\f$
/// \param a The first vertex id /// \param a The first vertex id
/// \param b The second vertex id /// \param b The second vertex id
constexpr void cut_edge2(size_t a, size_t b) { constexpr void cut_edge2(size_t a, size_t b) {
@@ -476,7 +476,7 @@ public:
} }
/// ///
/// \brief Break *all* edges to and from `n` /// \brief Break *all* edges to and from \f$n\f$
/// \param n The vertex id /// \param n The vertex id
void cut(size_t n) { void cut(size_t n) {
for (const auto it : outgoing(n)) { for (const auto it : outgoing(n)) {

View File

@@ -0,0 +1,63 @@
// =====================================================================================================================
// 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 initializer_list.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -95,7 +95,7 @@ public:
} }
/// ///
/// \brief Copy Constructor, copies all elements in `l` with optimized layout /// \brief Copy Constructor, copies all elements in \f$l\f$ with optimized layout
/// \param l The list to copy /// \param l The list to copy
constexpr list(const list& l) constexpr list(const list& l)
: list() { : list() {
@@ -134,24 +134,26 @@ public:
/// ///
/// \brief Copy Assignment Operator /// \brief Copy Assignment Operator
/// \param l the list to copy /// \param l the list to copy
/// \returns `this` after having copied all elements of `l` /// \returns \f$this\f$ after having copied all elements of \f$l\f$
constexpr list& operator=(const list& l) { constexpr list& operator=(const list& l) {
this->clear(); this->clear();
for (const value_t& it : l) { for (const value_t& it : l) {
this->push_back(it); this->push_back(it);
} }
return *this;
} }
/// ///
/// \brief Move Assignment Operator /// \brief Move Assignment Operator
/// \param l the list to copy /// \param l the list to copy
/// \returns `this` after having taken ownership over the contents of `l` /// \returns \f$this\f$ after having taken ownership over the contents of \f$l\f$
constexpr list& operator=(list&& l) noexcept { constexpr list& operator=(list&& l) noexcept {
this->clear(); this->clear();
_table = fennec::move(l._table); _table = fennec::move(l._table);
_freed = fennec::move(l._freed); _freed = fennec::move(l._freed);
_root = l._root; _last = l._last; _root = l._root; _last = l._last;
_size = l._size; _size = l._size;
return *this;
} }
/// @} /// @}
@@ -164,15 +166,21 @@ public:
/// ///
/// \returns The size of the list in elements. /// \returns The size of the list in elements.
constexpr size_t size() const { return _size; } 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(); } constexpr size_t capacity() const {
return _table.capacity();
}
/// ///
/// \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; } constexpr bool empty() const {
return _root == npos;
}
/// @} /// @}
@@ -185,7 +193,7 @@ public:
/// ///
/// \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$
/// ///
/// \details \f$O(N)\f$ /// \details \f$O(N)\f$
constexpr value_t& operator[](int i) { constexpr value_t& operator[](int i) {
@@ -198,7 +206,7 @@ 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$
/// ///
/// \details \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 {
@@ -248,6 +256,7 @@ public:
/// \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$ /// \details \f$O(1)\f$
constexpr size_t insert(const iterator& it, const value_t& x) { constexpr size_t insert(const iterator& it, const value_t& x) {
@@ -258,6 +267,7 @@ 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$ /// \details \f$O(1)\f$
constexpr size_t insert(const iterator& it, value_t&& x) { constexpr size_t insert(const iterator& it, value_t&& x) {
@@ -268,6 +278,7 @@ 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$ /// \details \f$O(N)\f$
constexpr size_t insert(size_t i, const value_t& x) { constexpr size_t insert(size_t i, const value_t& x) {
@@ -281,6 +292,7 @@ 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$ /// \details \f$O(N)\f$
constexpr size_t insert(size_t i, value_t&& x) { constexpr size_t insert(size_t i, value_t&& x) {
@@ -295,6 +307,7 @@ 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
/// ///
/// \details \f$O(N)\f$ /// \details \f$O(N)\f$
template<typename...ArgsT> template<typename...ArgsT>
@@ -308,6 +321,7 @@ public:
/// ///
/// \brief Push Front Copy /// \brief Push Front Copy
/// \param x Value to copy /// \param x Value to copy
/// \returns The id of the inserted node
constexpr size_t push_front(const value_t& x) { constexpr size_t push_front(const value_t& x) {
return this->_insert(_root, x); return this->_insert(_root, x);
} }
@@ -315,6 +329,7 @@ public:
/// ///
/// \brief Push Front Move /// \brief Push Front Move
/// \param x Value to move /// \param x Value to move
/// \returns The id of the inserted node
constexpr size_t push_front(value_t&& x) { constexpr size_t push_front(value_t&& x) {
return this->_insert(_root, fennec::forward<value_t>(x)); return this->_insert(_root, fennec::forward<value_t>(x));
} }
@@ -323,6 +338,7 @@ 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
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t emplace_front(ArgsT&&...args) { constexpr size_t emplace_front(ArgsT&&...args) {
return this->_insert(_root, fennec::forward<ArgsT>(args)...); return this->_insert(_root, fennec::forward<ArgsT>(args)...);
@@ -331,6 +347,7 @@ public:
/// ///
/// \brief Push Back Copy /// \brief Push Back Copy
/// \param x Value to copy /// \param x Value to copy
/// \returns The id of the inserted node
constexpr size_t push_back(const value_t& x) { constexpr size_t push_back(const value_t& x) {
return this->_insert(npos, x); return this->_insert(npos, x);
} }
@@ -338,6 +355,7 @@ public:
/// ///
/// \brief Push Back Move /// \brief Push Back Move
/// \param x Value to move /// \param x Value to move
/// \returns The id of the inserted node
constexpr size_t push_back(value_t&& x) { constexpr size_t push_back(value_t&& x) {
return this->_insert(npos, fennec::forward<value_t>(x)); return this->_insert(npos, fennec::forward<value_t>(x));
} }
@@ -346,6 +364,7 @@ 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
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t emplace_back(ArgsT&&...args) { constexpr size_t emplace_back(ArgsT&&...args) {
return this->_insert(npos, fennec::forward<ArgsT>(args)...); return this->_insert(npos, fennec::forward<ArgsT>(args)...);
@@ -384,10 +403,10 @@ public:
constexpr void clear() { constexpr void clear() {
size_t i = _root; size_t i = _root;
while (i != npos) { while (i != npos) {
fennec::destruct(_table[i]); fennec::destruct(&_table[i]);
i = this->_next(i); i = this->_next(i);
} }
_table.deallocate(_table); _table.deallocate();
} }
/// @} /// @}
@@ -398,7 +417,6 @@ public:
/// @{ /// @{
/// ///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for the first element in the list /// \returns An iterator for the first element in the list
constexpr iterator begin() { constexpr iterator begin() {
return iterator(this, _root); return iterator(this, _root);
@@ -406,18 +424,17 @@ public:
/// ///
/// \brief C++ Iterator Specification `end()` /// \brief C++ Iterator Specification `end()`
/// \returns An iterator for the end of the list
constexpr iterator end() {
return iterator(this, npos);
}
///
/// \brief Const C++ Iterator Specification `begin()`
/// \returns A const iterator for the first element in the list /// \returns A const iterator for the first element in the list
constexpr const_iterator begin() const { constexpr const_iterator begin() const {
return const_iterator(this, _root); return const_iterator(this, _root);
} }
///
/// \returns An iterator for the end of the list
constexpr iterator end() {
return iterator(this, npos);
}
/// ///
/// \brief Const C++ Iterator Specification `end()` /// \brief Const C++ Iterator Specification `end()`
/// \returns A const iterator for the end of the list /// \returns A const iterator for the end of the list
@@ -428,40 +445,60 @@ public:
/// @} /// @}
/// ///
/// \brief Iterator Class /// \brief C++ Iterator Specification `iterator`
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;
} }
@@ -481,11 +518,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;
@@ -494,24 +536,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;
} }

View File

@@ -56,7 +56,7 @@ namespace fennec
*/ */
/// ///
/// \brief Data Structure defining a mapping of `key` \f$KeyT\f$ to `value` \f$ValueT\f$ /// \brief Data Structure defining a mapping of \f$key\f$ \f$KeyT\f$ to \f$value\f$ \f$ValueT\f$
/// \details /// \details
/// | Property | Value | /// | Property | Value |
/// |:----------:|:----------:| /// |:----------:|:----------:|
@@ -91,15 +91,26 @@ public:
using set_t = set<elem_t, key_hash, key_equals, alloc_t>; ///< The underlying set using set_t = set<elem_t, key_hash, key_equals, alloc_t>; ///< The underlying set
using iterator = set_t::iterator; ///< Iterator type using iterator = set_t::iterator; ///< Iterator type
// We only want to hash the key ///
/// \brief key hash helper
struct key_hash : hash_t { struct key_hash : hash_t {
///
/// \brief C++ 11 Hash Specification `operator()`
/// \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 ///
/// \brief key comparison helper
struct key_equals : equality<KeyT> { struct key_equals : equality<KeyT> {
///
/// \brief C++ 11 Compare Specification `operator()`
/// \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);
} }
@@ -134,7 +145,7 @@ public:
} }
/// ///
/// \returns `true` when there are no elements in the set, `false` otherwise /// \returns \f$true\f$ when there are no elements in the set, \f$false\f$ otherwise
constexpr size_t empty() const { constexpr size_t empty() const {
return _set.size(); return _set.size();
} }
@@ -156,7 +167,7 @@ public:
/// ///
/// \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.
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;
@@ -165,7 +176,7 @@ 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.
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;
@@ -173,9 +184,9 @@ 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.
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)...));
@@ -186,7 +197,7 @@ 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.
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)...));
@@ -210,6 +221,7 @@ public:
/// ///
/// \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
template<typename...ArgsT> template<typename...ArgsT>
constexpr void emplace(const KeyT& key, ArgsT&&...args) { constexpr void emplace(const KeyT& key, ArgsT&&...args) {
@@ -291,7 +303,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);
} }

View File

@@ -1,586 +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 multiset.h
/// \brief A header containing the definition for a set of repeating values
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CONTAINERS_MULTISET_H
#define FENNEC_CONTAINERS_MULTISET_H
// https://programming.guide/robin-hood-hashing.html
#include <fennec/containers/optional.h>
#include <fennec/containers/multiset.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 A Data Structure that defines a set of elements that may repeat
/// \details
/// | Property | Value |
/// |:----------:|:----------:|
/// | stable | ⛔ |
/// | dynamic | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \tparam TypeT The type to contain
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
struct multiset {
// 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:
/// \name Constructors & Destructor
/// @{
///
/// \brief Default Constructor, initializes empty multiset
constexpr multiset()
: _alloc()
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
};
///
/// \brief Hash Copy Constructor, initializes empty multiset with a hash
constexpr multiset(const hash_t& hash)
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Move Constructor, initializes empty multiset with a hash
constexpr multiset(hash_t&& hash) noexcept
: _alloc()
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Copy Constructor, initializes empty multiset with an allocator
constexpr multiset(const alloc_t& alloc)
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Alloc Move Constructor, initializes empty multiset with an allocator
constexpr multiset(alloc_t&& alloc) noexcept
: _alloc(alloc)
, _hash()
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Hash Alloc Copy Constructor, initializes empty multiset with a hash and allocator
constexpr multiset(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 multiset with a hash and allocator
constexpr multiset(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 multiset with a hash and allocator
constexpr multiset(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 multiset with a hash and allocator
constexpr multiset(hash_t&& hash, const alloc_t& alloc) noexcept
: _alloc(alloc)
, _hash(hash)
, _size(0)
, _sumpsl(0)
, _load(default_load) {
}
///
/// \brief Set Copy Constructor
/// \param multiset Set to copy
constexpr multiset(const multiset& multiset)
: _alloc( multiset._alloc)
, _hash( multiset._hash)
, _size( multiset._size)
, _sumpsl( multiset._sumpsl)
, _load( multiset._load) {
}
///
/// \brief Set Move Constructor
/// \param multiset Set to move
constexpr multiset(multiset&& multiset) noexcept
: _alloc(fennec::move( multiset._alloc))
, _hash(fennec::move( multiset._hash))
, _size(fennec::move( multiset._size))
, _sumpsl( multiset._sumpsl)
, _load( multiset._load) {
}
///
/// \brief Destructor, destructs all elements and releases the allocation
constexpr ~multiset() {
for (size_t i = 0; i < capacity(); ++i) {
_alloc[i].value = nullopt;
}
}
/// @}
// Properties ==========================================================================================================
/// \name Properties
/// @{
///
/// \returns Size of the multiset in elements
constexpr size_t size() const {
return _size;
}
///
/// \returns `true` when the set is empty, `false` otherwise
constexpr bool empty() const {
return _size == 0;
}
///
/// \returns Capacity of the multiset in elements
constexpr size_t capacity() const {
return _alloc.capacity();
}
/// @}
// Access ==============================================================================================================
/// \name 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 multiset 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;
}
/// @}
// Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
///
/// \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 ============================================================================================================
/// \name Iteration
/// @{
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const {
iterator it(this, 0);
if (not _alloc[it._i].value) {
++it;
}
return it;
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator representing the end of the set
constexpr iterator end() const {
return iterator(this, npos);
}
/// @}
///
/// \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 multiset* _set;
size_t _i;
friend multiset;
constexpr iterator(const multiset* multiset, size_t i)
: _set(multiset)
, _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 multiset* _set;
size_t _i;
int _psl;
elem_t _value;
friend multiset;
constexpr value_iterator(const multiset* multiset, size_t i, int psl, const elem_t& value)
: _set(multiset)
, _i(i)
, _value(value) {
}
};
// PRIVATE =============================================================================================================
private:
constexpr void _expand() {
multiset cpy; // Create a new multiset
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 static_cast<float>(_size) / capacity() >= _load) { // expand when full
_expand();
}
elem_t value(fennec::forward<ArgsT>(args)...);
size_t i = _hash(value) % capacity(); // Initial search index
int psl = 0;
while (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
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;
float _load;
};
}
#endif // FENNEC_CONTAINERS_SET_H

View File

@@ -47,9 +47,13 @@ struct object_pool {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
public: public:
using value_t = TypeT; using value_t = TypeT; //!< The held object type
using elem_t = optional<TypeT>; using elem_t = optional<value_t>; //!< The element type
using table_t = dynarray<elem_t, AllocT>; 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 =========================================================================================== // Constructors & Destructor ===========================================================================================
@@ -88,13 +92,13 @@ public:
} }
/// ///
/// \returns `true` when there are no objects in the pool, `false` otherwise /// \returns \f$true\f$ when there are no objects in the pool, \f$false\f$ otherwise
constexpr bool empty() const { constexpr bool empty() const {
return size() == 0; return size() == 0;
} }
/// ///
/// \brief Retrieve the next id `i` that would be assigned to an object `o` were it added to the object pool /// \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. /// \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 /// \returns The id of the next inserted node
@@ -116,20 +120,20 @@ public:
/// ///
/// \brief Array Access Operator /// \brief Array Access Operator
/// \param i id of the object /// \param i id of the object
/// \returns a reference to the object with id `i` /// \returns a reference to the object with id \f$i\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 /// \brief Array Const Access Operator
/// \param i id of the object /// \param i id of the object
/// \returns a const-qualified reference to the object with id `i` /// \returns a const-qualified reference to the object with id \f$i\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];
} }
@@ -142,7 +146,7 @@ public:
/// @{ /// @{
/// ///
/// \brief Move Insertion, inserts `x` into the pool /// \brief Move Insertion, inserts \f$x\f$ into the pool
/// \param x the object to move /// \param x the object to move
/// \returns An integer corresponding to the id of the node /// \returns An integer corresponding to the id of the node
constexpr size_t insert(value_t&& x) { constexpr size_t insert(value_t&& x) {
@@ -150,7 +154,7 @@ public:
} }
/// ///
/// \brief Move Insertion, inserts a copy of `x` into the pool /// \brief Move Insertion, inserts a copy of \f$x\f$ into the pool
/// \param x the object to copy /// \param x the object to copy
/// \returns An integer corresponding to the id of the node /// \returns An integer corresponding to the id of the node
constexpr size_t insert(const value_t& x) { constexpr size_t insert(const value_t& x) {
@@ -176,11 +180,246 @@ public:
--_size; --_size;
} }
///
/// \brief Clear the object pool
constexpr void clear() {
for (auto& it : _table) {
it = nullopt;
}
_size = 0;
_freed.clear();
}
/// @} /// @}
// Iterator ============================================================================================================
///
/// \returns an iterator at the start of the object pool
iterator begin() {
return iterator(this, 0);
}
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the start of the object pool
const_iterator begin() const {
return iterator(this, 0);
}
///
/// \returns an iterator at the start of the end of the object pool
iterator end() {
return iterator(this, _size);
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns an iterator at the start of the end of the object pool
const_iterator end() const {
return iterator(this, _size);
}
///
/// \brief C++ Iterator Specification `iterator`
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 `const_iterator`
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() {
@@ -197,7 +436,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;

View File

@@ -38,10 +38,16 @@
namespace fennec namespace fennec
{ {
///
/// \brief struct to represent a \f$null\f$ `optional`
struct nullopt_t {}; struct nullopt_t {};
///
/// \brief value representing a \f$null\f$ `optional`
constexpr nullopt_t nullopt_v = {}; constexpr nullopt_t nullopt_v = {};
///
/// \brief alias for representing a \f$null\f$ `optional`
#define nullopt nullopt_v #define nullopt nullopt_v
/// ///
@@ -52,10 +58,10 @@ struct optional {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
public: public:
using reference_t = T&; using reference_t = T&; //!< reference type
using pointer_t = T*; using pointer_t = T*; //!< pointer type
using const_reference_t = T&; using const_reference_t = T&; //!< const reference type
using const_pointer_t = const T*; using const_pointer_t = const T*; //!< const pointer type
// Constructors ======================================================================================================== // Constructors ========================================================================================================
@@ -116,12 +122,18 @@ public:
opt = nullopt; opt = nullopt;
} }
///
/// \brief Emplace Constructor
/// \tparam ArgsT The argument types
/// \param args The argument values
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
constexpr ~optional() { constexpr ~optional() {
if constexpr(is_fundamental_v<T>) { if constexpr(is_fundamental_v<T>) {
return; return;
@@ -140,13 +152,14 @@ public:
/// @{ /// @{
/// ///
/// \brief Implicit Boolean Check, returns `true` when there is a value contained /// \brief Implicit Boolean Check
/// \returns \f$true\f$ when there is a value contained
constexpr operator bool() const { constexpr operator bool() const {
return _set; return _set;
} }
/// ///
/// \returns `true` when there is no held value, `false` otherwise. /// \returns \f$true\f$ when there is no held value, \f$false\f$ otherwise.
constexpr bool empty() const { constexpr bool empty() const {
return not _set; return not _set;
} }
@@ -160,8 +173,8 @@ public:
/// @{ /// @{
/// ///
/// \brief Fundamental Type Assignment /// \brief Null Assignment
/// \param val The value to set with /// \returns A reference to self
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) {
@@ -176,6 +189,7 @@ 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
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;
@@ -189,6 +203,7 @@ 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
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);
@@ -202,6 +217,7 @@ public:
/// ///
/// \brief Copy Assignment /// \brief Copy Assignment
/// \param opt The optional to copy /// \param opt The optional to copy
/// \returns A reference to self
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;
@@ -220,6 +236,7 @@ public:
/// ///
/// \brief Move Assignment /// \brief Move Assignment
/// \param opt The optional to move /// \param opt The optional to move
/// \returns A reference to self
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;
@@ -244,13 +261,13 @@ public:
/// @{ /// @{
/// ///
/// \returns A pointer to the value, `nullptr` if there is no value /// \returns A pointer to the value, \f$nullptr\f$ if there is no value
constexpr pointer_t operator->() noexcept { constexpr pointer_t operator->() noexcept {
return _set ? &_val : nullptr; return _set ? &_val : nullptr;
} }
/// ///
/// \returns A const-qualified pointer to the value, `nullptr` if there is no value /// \returns A const-qualified pointer to the value, \f$nullptr\f$ if there is no value
constexpr const_pointer_t operator->() const noexcept { constexpr const_pointer_t operator->() const noexcept {
return _set ? &_val : nullptr; return _set ? &_val : nullptr;
} }
@@ -294,9 +311,38 @@ public:
/// \name Modifiers /// \name Modifiers
/// @{ /// @{
///
/// \brief Emplace Assignment, Move overload
/// \param val The object to take ownership of
/// \returns A reference to the held value
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
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
template<typename...ArgsT> template<typename...ArgsT>
constexpr T& emplace(ArgsT&&...args) { constexpr T& emplace(ArgsT&&...args) {
if (_set) { if (_set) {

View File

@@ -95,19 +95,39 @@ 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
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
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
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
constexpr pair& operator=(pair&& pair) {
first = fennec::move(pair.first);
second = fennec::move(pair.second);
return *this;
}
/// @} /// @}
@@ -120,7 +140,7 @@ struct pair {
/// ///
/// \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
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;
} }
@@ -128,7 +148,7 @@ 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
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;
} }
@@ -136,7 +156,7 @@ 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
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);
@@ -145,7 +165,7 @@ 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
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);
@@ -154,7 +174,7 @@ 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
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);
@@ -163,7 +183,7 @@ 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
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);
@@ -172,8 +192,16 @@ struct pair {
/// @} /// @}
}; };
///
/// \brief C++ 11 Hash Specification for `pair<TypeT0, TypeT1>`
/// \tparam TypeT0 The first type of the pair
/// \tparam TypeT1 The second type of the pair
template<typename TypeT0, typename TypeT1> template<typename TypeT0, typename TypeT1>
struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> { struct hash<pair<TypeT0, TypeT1>> : hash<TypeT0>, hash<TypeT1> {
///
/// \brief C++ 11 Hash Specification `operator()`
/// \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 { 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<TypeT0>::operator()(p.first), hash<TypeT0>::operator()(p.first),

View File

@@ -0,0 +1,221 @@
// =====================================================================================================================
// 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 priority_queue.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
/// \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:
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
constexpr priority_queue()
: _size(0) {
}
///
/// \brief destructor
constexpr ~priority_queue() {
while (_size > 0) {
--_size;
fennec::destruct(&_table[_size]);
}
}
// Properties ==========================================================================================================
///
/// \returns the size of the queue
constexpr size_t size() const {
return _size;
}
///
/// \returns the capacity of the underlying allocation
constexpr size_t capacity() const {
return _table.capacity();
}
///
/// \returns \f$true\f$ if the queue holds no elements
constexpr bool empty() const {
return size() == 0;
}
// Access ==============================================================================================================
///
/// \returns the value at the front of the queue
constexpr const value_t& front() const {
return _table[0];
}
// Modifiers ===========================================================================================================
///
/// \brief push a new key into the queue
/// \param key the key to insert
constexpr void push(const value_t& key) {
this->_insert(key);
}
///
/// \param key the key to insert
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
template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief pop the element at the front of the queue
constexpr void pop() {
fennec::swap(_table[0], _table[--_size]);
fennec::destruct(&_table[_size]);
_fix_erase(0);
}
// Members =============================================================================================================
private:
compare_t _compare;
alloc_t _table;
size_t _size;
// Helpers =============================================================================================================
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

View File

@@ -34,6 +34,8 @@
#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
@@ -47,18 +49,18 @@ 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 value_t = TypeT; //!< the held value type
using alloc_t = typename allocator_traits<AllocT>::template rebind<node>; using alloc_t = typename allocator_traits<AllocT>::template rebind<node>; //!< the underlying allocator type
static constexpr size_t root = 0; static constexpr size_t root = 0; //!< the id of the root node
static constexpr size_t npos = -1; static constexpr size_t npos = -1; //!< the id of a null node
protected: 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;
@@ -105,14 +107,14 @@ public:
} }
/// ///
/// \brief Copy Constructor, copies the contents of `tree` /// \brief Copy Constructor, copies the contents of \f$tree\f$
/// \param tree the rdtree to copy /// \param tree the rdtree to copy
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) {
} }
/// ///
/// \brief Move Constructor, takes ownership over the contents of `tree` /// \brief Move Constructor, takes ownership over the contents of \f$tree\f$
/// \param tree the rdtree to move /// \param tree the rdtree to move
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) {
@@ -129,7 +131,7 @@ public:
/// ///
/// \brief Copy Assignment Operator /// \brief Copy Assignment Operator
/// \param rhs the rdtree to copy /// \param rhs the rdtree to copy
/// \returns `this` after copying the contents of `rhs` /// \returns \f$this\f$ after copying the contents of \f$rhs\f$
constexpr rdtree& operator=(const rdtree& rhs) { constexpr rdtree& operator=(const rdtree& rhs) {
for (value_t* it : this->_table) { for (value_t* it : this->_table) {
fennec::destruct(it); fennec::destruct(it);
@@ -143,7 +145,7 @@ public:
/// ///
/// \brief Move Assignment Operator /// \brief Move Assignment Operator
/// \param rhs the rdtree to move /// \param rhs the rdtree to move
/// \returns `this` after taking ownership over the contents of `rhs` /// \returns \f$this\f$ after taking ownership over the contents of \f$rhs\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);
@@ -174,7 +176,7 @@ public:
} }
/// ///
/// \returns `true` when there are no nodes in the tree, `false` otherwise /// \returns \f$true\f$ when there are no nodes in the tree, \f$false\f$ otherwise
constexpr bool empty() const { constexpr bool empty() const {
return _size == 0; return _size == 0;
} }
@@ -192,9 +194,10 @@ 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
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);
@@ -203,9 +206,10 @@ 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
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;
} }
@@ -227,6 +231,7 @@ 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
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;
@@ -251,7 +256,7 @@ 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$
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;
@@ -268,7 +273,7 @@ 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$
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) {
@@ -303,7 +308,7 @@ public:
} }
/// ///
/// \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_id() const { constexpr size_t next_id() const {
size_t i = _size; size_t i = _size;
if (not _freed.empty()) { if (not _freed.empty()) {
@@ -315,34 +320,24 @@ 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) { constexpr value_t& operator[](size_t i) {
auto& it = _table[i].value; return _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
} }
/// ///
/// \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 { constexpr const value_t& operator[](size_t i) const {
const auto& it = _table[i].value; return _table[i].value;
if (it) {
return &*_table[i].value;
} else {
return nullptr;
}
} }
// Insertion & Deletion ================================================================================================ // Insertion & Deletion ================================================================================================
/// ///
/// \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
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) {
@@ -350,8 +345,8 @@ public:
} }
/// ///
/// \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
@@ -360,9 +355,38 @@ public:
} }
/// ///
/// \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
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.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
template<typename...ArgsT> template<typename...ArgsT>
@@ -400,13 +424,15 @@ public:
} }
// Traversal =========================================================================================================== // Traversal ===========================================================================================================
/// ///
/// \brief Traverse the tree using a specified order and visiting functor /// \brief Traverse the tree using a specified order and visiting functor
/// ///
/// \details /// \details
/// The visitor should accept a reference to a value of type `TypeT` and a `size_t` which contains the node's id. /// The visitor should accept a reference to a value of type \f$TypeT\f$ and a `size_t` which contains the node's id.
/// The visitor should return one of the following values in the `fennec::traversal_control_` enum /// 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 OrderT The order with which to traverse the tree.
@@ -414,13 +440,13 @@ public:
/// \param visit The visiting object /// \param visit The visiting object
/// \param i The node to start at /// \param i The node to start at
template<typename OrderT, typename VisitorT> template<typename OrderT, typename VisitorT>
void traverse(VisitorT&& visit, size_t i = root) { constexpr void traverse(VisitorT&& visit, size_t i = root) {
OrderT order; OrderT order;
i = order(*this, i); i = order(*this, i);
while (i != npos) { while (i != npos) {
uint8_t mode = traversal_control_continue; uint8_t mode = traversal_control_continue;
if (_table[i].value) { if (_table[i].value) {
mode = visit(*_table[i].value, i); mode = visit(_table[i].value, i);
} }
if (mode == traversal_control_break) { if (mode == traversal_control_break) {
break; break;
@@ -429,16 +455,76 @@ public:
} }
} }
struct pre_order { ///
list<size_t> visit; /// \brief Traverser pattern for breadth-first traversal
size_t head; struct breadth_first {
size_t operator()(const rdtree&, size_t start) { ///
/// \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.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;
} }
@@ -463,18 +549,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;
} }
@@ -501,18 +601,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;
} }
@@ -537,10 +651,14 @@ public:
return node; return node;
} }
private:
list<size_t> visit;
size_t head;
}; };
protected: private:
allocation<node, alloc_t> _table; allocation<node, alloc_t> _table;
list<size_t> _freed; list<size_t> _freed;
size_t _size; size_t _size;

View File

@@ -0,0 +1,858 @@
// =====================================================================================================================
// 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 sequence.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 | ✅ |
/// | homogenous | ✅ |
/// | 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$ |
///
/// \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:
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
constexpr sequence()
: _root(nullptr), _size(0) {
}
///
/// \brief Move Constructor, takes ownership of a sequence
constexpr sequence(sequence&&) noexcept = default;
///
/// \brief Copy Constructor, copies a sequence
constexpr sequence(const sequence&) = default;
///
/// \brief Default Destructor, destructs elements *in-order*
constexpr ~sequence() {
this->clear();
}
/// @}
// Search ==============================================================================================================
public:
/// \name Search
/// @{
///
/// \brief Value Find Function, finds the iterator position for \f$val\f$, otherwise returns `end()`
/// \param val The value to find
/// \returns An iterator at the value
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
constexpr size_t size() const {
return _size;
}
///
/// \returns \f$true\f$ when there are no elements in the sequence, \f$false\f$ otherwise.
constexpr bool 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
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
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
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
constexpr void erase(const value_t& val) {
_erase(find(val)._node);
}
///
/// \brief Destructs all elements, *in-order*, contained in the sequence
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
constexpr iterator begin() {
return sequence::iterator(this, _root);
}
///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator at the smallest element in the sequence
constexpr const_iterator begin() const {
return sequence::const_iterator(this, _root);
}
///
/// \returns An iterator after the largest element in the sequence
constexpr iterator end() {
return sequence::iterator(this, _root, nullptr);
}
///
/// \brief Const C++ Iterator Specification `end()`
/// \returns An iterator after the largest element in the sequence
constexpr const_iterator end() const {
return sequence::const_iterator(this, _root, nullptr);
}
///
/// \brief C++ Iterator Specification `iterator`
class iterator {
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) {
}
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.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;
}
friend struct sequence;
};
///
/// \brief C++ Iterator Specification `iterator`
class const_iterator {
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) {
}
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.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;
}
friend struct sequence;
};
// Fields ==============================================================================================================
private:
alloc_t _alloc;
node _root;
compare_t _compare;
size_t _size;
// 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

View File

@@ -69,14 +69,13 @@ struct set {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
public: public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>; using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>; //!< the allocator type
using hash_t = Hash; using hash_t = Hash; //!< the hash type
using equal_t = Equals; using equal_t = Equals; //!< the equality type
using elem_t = TypeT; using elem_t = TypeT; //!< the element type
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 { struct node {
@@ -87,6 +86,8 @@ private:
constexpr ~node() = default; constexpr ~node() = default;
}; };
static constexpr size_t npos = -1;
// Constructors ======================================================================================================== // Constructors ========================================================================================================
public: public:
@@ -182,7 +183,7 @@ public:
} }
/// ///
/// \returns `true` when the set is empty, `false` otherwise /// \returns \f$true\f$ when the set is empty, \f$false\f$ otherwise
constexpr bool empty() const { constexpr bool empty() const {
return _size == 0; return _size == 0;
} }
@@ -252,7 +253,7 @@ 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
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();
} }
@@ -260,7 +261,7 @@ 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.
constexpr elem_t* at(const iterator& it) { constexpr elem_t* at(const iterator& it) {
if (it == end()) { if (it == end()) {
@@ -275,7 +276,7 @@ public:
/// ///
/// \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.
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 _alloc[it._i].value) return nullptr;
return &*_alloc[it._i].value; return &*_alloc[it._i].value;
@@ -291,6 +292,7 @@ public:
/// ///
/// \brief Move Insertion /// \brief Move Insertion
/// \param val Value to insert /// \param val Value to insert
/// \returns An iterator at the held value
constexpr iterator insert(elem_t&& val) { constexpr iterator insert(elem_t&& val) {
return this->_insert(fennec::forward<elem_t>(val)); return this->_insert(fennec::forward<elem_t>(val));
} }
@@ -298,6 +300,7 @@ public:
/// ///
/// \brief Copy Insertion /// \brief Copy Insertion
/// \param val Value to insert /// \param val Value to insert
/// \returns An iterator at the held value
constexpr iterator insert(const elem_t& val) { constexpr iterator insert(const elem_t& val) {
return this->_insert(val); return this->_insert(val);
} }
@@ -306,6 +309,7 @@ public:
/// \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
template<typename...ArgsT> template<typename...ArgsT>
constexpr iterator emplace(ArgsT&&...args) { constexpr iterator emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(args)...); return this->_insert(fennec::forward<ArgsT>(args)...);
@@ -330,7 +334,7 @@ public:
while (_alloc[i = (i + 1) % capacity()].value) { while (_alloc[i = (i + 1) % capacity()].value) {
if (_alloc[i].psl == 0) break; if (_alloc[i].psl == 0) break;
fennec::swap(_alloc[i - 1].value, _alloc[i].value); fennec::swap(_alloc[p].value, _alloc[i].value);
--_alloc[p].psl, --_sumpsl; --_alloc[p].psl, --_sumpsl;
p = i; p = i;
} }
@@ -344,11 +348,9 @@ public:
} }
/// ///
/// \brief /// \brief Clear all elements from the set, destructing them
constexpr void clear() { constexpr void clear() {
for (size_t i = 0; i < _alloc.capacity(); ++i) { _alloc.clear();
}
} }
/// @} /// @}
@@ -360,6 +362,7 @@ public:
/// @{ /// @{
/// ///
/// \brief C++ Iterator Specification `begin()`
/// \returns An iterator for all elements of the set in no particular order /// \returns An iterator for all elements of the set in no particular order
constexpr iterator begin() const { constexpr iterator begin() const {
iterator it(this, 0); iterator it(this, 0);
@@ -370,6 +373,7 @@ public:
} }
/// ///
/// \brief C++ Iterator Specification `end()`
/// \returns An iterator representing the end of the set /// \returns An iterator representing the end of the set
constexpr iterator end() const { constexpr iterator end() const {
return iterator(this, npos); return iterator(this, npos);
@@ -378,57 +382,79 @@ public:
/// @} /// @}
/// ///
/// \brief Class for Iterating the Set /// \brief C++ Iterator Specification `iterator`
class iterator { class iterator {
public: public:
constexpr iterator(const set* set, size_t i) ///
: _set(set) /// \brief destructor
, _i(i) {
}
constexpr ~iterator() { constexpr ~iterator() {
_set = nullptr; _set = nullptr;
} }
// prefix operator ///
constexpr friend iterator& operator++(iterator& rhs) { /// \brief prefix increment operator
while (++rhs._i < rhs._set->capacity()) { /// \param it the iterator to increment
if (rhs._set->_alloc[rhs._i].value) { /// \returns \f$it\f$ after having moved to the next element in the list
return rhs; constexpr friend iterator& operator++(iterator& it) {
while (++it._i < it._set->capacity()) {
if (it._set->_alloc[it._i].value) {
return it;
} }
} }
rhs._i = npos; it._i = npos;
return rhs; return it;
} }
constexpr friend iterator operator++(iterator& lhs, int) { ///
iterator prev = lhs; /// \brief postfix increment operator
++lhs; /// \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; return prev;
} }
///
/// \brief dereference operator
/// \returns a reference to the value pointed by the iterator
constexpr const elem_t& operator*() const { constexpr const elem_t& operator*() const {
return *_set->_alloc[_i].value; return *_set->_alloc[_i].value;
} }
///
/// \brief pointer access operator
/// \returns a pointer to the value pointed by the iterator
constexpr const elem_t* operator->() const { constexpr const elem_t* operator->() const {
if (not _set->_alloc[_i].value) return nullptr; if (not _set->_alloc[_i].value) return nullptr;
return &*_set->_alloc[_i].value; return &*_set->_alloc[_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 { constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i; 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 { constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i; return _set != it._set or _i != it._i;
} }
constexpr size_t index() const { return _i; }
private: private:
const set* _set; const set* _set;
size_t _i; size_t _i;
constexpr iterator(const set* set, size_t i)
: _set(set)
, _i(i) {
}
friend set; friend set;
}; };

View File

@@ -43,7 +43,7 @@ namespace fennec
/// \brief Tuple, holds a collection of values of different types /// \brief Tuple, holds a collection of values of different types
/// \details /// \details
/// | Property | Value | /// | Property | Value |
/// |:----------:|:----------:| /// |:----------:|:-----------:|
/// | stable | ⛔ | /// | stable | ⛔ |
/// | dynamic | ✅ | /// | dynamic | ✅ |
/// | homogenous | ⛔ | /// | homogenous | ⛔ |
@@ -59,7 +59,12 @@ namespace fennec
/// \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>;
@@ -67,6 +72,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>;
@@ -76,28 +86,43 @@ 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...>; 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
///
/// \brief tuple constructor
/// \tparam ArgsT The element types
/// \param args The arguments to initialize the tuple with
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
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
tuple(tuple&& mov)
: base_t(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...>;

View File

@@ -31,4 +31,243 @@
#ifndef FENNEC_CONTAINERS_VARIANT_H #ifndef FENNEC_CONTAINERS_VARIANT_H
#define 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 `TypesT...`
/// \tparam TypesT The types to hold in the variant
template<typename...TypesT>
struct variant {
// Assertions ==========================================================================================================
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
);
// Typedefs & 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 ========================================================================================================
///
/// \brief Default Constructor, constructs the first type in `TypesT...` that is default constructible
variant()
: _bytes {}
, _type(nulltype) {
using construct_t = search_element_t<is_default_constructible, TypesT...>;
fennec::construct<construct_t>(_handle);
}
///
/// \brief Conversion Constructor, constructs the type in `TypesT...` 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
template<typename T>
variant(T&& t)
: _bytes {}
, _type() {
using same_t = search_element_args<is_same, type_sequence<T>, TypesT...>;
using convert_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
using construct_t = conditional_t<is_void_v<same_t>, convert_t, convert_t>;
fennec::construct<construct_t>(_handle, fennec::forward<T>(t));
}
///
/// \brief Emplace Constructor, constructs the first type in `TypesT...` that is constructible with `ArgsT...`
/// \tparam ArgsT The arguments of the constructor
/// \param args The argument values
template<typename T, typename...ArgsT>
variant(type_identity<T>, ArgsT&&...args)
: _bytes{}
, _type(nulltype) {
static_assert(contains_element_v<T, TypesT...>, "T must be in TypesT...");
fennec::construct<T>(_handle, fennec::forward<ArgsT>(args)...);
_type = find_element_v<T>;
}
///
/// \brief Copy Constructor
/// \param v The variant to copy
variant(const variant& v)
: _bytes {}
, _type(nulltype) {
if (v._type == nulltype) {
return;
}
((v._type == find_element_v<TypesT, TypesT...> ?
fennec::construct<TypesT>(_handle, v.get<TypesT>()) :
(0)
), ...);
_type = v._type;
}
///
/// \brief Move Constructor
/// \param v The variant to move
variant(variant&& v) noexcept
: _bytes {}
, _type() {
if (v._type == nulltype) {
return;
}
((v._type == find_element_v<TypesT, TypesT...> ?
fennec::construct<TypesT>(_handle, fennec::move(v.get<TypesT>())) :
(0)
), ...);
_type = v._type;
}
///
/// \brief Destructor, if a type is held, destruct it.
~variant() {
_clear();
}
// 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$
template<typename T>
variant& operator=(T&& t) {
// First, check if \f$T\f$ 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...>) {
*static_cast<type_t*>(_handle) = fennec::forward<T>(t);
} else {
_clear();
fennec::construct<type_t>(_handle, 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...> ?
(*static_cast<TypesT*>(_handle) = fennec::forward<T>(t), assigned = true) :
(0)
), ...);
}
if (assigned) {
return *this;
}
// Otherwise, destruct, then construct
_clear();
using construct_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
fennec::construct<construct_t>(_handle, 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
template<typename T, typename...ArgsT> requires(contains_element_v<T, TypesT...>)
void emplace(ArgsT&&...args) {
_clear();
fennec::construct<T>(_handle, fennec::forward<ArgsT>(args)...);
}
///
/// \brief deduced emplace function
/// \tparam ArgsT the argument types
/// \param args the argument values
template<size_t I, typename...ArgsT>
void emplace(ArgsT&&...args) {
using type_t = nth_element_t<I, TypesT...>;
_clear();
fennec::construct<type_t>(fennec::forward<ArgsT>(args)...);
}
// 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$
template<typename T> requires(contains_element_v<T, TypesT...>)
T& get() {
return *static_cast<T*>(_handle);
}
///
/// \tparam T the type to interpret as
/// \returns The value interpreted as \f$T\f$
template<typename T> requires(contains_element_v<T, TypesT...>)
const T& get() const {
return *static_cast<T*>(_handle);
}
///
/// \tparam T the type to interpret as
/// \returns The value interpreted as \f$T\f$
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
T& get() {
return *static_cast<T*>(_handle);
}
///
/// \tparam T the type to interpret as
/// \returns The value interpreted as \f$T\f$
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
const T& get() const {
return *static_cast<T*>(_handle);
}
private:
union {
byte_t _bytes[size];
void* _handle;
};
size_t _type;
void _clear() {
if (_type == nulltype) {
return;
}
((_type == find_element_v<TypesT, TypesT...> ?
fennec::destruct<TypesT>(_handle) :
(0)
), ...);
_type = nulltype;
}
};
}
#endif // FENNEC_CONTAINERS_VARIANT_H #endif // FENNEC_CONTAINERS_VARIANT_H

View File

@@ -69,4 +69,15 @@
#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

View File

@@ -20,40 +20,66 @@
#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 {
public: public:
virtual ~event_listener() = default; virtual ~event_listener() = default;
///
/// \brief event handler callback
/// \param event the event to handle
virtual void handle_event(event* event) = 0; virtual void handle_event(event* event) = 0;
}; };
///
/// \brief Main event interface, includes static methods for registering listeners and dispatching events
struct event { struct event {
const uint64_t type; virtual ~event() = default;
event() = delete;
template<typename EventT>
event() : type(typeuuid<EventT>()) { }
///
/// \brief Registers a listener for the event type
/// \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, typeuuid<EventT, event>());
} }
template<typename EventT> ///
static void dispatch(EventT* event) { /// \brief Add a listener for an event type of \f$type\f$
dispatch(event); /// \param listener the listener to add
/// \param type the event type to listen for
static void add_listener(event_listener* listener, uint64_t type);
///
/// \brief removes a listener from the event system
/// \param listener the listener to remove
static void remove_listener(event_listener* listener);
template<typename EventT, typename...ArgsT>
static void dispatch_immediate(ArgsT&&...args) {
} }
static void add_listener(event_listener* listener, uint64_t type); template<typename EventT, typename...ArgsT>
static void remove_listener(event_listener* listener); static void dispatch_next_tick(ArgsT&&...args) {
static void dispatch(event* event);
}
#ifndef FENNEC_DOXYGEN
FENNEC_RTTI_CLASS_ENABLE() {
}
#endif
}; };
} }

View File

@@ -0,0 +1,102 @@
// =====================================================================================================================
// 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 logger.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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> {
public:
///
/// \brief default constructor
logger();
~logger();
///
/// \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:
file _logfile;
file* _cout;
};
}
#endif // FENNEC_CORE_LOGGER_H

View File

@@ -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
{ {

View File

@@ -0,0 +1,52 @@
// =====================================================================================================================
// 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 version.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_CORE_VERSION_H
#define FENNEC_CORE_VERSION_H
#include <fennec/lang/types.h>
#include <fennec/string/string.h>
namespace fennec
{
///
/// \brief simple version struct for drivers and systems
struct version {
uint32_t major; //!< the major version
uint32_t minor; //!< the minor version
uint32_t patch; //!< the patch
string str; //!< string representation of the version
};
}
#endif // FENNEC_CORE_VERSION_H

View File

@@ -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

View File

@@ -16,14 +16,15 @@
// 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_IO_FILE_H #ifndef FENNEC_FILESYSTEM_FILE_H
#define FENNEC_LANGPROC_IO_FILE_H #define FENNEC_FILESYSTEM_FILE_H
#include <fennec/langproc/filesystem/path.h> #include <fennec/filesystem/path.h>
#include <fennec/format/format.h>
#include <fennec/langproc/strings/cstring.h> #include <fennec/string/cstring.h>
#include <fennec/langproc/strings/string.h> #include <fennec/string/string.h>
#include <fennec/langproc/strings/wstring.h> #include <fennec/string/wstring.h>
namespace fennec namespace fennec
{ {
@@ -38,10 +39,10 @@ namespace fennec
/// <tr><th style="vertical-align: top">Flags /// <tr><th style="vertical-align: top">Flags
/// <th style="vertical-align: top">Description /// <th style="vertical-align: top">Description
/// ///
/// <tr><td style="vertical-align: top">`read` /// <tr><td style="vertical-align: top">\f$read\f$
/// <td style="vertical-align: top">Opens file as read-only, reading from start /// <td style="vertical-align: top">Opens file as read-only, reading from start
/// ///
/// <tr><td style="vertical-align: top">`write` /// <tr><td style="vertical-align: top">\f$write\f$
/// <td style="vertical-align: top">Opens file as write-only, writing to end /// <td style="vertical-align: top">Opens file as write-only, writing to end
/// ///
/// <tr><td style="vertical-align: top">`read | write` /// <tr><td style="vertical-align: top">`read | write`
@@ -108,6 +109,33 @@ public:
/// \brief default constructor, initializes an empty stream /// \brief default constructor, initializes an empty stream
file(); file();
///
/// \brief path constructor, initializes a stream pointing to \f$mode\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);
}
///
/// \brief path constructor, initializes a stream pointing to \f$mode\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, initializes a stream pointing to \f$mode\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 default destructor, cleans up an open stream /// \brief default destructor, cleans up an open stream
~file(); ~file();
@@ -117,6 +145,9 @@ public:
/// \param file the stream to take ownership of /// \param file the stream to take ownership of
file(file&& file) noexcept; file(file&& file) noexcept;
///
/// \brief move assignment
/// \param file the stream to take ownership of
file& operator=(file&& file) noexcept; file& operator=(file&& file) noexcept;
// don't allow copying streams // don't allow copying streams
@@ -242,69 +273,184 @@ public:
// File Positioning ==================================================================================================== // File Positioning ====================================================================================================
///
/// \returns the position index in the stream
size_t get_pos() const; 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); 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(); bool rewind();
///
/// \returns \f$true\f$ if the stream has reached the end of the file, \f$false\f$ otherwise
bool eof() const; bool eof() const;
// Binary Read Operations ============================================================================================== // Binary Read Operations ==============================================================================================
char getc(); ///
wchar_t getwc(); /// \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); 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> template<typename T>
size_t read(T* data, size_t n) { size_t read(T* data, size_t n) {
return read(static_cast<void*>(data), sizeof(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> template<typename T, size_t n>
size_t read(T (&data)[n]) { size_t read(T (&data)[n]) {
return read(static_cast<void*>(data), sizeof(T), n); return read(static_cast<void*>(data), sizeof(T), n);
} }
string getline();
wstring getwline();
// Binary Write Operations ============================================================================================= // 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); 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); 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); 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> template<size_t n>
size_t write(const char (&data)[n]) { size_t write(const char (&data)[n]) {
return write(data, sizeof(char), n - 1); 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> template<size_t n>
size_t write(const wchar_t (&data)[n]) { size_t write(const wchar_t (&data)[n]) {
return write(data, sizeof(wchar_t), n - 1); 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> template<typename T>
size_t write(const T* data, size_t n) { size_t write(const T* data, size_t n) {
return write(static_cast<const void*>(data), sizeof(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> template<typename T, size_t n>
size_t write(const T (&data)[n]) { size_t write(const T (&data)[n]) {
return write(static_cast<const void*>(data), sizeof(T), n); return write(static_cast<const void*>(data), sizeof(T), n);
} }
// Read Operations =====================================================================================================
///
/// \returns the character read at the current position in the stream
char getc();
///
/// \returns the wide character read at the current position in the stream
wchar_t getwc();
///
/// \returns a string containing all characters from the current position in the stream to the next newline character
string getline();
///
/// \returns a string containing all characters from the current position in the stream to the next newline character
wstring getwline();
// Printing Operations ================================================================================================= // Printing 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 ====================================================================================================== // Error Handling ======================================================================================================
///
/// \returns the current error state of the file
const char* get_error() const { return _error; } const char* get_error() const { return _error; }
///
/// \brief clears the errored state
void clear_error() { _error = nullptr; } void clear_error() { _error = nullptr; }
private: private:
@@ -316,4 +462,4 @@ private:
} }
#endif // FENNEC_LANGPROC_IO_FILE_H #endif // FENNEC_FILESYSTEM_FILE_H

View File

@@ -16,10 +16,11 @@
// 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_IO_PATH_H #ifndef FENNEC_FILESYSTEM_PATH_H
#define FENNEC_LANGPROC_IO_PATH_H #define FENNEC_FILESYSTEM_PATH_H
#include <fennec/langproc/strings/string.h> #include <fennec/filesystem/path.h>
#include <fennec/string/string.h>
namespace fennec namespace fennec
{ {
@@ -32,6 +33,12 @@ namespace fennec
struct path struct path
{ {
public: public:
// Definitions =========================================================================================================
class iterator;
friend class iterator;
// Static Functions ==================================================================================================== // Static Functions ====================================================================================================
/// \brief Get the current working directory /// \brief Get the current working directory
@@ -88,7 +95,7 @@ public:
/// ///
/// \brief C-String Assignment Operator /// \brief C-String Assignment Operator
/// \param str the cstring to assign /// \param str the cstring to assign
/// \returns a reference to `this` after assigning `p` /// \returns a reference to \f$this\f$ after assigning \f$p\f$
template<size_t n> template<size_t n>
path& operator=(const char (&str)[n]) { path& operator=(const char (&str)[n]) {
_str = str; _str = str;
@@ -98,7 +105,7 @@ public:
/// ///
/// \brief C-String Assignment Operator /// \brief C-String Assignment Operator
/// \param p the cstring to assign /// \param p the cstring to assign
/// \returns a reference to `this` after assigning `p` /// \returns a reference to \f$this\f$ after assigning \f$p\f$
path& operator=(const cstring& p) { path& operator=(const cstring& p) {
_str = p; _str = p;
return *this; return *this;
@@ -107,7 +114,7 @@ public:
/// ///
/// \brief String Assignment Operator /// \brief String Assignment Operator
/// \param p the cstring to assign /// \param p the cstring to assign
/// \returns a reference to `this` after assigning `p` /// \returns a reference to \f$this\f$ after assigning \f$p\f$
path& operator=(const string& p) { path& operator=(const string& p) {
_str = p; _str = p;
return *this; return *this;
@@ -116,7 +123,7 @@ public:
/// ///
/// \brief Path Copy Assignment Operator /// \brief Path Copy Assignment Operator
/// \param p the path to copy /// \param p the path to copy
/// \returns a reference to `this` after copying `p` /// \returns a reference to \f$this\f$ after copying \f$p\f$
path& operator=(const path& p) { path& operator=(const path& p) {
_str = p._str; _str = p._str;
return *this; return *this;
@@ -125,7 +132,7 @@ public:
/// ///
/// \brief Path Move Assignment Operator /// \brief Path Move Assignment Operator
/// \param p the path to take ownership of /// \param p the path to take ownership of
/// \returns a reference to `this` after taking ownership of `p` /// \returns a reference to \f$this\f$ after taking ownership of \f$p\f$
path& operator=(path&& p) noexcept { path& operator=(path&& p) noexcept {
_str = move(p._str); _str = move(p._str);
return *this; return *this;
@@ -135,28 +142,55 @@ public:
// Append Operators ==================================================================================================== // Append Operators ====================================================================================================
/// ///
/// \brief /// \brief path append operator
/// \param str /// \param str the filename to append
/// \return /// \returns a path containing the current path followed by \f$str\f$
path operator/(const cstring& str) const { path operator/(const cstring& str) const {
return path(_str + '/' + str); 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 { path operator/(const string& str) const {
return path(_str + '/' + str); 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 { path operator/(const path& p) const {
return path(_str + '/' + p._str); return path(_str + '/' + p._str);
} }
///
/// \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 { bool operator==(const path& p) const {
return _str == p._str; return _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; } const string& str() const { return _str; }
///
/// \returns the underlying C-Style string representation
const char* cstr() const { return _str.cstr(); } const char* cstr() const { return _str.cstr(); }
///
/// \returns \f$true\f$ if the path is empty or points to root
bool empty() { bool empty() {
size_t size = _str.size(); size_t size = _str.size();
if (size == 0) return true; if (size == 0) return true;
@@ -167,6 +201,8 @@ public:
#endif #endif
} }
///
/// \returns a copy of the path with the filename removed
path parent() const { path parent() const {
#ifdef FENNEC_PLATFORM_WINDOWS #ifdef FENNEC_PLATFORM_WINDOWS
size_t start = _str.size() - 1; size_t start = _str.size() - 1;
@@ -191,6 +227,9 @@ public:
#endif #endif
} }
///
/// \brief absolute path
/// \returns a copy of the path with relative paths resolved
path absolute() const { path absolute() const {
path parse = *this; path parse = *this;
path working; working._str.resize(0); path working; working._str.resize(0);
@@ -238,10 +277,119 @@ public:
return working; return working;
} }
// Iterator ============================================================================================================
///
/// \brief C++ Iterator Specification `begin()`
/// \returns an iterator at the first filename in the path
iterator begin() const {
return iterator(this, 0);
}
///
/// \brief C++ Iterator Specification `end()`
/// \returns an iterator to the end of the path
iterator end() const {
return iterator(this, _str.size());
}
///
/// \brief C++ Iterator Specification `iterator`
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: private:
string _str; string _str;
}; };
} }
#endif // FENNEC_LANGPROC_IO_PATH_H #endif // FENNEC_FILESYSTEM_PATH_H

View File

@@ -0,0 +1,201 @@
// =====================================================================================================================
// 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 charconv.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -0,0 +1,164 @@
// =====================================================================================================================
// 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 _format.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#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

View File

@@ -0,0 +1,383 @@
// =====================================================================================================================
// 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 format.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -0,0 +1,54 @@
// =====================================================================================================================
// 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 format_arg.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -0,0 +1,301 @@
// =====================================================================================================================
// 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 formatter.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -0,0 +1,113 @@
// =====================================================================================================================
// 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 tokenizer.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -46,18 +46,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,21 +68,32 @@
#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* expression, const char* file, int line, const char* function, const char* desc, bool halt); void _assert_impl(const char* expression, const char* file, int line, const char* function, const char* desc, bool 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_impl(#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_impl(#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) #define assertd(expression, description)
#else #else

View File

@@ -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 `T0` is less than a value of type `T1`
/// \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 `T0` is less than or equal to a value of type `T1`
/// \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 `T0` is greater than a value of type `T1`
/// \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 `T0` is greater than or equal to a value of type `T1`
/// \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;
} }
}; };

View File

@@ -89,23 +89,23 @@ 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 `DetectT<ArgsT...>` 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 `DetectT<ArgsT...>` if it exists, otherwise selects \f$DefaultT\f$ The chosen type is stored in `detect::type` and
/// a boolean value is stored in `detect::is_detected` representing whether `DetectT<ArgsT...>` is found. /// a boolean value is stored in `detect::is_detected` representing whether `DetectT<ArgsT...>` is found.
/// \tparam DefaultT Default type /// \tparam DefaultT Default type
/// \tparam DetectT Type to detect /// \tparam DetectT Type to detect
@@ -113,8 +113,8 @@ struct conditional<false, T, F> : type_identity<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
}; };
/// ///
@@ -123,6 +123,7 @@ template<typename DefaultT, template<typename...> typename DetectT, typename...A
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,
@@ -156,9 +158,11 @@ struct enable_if {};
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
} }

View File

@@ -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

View File

@@ -0,0 +1,52 @@
// =====================================================================================================================
// 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 declval.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -0,0 +1,46 @@
// =====================================================================================================================
// 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 _declval.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#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

View File

@@ -16,25 +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/>.
// ===================================================================================================================== // =====================================================================================================================
#ifndef FENNEC_TEST_LANGPROC_STRINGS_H ///
#define FENNEC_TEST_LANGPROC_STRINGS_H /// \file _function.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#include "../../test.h"
#include "./strings/test_cstring.h"
namespace fennec::test #ifndef FENNEC_LANG_DETAIL_FUNCTION_H
#define FENNEC_LANG_DETAIL_FUNCTION_H
#include <fennec/lang/utility.h>
namespace fennec::detail
{ {
inline void fennec_test_langproc_strings()
{
fennec_test_subheader("cstring");
fennec_test_spacer(2);
fennec_test_langproc_strings_cstring();
fennec_test_spacer(3);
// TODO
}
} }
#endif // FENNEC_TEST_LANGPROC_STRINGS_H #endif // FENNEC_LANG_DETAIL_FUNCTION_H

View File

@@ -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> requires(is_same_v<FindT, HeadT>)
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

View File

@@ -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)> {};
@@ -65,11 +72,80 @@ namespace fennec::detail
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

View File

@@ -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&&;
}; };
} }

View File

@@ -0,0 +1,136 @@
// =====================================================================================================================
// 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 function.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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

View File

@@ -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));
} }

View File

@@ -55,44 +55,44 @@
/// `FENNEC_HAS_BUILTIN_IS_CONVERTIBLE` <br> /// `FENNEC_HAS_BUILTIN_IS_CONVERTIBLE` <br>
/// `B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)` /// `B FENNEC_BUILTIN_IS_CONVERTIBLE(X, Y)`
/// <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> /// `FENNEC_HAS_BUILTIN_IS_EMPTY` <br>
/// `B FENNEC_BUILTIN_IS_EMPTY(X)` /// `B FENNEC_BUILTIN_IS_EMPTY(X)`
/// <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> /// `FENNEC_HAS_BUILTIN_IS_POLYMORPHIC` <br>
/// `B FENNEC_BUILTIN_IS_POLYMORPHIC(X)` /// `B FENNEC_BUILTIN_IS_POLYMORPHIC(X)`
/// <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> /// `FENNEC_HAS_BUILTIN_IS_FINAL` <br>
/// `B FENNEC_BUILTIN_IS_FINAL(X)` /// `B FENNEC_BUILTIN_IS_FINAL(X)`
/// <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> /// `FENNEC_HAS_BUILTIN_IS_ABSTRACT` <br>
/// `B FENNEC_BUILTIN_IS_ABSTRACT(X)` /// `B FENNEC_BUILTIN_IS_ABSTRACT(X)`
/// <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 `FENNEC_BUILTIN_IS_FINAL`, 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> /// `FENNEC_HAS_BUILTIN_IS_STANDARD_LAYOUT` <br>
/// `B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)` /// `B FENNEC_BUILTIN_IS_STANDARD_LAYOUT(X)`
/// <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> /// `FENNEC_HAS_BUILTIN_IS_CONSTRUCTIBLE` <br>
/// `B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)` /// `B FENNEC_BUILTIN_IS_CONSTRUCTIBLE(X, ...)`
/// <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 `...`, such that `X::X(...)` 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

View File

@@ -249,15 +249,15 @@ template<typename TypeT> struct numeric_limits
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

View File

@@ -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

View File

@@ -0,0 +1,246 @@
// =====================================================================================================================
// 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 metasequences.h
/// \brief \ref fennec_lang_metasequences
///
///
/// \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_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 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 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 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

View File

@@ -0,0 +1,104 @@
// =====================================================================================================================
// 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 ranges.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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 `begin()`
/// \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 `begin()`
/// \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 `begin()`
/// \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 `end()`
/// \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 `end()`
/// \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 `end()`
/// \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

View File

@@ -0,0 +1,37 @@
// =====================================================================================================================
// 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_LANG_STARTUP_H
#define FENNEC_LANG_STARTUP_H
///
/// \brief Macro for running a function before main
/// \param f the name of the function
#define FENNEC_PRIVATE_STATIC_CONSTRUCTOR(f) \
inline static void f(void); \
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
inline static void f(void)
///
/// \brief Macro for running a function before main in a class scope
/// \param f the name of the function
#define FENNEC_CLASS_STATIC_CONSTRUCTOR(f) \
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
inline static void f(void)
#endif // FENNEC_LANG_STARTUP_H

View File

@@ -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;

View File

@@ -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;
} }

File diff suppressed because it is too large Load Diff

View File

@@ -31,7 +31,9 @@
#ifndef FENNEC_LANG_TYPE_TRANSFORMS_H #ifndef FENNEC_LANG_TYPE_TRANSFORMS_H
#define FENNEC_LANG_TYPE_TRANSFORMS_H #define FENNEC_LANG_TYPE_TRANSFORMS_H
#include <fennec/lang/conditional_types.h>
#include <fennec/lang/type_identity.h> #include <fennec/lang/type_identity.h>
#include <fennec/lang/detail/_type_traits.h>
#include <fennec/lang/detail/_type_transforms.h> #include <fennec/lang/detail/_type_transforms.h>
/// ///
@@ -107,16 +109,16 @@
/// \copydetails fennec::remove_cv /// \copydetails fennec::remove_cv
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::add_cvr "add_cvr<TypeT>::type"<br> /// \ref fennec::add_cvref "add_cvref<TypeT>::type"<br>
/// \ref fennec::add_cvr_t "add_cvr_t<TypeT>" /// \ref fennec::add_cvref_t "add_cvref_t<TypeT>"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::add_cvr /// \copydetails fennec::add_cvref
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::remove_cvr "remove_cvr<TypeT>::type"<br> /// \ref fennec::remove_cvref "remove_cvref<TypeT>::type"<br>
/// \ref fennec::remove_cvr_t "remove_cvr_t<TypeT>" /// \ref fennec::remove_cvref_t "remove_cvref_t<TypeT>"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::remove_cvr /// \copydetails fennec::remove_cvref
/// ///
/// </table> /// </table>
/// ///
@@ -125,14 +127,26 @@
namespace fennec namespace fennec
{ {
// Decay Conversions ===================================================================================================
///
/// \brief decays a type into its most basic form
/// \tparam T the type to decay
template<typename T> struct decay : detail::_decay<T> {};
///
/// \brief shorthand for `typename decay<T>::type`
/// \tparam T the type to decay
template<typename T> using decay_t = typename decay<T>::type;
// Pointer Conversions ================================================================================================= // Pointer Conversions =================================================================================================
/// ///
/// \brief adds a pointer level to \p T /// \brief adds a pointer level to \p T
/// ///
/// \details adds a pointer to the provided type such that `T` becomes `T*` /// \details adds a pointer to the provided type such that \f$T\f$ becomes `T*`
/// \tparam T Resultant Type /// \tparam T Resultant Type
template<typename T> struct add_pointer : type_identity<T*>{}; template<typename T> struct add_pointer : detail::_add_pointer<T>{};
/// ///
/// \brief shorthand for `typename add_pointer<T>::type` /// \brief shorthand for `typename add_pointer<T>::type`
@@ -142,25 +156,38 @@ template<typename T> using add_pointer_t = typename add_pointer<T>::type;
/// ///
/// \brief removes a pointer level from \p T /// \brief removes a pointer level from \p T
/// ///
/// \details removes a pointer from the provided type such that `T*` becomes `T` /// \details removes a pointer from the provided type such that `T*` becomes \f$T\f$
/// \tparam T Resultant Type /// \tparam T Resultant Type
template<typename T> struct remove_pointer : type_identity<T> {}; template<typename T> struct remove_pointer : detail::_remove_pointer<T> {};
// specialization for T*
template<typename T> struct remove_pointer<T*> : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_pointer<T>::type` /// \brief shorthand for `typename remove_pointer<T>::type`
template<typename T> using remove_pointer_t = typename remove_pointer<T>::type; template<typename T> using remove_pointer_t = typename remove_pointer<T>::type;
///
/// \brief removes all pointer levels from \p T
///
/// \details removes all pointers from the provided type such that `T*`, `T**`, etc. becomes \f$T\f$
/// \tparam T Resultant Type
template<typename T> struct strip_pointers : conditional_t<
detail::_is_pointer<T>::value,
strip_pointers<remove_pointer_t<T>>,
type_identity<T>
> {};
///
/// \brief shorthand for `typename strip_pointers<T>::type`
template<typename T> using strip_pointers_t = strip_pointers<T>::type;
// Reference Conversions =============================================================================================== // Reference Conversions ===============================================================================================
/// ///
/// \brief add a reference to \p T /// \brief add a reference to \p T
/// ///
/// \details adds a pointer to the provided type such that `T` becomes `T&` /// \details adds a pointer to the provided type such that \f$T\f$ becomes `T&`
/// \tparam T Resultant Type /// \tparam T Resultant Type
template<typename T> struct add_reference : type_identity<T&> {}; template<typename T> struct add_reference : type_identity<T&> {};
@@ -172,7 +199,7 @@ template<typename T> using add_reference_t = typename add_reference<T>::type;
/// ///
/// \brief remove a reference from \p T /// \brief remove a reference from \p T
/// ///
/// \details removes references from the provided type such that `T&` and `T&&` become `T` /// \details removes references from the provided type such that `T&` and `T&&` become \f$T\f$
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_reference : type_identity<T> {}; template<typename T> struct remove_reference : type_identity<T> {};
@@ -217,104 +244,74 @@ template<typename T> using add_rvalue_reference_t = typename add_rvalue_referen
/// ///
/// \brief add the const qualifier to the provided type \p T /// \brief add the const qualifier to the provided type \p T
/// ///
/// \details adds const qualification to the provided type such that `T` becomes `const T` /// \details adds const qualification to the provided type such that \f$T\f$ becomes `const T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_const : type_identity<const T> {}; template<typename T> struct add_const : detail::_add_const<T> {};
/// ///
/// \brief shorthand for `typename add_const<T>::type` /// \brief shorthand for `typename add_const<T>::type`
template<typename T> using add_const_t = typename add_const<T>::type; template<typename T> using add_const_t = typename add_const<T>::type;
// specialization for const types
template<typename T> struct add_const<const T> : type_identity<const T> {};
/// ///
/// \brief remove the const qualifier from the provided type \p T /// \brief remove the const qualifier from the provided type \p T
/// ///
/// \details removes const qualification from the provided type such that `const T` becomes `T` /// \details removes const qualification from the provided type such that `const T` becomes \f$T\f$
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_const : type_identity<T> {}; template<typename T> struct remove_const : detail::_remove_const<T> {};
/// ///
/// \brief shorthand for `typename remove_const<T>::type` /// \brief shorthand for `typename remove_const<T>::type`
template<typename T> using remove_const_t = typename remove_const<T>::type; template<typename T> using remove_const_t = typename remove_const<T>::type;
// specialization for const types
template<typename T> struct remove_const<const T> : type_identity<T> {};
/// ///
/// \brief add the volatile qualifier to the provided type \p T /// \brief add the volatile qualifier to the provided type \p T
/// ///
/// \details removes references from the provided type such that `T` becomes `volatile T` /// \details removes references from the provided type such that \f$T\f$ becomes `volatile T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_volatile : type_identity<volatile T> {}; template<typename T> struct add_volatile : detail::_add_volatile<T> {};
/// ///
/// \brief shorthand for `typename add_volatile<T>::type` /// \brief shorthand for `typename add_volatile<T>::type`
template<typename T> using add_volatile_t = typename add_volatile<T>::type; template<typename T> using add_volatile_t = typename add_volatile<T>::type;
// specialization for volatile types
template<typename T> struct add_volatile<volatile T> : type_identity<volatile T> {};
/// ///
/// \brief remove the volatile qualifier from the provided type \p T /// \brief remove the volatile qualifier from the provided type \p T
/// ///
/// \details removes references from the provided type such that `volatile T` becomes `T` /// \details removes references from the provided type such that `volatile T` becomes \f$T\f$
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_volatile : type_identity<T> {}; template<typename T> struct remove_volatile : detail::_remove_volatile<T> {};
/// ///
/// \brief shorthand for `typename remove_volatile<T>::type` /// \brief shorthand for `typename remove_volatile<T>::type`
template<typename T> using remove_volatile_t = typename remove_volatile<T>::type; template<typename T> using remove_volatile_t = typename remove_volatile<T>::type;
// specialization for volatile types
template<typename T> struct remove_volatile<volatile T> : type_identity<T> {};
/// ///
/// \brief remove the volatile qualifier from the provided type \p T /// \brief remove the volatile qualifier from the provided type \p T
/// ///
/// \details removes references from the provided type such that `T`, `const T`, and `volatile T` become /// \details removes references from the provided type such that \f$T\f$, `const T`, and `volatile T` become
/// `const volatile T` /// `const volatile T`
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_cv : type_identity<const volatile T> {}; template<typename T> struct add_cv : detail::_add_cv<T> {};
/// ///
/// \brief shorthand for `typename add_cv<T>::type` /// \brief shorthand for `typename add_cv<T>::type`
template<typename T> using add_cv_t = typename add_cv<T>::type; template<typename T> using add_cv_t = typename add_cv<T>::type;
// specialization for const types
template<typename T> struct add_cv<const T> : type_identity<const volatile T> {};
// specialization for volatile types
template<typename T> struct add_cv<volatile T> : type_identity<const volatile T> {};
// specialization for const volatile types
template<typename T> struct add_cv<const volatile T> : type_identity<const volatile T> {};
/// ///
/// \brief remove the const and volatile qualifiers from the provided type \p T /// \brief remove the const and volatile qualifiers from the provided type \p T
/// ///
/// \details removes const and volatile from the provided type such that `const T`, `volatile T`, and /// \details removes const and volatile from the provided type such that `const T`, `volatile T`, and
/// `const volatile T` become `T` /// `const volatile T` become \f$T\f$
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_cv : type_identity<T> {}; template<typename T> struct remove_cv : detail::_remove_cv<T> {};
// specialization for const types
template<typename T> struct remove_cv<const T> : type_identity<T> {};
// specialization for volatile types
template<typename T> struct remove_cv<volatile T> : type_identity<T> {};
// specialization for const volatile types
template<typename T> struct remove_cv<const volatile T> : type_identity<T> {};
/// ///
/// \brief shorthand for `typename remove_cv<T>::type` /// \brief shorthand for `typename remove_cv<T>::type`
@@ -327,11 +324,11 @@ template<typename T> using remove_cv_t = typename remove_cv<T>::type;
/// ///
/// \details adds references and const volatile qualifiers to the provided type. /// \details adds references and const volatile qualifiers to the provided type.
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct add_cvr : type_identity<add_reference_t<add_cv_t<T>>> {}; template<typename T> struct add_cvref : type_identity<add_reference_t<add_cv_t<T>>> {};
/// ///
/// \brief shorthand for `typename add_cvr<T>::type` /// \brief shorthand for `typename add_cvr<T>::type`
template<typename T> using add_cvr_t = typename add_cvr<T>::type; template<typename T> using add_cvref_t = typename add_cvref<T>::type;
@@ -340,12 +337,26 @@ template<typename T> using add_cvr_t = typename add_cvr<T>::type;
/// ///
/// \details removes const and volatile from the provided type such that /// \details removes const and volatile from the provided type such that
/// \tparam T Reference Type /// \tparam T Reference Type
template<typename T> struct remove_cvr : type_identity<remove_cv_t<remove_reference_t<T>>> {}; template<typename T> struct remove_cvref : type_identity<remove_cv_t<remove_reference_t<T>>> {};
/// ///
/// \brief shorthand for `typename remove_cvr<T>::type` /// \brief shorthand for `typename remove_cvr<T>::type`
template<typename T> using remove_cvr_t = typename remove_cvr<T>::type; template<typename T> using remove_cvref_t = typename remove_cvref<T>::type;
///
/// \brief removes references and pointers as well as the const and volatile qualifiers from the provided type \p T
///
/// \details removes const and volatile from the provided type such that
/// \tparam T Reference Type
template<typename T> struct remove_cvrefptr : type_identity<remove_cv_t<remove_reference_t<strip_pointers_t<T>>>> {};
///
/// \brief shorthand for `typename remove_cvrp_t<T>::type`
template<typename T> using remove_cvrefptr_t = typename remove_cvrefptr<T>::type;
} }

View File

@@ -203,8 +203,6 @@
#include <fennec/lang/detail/_int.h> #include <fennec/lang/detail/_int.h>
#include <fennec/lang/conditional_types.h>
namespace fennec namespace fennec
{ {
// Basic Types ========================================================================================================= // Basic Types =========================================================================================================
@@ -215,6 +213,8 @@ namespace fennec
using bool_t = bool; ///< \brief A conditional type using bool_t = bool; ///< \brief A conditional type
using byte_t = unsigned char; ///< \brief A type capable of holding a single byte
using char_t = char; ///< \brief A type capable of holding an ascii value using char_t = char; ///< \brief A type capable of holding an ascii value
using schar_t = signed char; ///< \brief A type with the size of a char, capable of holding a signed 8-bit integer using schar_t = signed char; ///< \brief A type with the size of a char, capable of holding a signed 8-bit integer
using uchar_t = unsigned char; ///< \brief A type with the size of a char, capable of holding an unsigned 8-bit integer using uchar_t = unsigned char; ///< \brief A type with the size of a char, capable of holding an unsigned 8-bit integer
@@ -247,7 +247,7 @@ namespace fennec
using uintptr_t = uintptr_t; ///< \brief Unsigned Integer Capable of Holding a Pointer to void using uintptr_t = uintptr_t; ///< \brief Unsigned Integer Capable of Holding a Pointer to void
using intmax_t = intmax_t; ///< \brief Maximum Width Signed Integer Type using intmax_t = intmax_t; ///< \brief Maximum Width Signed Integer Type
using uintmax_t = uintmax_t; ///< \brief Maximum Width Unsigned Integer Type using uintmax_t = uintmax_t; ///< \brief Maximum Width Unsigned Integer Type
using size_t = size_t; ///< \brief Unsigned Integer Type Returned By `sizeof`, `sizeof...`, and `alignof` using size_t = size_t; ///< \brief Unsigned Integer Type Returned By \f$sizeof\f$, `sizeof...`, and \f$alignof\f$
using ptrdiff_t = __PTRDIFF_TYPE__; ///< \brief Signed Integer Type Returned by the Subtraction of two Pointers using ptrdiff_t = __PTRDIFF_TYPE__; ///< \brief Signed Integer Type Returned by the Subtraction of two Pointers
struct empty_t {}; struct empty_t {};

View File

@@ -76,10 +76,21 @@ template<typename T> constexpr T&& forward(remove_reference_t<T>& x) noexcept {
return static_cast<T&&>(x); return static_cast<T&&>(x);
} }
#ifndef FENNEC_DOXYGEN
// specialization for T&& // specialization for T&&
template<typename T> constexpr T&& forward(remove_reference_t<T>&& x) noexcept { template<typename T> constexpr T&& forward(remove_reference_t<T>&& x) noexcept {
return static_cast<T&&>(x); return static_cast<T&&>(x);
} }
#endif
/// \brief Copies \f$v\f$ to a new object of `decay_t<T>`
/// \tparam T The type
/// \param v The object
/// \returns A stack allocated copy of \f$v\f$ in `decay_t<T>`
template<typename T> constexpr decay_t<T> decay_copy(T&& v) {
return fennec::forward<T>(v);
}
/// ///
@@ -123,9 +134,18 @@ constexpr void swap(T& x, T& y) noexcept {
/// \param x first value /// \param x first value
/// \param y second value /// \param y second value
template<typename T> constexpr void swap(T& x, T& y) noexcept { template<typename T> constexpr void swap(T& x, T& y) noexcept {
#if FENNEC_COMPILER_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
T a = fennec::move(x); T a = fennec::move(x);
x = fennec::move(y); x = fennec::move(y);
y = fennec::move(a); y = fennec::move(a);
#if FENNEC_COMPILER_GCC
#pragma GCC diagnostic pop
#endif
} }
} }

View File

@@ -44,81 +44,81 @@
/// ///
/// ///
/// ///
/// \section section_sign_functions Sign /// \section fennec_math_common_section_sign_functions Sign Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_sign_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_common_sign_functions">
/// <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>
/// \ref fennec::abs(fennec::genType) "genIType abs(genIType x)" <br> /// \ref fennec::abs "genIType abs(genIType x)" <br>
/// \ref fennec::abs(fennec::genType) "genFType abs(genFType x)" <br> /// \ref fennec::abs "genFType abs(genFType x)" <br>
/// \ref fennec::abs(fennec::genType) "genDType abs(genDType x)" /// \ref fennec::abs "genDType abs(genDType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::abs(fennec::genType) /// \copydetails fennec::abs
/// ///
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::sign(fennec::genType) "genIType sign(genIType x)" <br> /// \ref fennec::sign "genIType sign(genIType x)" <br>
/// \ref fennec::sign(fennec::genType) "genFType sign(genFType x)" <br> /// \ref fennec::sign "genFType sign(genFType x)" <br>
/// \ref fennec::sign(fennec::genType) "genDType sign(genDType x)" /// \ref fennec::sign "genDType sign(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::sign(fennec::genType) /// \copydetails fennec::sign
/// ///
/// </table> /// </table>
/// ///
/// ///
/// \section section_rounding_functions Rounding /// \section fennec_math_common_section_rounding_functions Rounding Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_rounding_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_common_rounding_functions">
/// <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>
/// \ref fennec::floor(fennec::genType) "genFType floor(genFType x)" <br> /// \ref fennec::floor "genFType floor(genFType x)" <br>
/// \ref fennec::floor(fennec::genType) "genDType floor(genDType x)" /// \ref fennec::floor "genDType floor(genDType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::floor(fennec::genType) /// \copydetails fennec::floor
/// ///
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::ceil(fennec::genType) "genFType ceil(genFType x)" <br> /// \ref fennec::ceil "genFType ceil(genFType x)" <br>
/// \ref fennec::ceil(fennec::genType) "genDType ceil(genDType x)" /// \ref fennec::ceil "genDType ceil(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::ceil(fennec::genType) /// \copydetails fennec::ceil
/// ///
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::round(fennec::genType) "genFType round(genFType x)" <br> /// \ref fennec::round "genFType round(genFType x)" <br>
/// \ref fennec::round(fennec::genType) "genDType round(genDType x)" /// \ref fennec::round "genDType round(genDType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::round(fennec::genType) /// \copydetails fennec::round
/// ///
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::roundEven(fennec::genType) "genFType roundEven(genFType x)" <br> /// \ref fennec::roundEven "genFType roundEven(genFType x)" <br>
/// \ref fennec::roundEven(fennec::genType) "genDType roundEven(genDType x)" /// \ref fennec::roundEven "genDType roundEven(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::roundEven(fennec::genType) /// \copydetails fennec::roundEven
/// ///
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::trunc(fennec::genType) "genFType trunc(genFType x)" <br> /// \ref fennec::trunc "genFType trunc(genFType x)" <br>
/// \ref fennec::trunc(fennec::genType) "genDType trunc(genDType x)" /// \ref fennec::trunc "genDType trunc(genDType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::trunc(fennec::genType) /// \copydetails fennec::trunc
/// ///
/// </table> /// </table>
/// ///
/// ///
/// \section section_decimal_functions Decimal-Point /// \section fennec_math_common_section_decimal_functions Decimal-Point Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_decimal_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_common_decimal_functions">
/// <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>
/// \ref fennec::fract(fennec::genType) "genFType fract(genFType x)" <br> /// \ref fennec::fract "genFType fract(genFType x)" <br>
/// \ref fennec::fract(fennec::genType) "genDType fract(genDType x)" /// \ref fennec::fract "genDType fract(genDType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::fract(fennec::genType) /// \copydetails fennec::fract
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::mod "genFType mod(genFType x, float y)" <br> /// \ref fennec::mod "genFType mod(genFType x, float y)" <br>
@@ -129,59 +129,59 @@
/// \copydetails fennec::mod /// \copydetails fennec::mod
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::modf(fennec::genType, fennec::genType&) "genFType modf(genFType x, out genFType i)" <br> /// \ref fennec::modf "genFType modf(genFType x, out genFType i)" <br>
/// \ref fennec::modf(fennec::genType, fennec::genType&) "genDType modf(genDType x, out genDType i)" /// \ref fennec::modf "genDType modf(genDType x, out genDType i)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::modf(fennec::genType, fennec::genType&) /// \copydetails fennec::modf
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::isnan(fennec::genType) "genBType isnan(genFType x)" <br> /// \ref fennec::isnan "genBType isnan(genFType x)" <br>
/// \ref fennec::isnan(fennec::genType) "genBType isnan(genDType x)" /// \ref fennec::isnan "genBType isnan(genDType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::isnan(fennec::genType) /// \copydetails fennec::isnan
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::isinf(fennec::genType) "genBType isinf(genFType x)" <br> /// \ref fennec::isinf "genBType isinf(genFType x)" <br>
/// \ref fennec::isinf(fennec::genType) "genBType isinf(genDType x)" /// \ref fennec::isinf "genBType isinf(genDType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::isinf(fennec::genType) /// \copydetails fennec::isinf
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::frexp(fennec::genType, fennec::genIType&) "genFType frexp(genFType x, out genIType exp)" <br> /// \ref fennec::frexp "genFType frexp(genFType x, out genIType exp)" <br>
/// \ref fennec::frexp(fennec::genType, fennec::genIType&) "genDType frexp(genDType x, out genIType exp)" /// \ref fennec::frexp "genDType frexp(genDType x, out genIType exp)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::frexp(fennec::genType, fennec::genIType&) /// \copydetails fennec::frexp
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::ldexp(fennec::genType, fennec::genIType) "genFType ldexp(genFType x, genIType exp)" <br> /// \ref fennec::ldexp "genFType ldexp(genFType x, genIType exp)" <br>
/// \ref fennec::ldexp(fennec::genType, fennec::genIType) "genDType ldexp(genDType x, genIType exp)" /// \ref fennec::ldexp "genDType ldexp(genDType x, genIType exp)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::ldexp(fennec::genType, fennec::genIType) /// \copydetails fennec::ldexp
/// ///
/// </table> /// </table>
/// ///
/// ///
/// \section section_bit_conversion_functions Bit Conversion /// \section fennec_math_common_section_bit_functions Bit Conversion
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_bit_conversions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_common_bit_conversions">
/// <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>
/// \ref fennec::floatBitsToInt(fennec::genType) "genIType floatBitsToInt(genType value)" <br> /// \ref fennec::floatBitsToInt "genIType floatBitsToInt(genType value)" <br>
/// \ref fennec::floatBitsToUint(fennec::genType) "genUType floatBitsToUint(genType value)" /// \ref fennec::floatBitsToUint "genUType floatBitsToUint(genType value)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::floatBitsToUint(fennec::genType) /// \copydetails fennec::floatBitsToUint
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::intBitsToFloat(fennec::genIType) "genFType intBitsToFloat(genIType value)" <br> /// \ref fennec::intBitsToFloat "genFType intBitsToFloat(genIType value)" <br>
/// \ref fennec::uintBitsToFloat(fennec::genUType) "genFType uintBitsToFloat(genUType value)" /// \ref fennec::uintBitsToFloat "genFType uintBitsToFloat(genUType value)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::uintBitsToFloat(fennec::genType) /// \copydetails fennec::uintBitsToFloat
/// ///
/// </table> /// </table>
/// ///
/// ///
/// \section section_comparison_functions Comparison /// \section fennec_math_common_section_comparison_functions Comparison Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_common_comparison_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_common_comparison_functions">
/// <tr><th style="vertical-align: top">Syntax /// <tr><th style="vertical-align: top">Syntax
@@ -226,7 +226,7 @@
/// </table> /// </table>
/// ///
/// ///
/// \section section_curve_functions Curves /// \section fennec_math_common_section_curve_functions Curve Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_curve_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_curve_functions">
/// <tr><th style="vertical-align: top">Syntax /// <tr><th style="vertical-align: top">Syntax
@@ -256,11 +256,11 @@
/// \copydetails fennec::mix /// \copydetails fennec::mix
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::mix(fennec::genBType, fennec::genBType, fennec::genBType) "mix(genBType x, genBType y, genBType a)" <br> /// \ref fennec::mix "mix(genBType x, genBType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genIType x, genIType y, genBType a)" <br> /// \ref fennec::mix "mix(genIType x, genIType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genUType x, genUType y, genBType a)" <br> /// \ref fennec::mix "mix(genUType x, genUType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genFType x, genFType y, genBType a)" <br> /// \ref fennec::mix "mix(genFType x, genFType y, genBType a)" <br>
/// \ref fennec::mix(fennec::genType, fennec::genType, fennec::genBType) "mix(genDType x, genDType y, genBType a)" /// \ref fennec::mix "mix(genDType x, genDType y, genBType a)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::mix /// \copydetails fennec::mix
/// ///
@@ -308,14 +308,6 @@ constexpr genType sign(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> sign(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sign(x[i]) ...);
}
// Absolute Value ====================================================================================================== // Absolute Value ======================================================================================================
/// ///
@@ -331,14 +323,6 @@ constexpr genType abs(genType x) {
return x * fennec::sign(x); return x * fennec::sign(x);
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> abs(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::abs(x[i]) ...);
}
/// @} /// @}
@@ -366,14 +350,6 @@ constexpr genType floor(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> floor(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::floor(x[i]) ...);
}
// Ceil ================================================================================================================ // Ceil ================================================================================================================
@@ -391,14 +367,6 @@ constexpr genType ceil(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> ceil(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::ceil(x[i]) ...);
}
// Round =============================================================================================================== // Round ===============================================================================================================
@@ -416,14 +384,6 @@ template<typename genType> constexpr genType round(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> round(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::round(x[i]) ...);
}
// Trunc =============================================================================================================== // Trunc ===============================================================================================================
@@ -441,14 +401,6 @@ constexpr genType trunc(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> trunc(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::trunc(x[i]) ...);
}
// Round Even ========================================================================================================== // Round Even ==========================================================================================================
@@ -486,13 +438,6 @@ constexpr genType roundEven(genType x) {
//return i + static_cast<genType>(up); //return i + static_cast<genType>(up);
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::roundEven(x[i]) ...);
}
/// @} /// @}
@@ -519,14 +464,6 @@ constexpr genType fract(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> fract(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::fract(x[i]) ...);
}
// Mod ================================================================================================================= // Mod =================================================================================================================
/// ///
@@ -544,19 +481,6 @@ constexpr genType mod(genType x, genType y) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, genType y) {
return x - y * fennec::floor(x / y);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, const vector<genType, i...>& y) {
return x - y * fennec::floor(x / y);
}
// ModF ================================================================================================================ // ModF ================================================================================================================
/// ///
@@ -574,14 +498,6 @@ constexpr genType modf(genType x, genType& i) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genType, i...>& I) {
I = fennec::floor(x); return fennec::fract(x);
}
// Is NaN ============================================================================================================== // Is NaN ==============================================================================================================
/// ///
@@ -590,7 +506,7 @@ constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genT
/// \returns **true** if \f$x\f$ holds a NaN. Returns **false** otherwise. <br> <br> /// \returns **true** if \f$x\f$ holds a NaN. Returns **false** otherwise. <br> <br>
/// \details \f$NaN\f$ is a concept unique to computing. It strictly means, and more specifically, /// \details \f$NaN\f$ is a concept unique to computing. It strictly means, and more specifically,
/// floating point values can only represent *real* numbers. This is why some functions, /// floating point values can only represent *real* numbers. This is why some functions,
/// like \ref fennec::sqrt(fennec::genFType) "sqrt()", return \f$NaN\f$ when an expression would return /// like \ref fennec::sqrt "sqrt()", return \f$NaN\f$ when an expression would return
/// a value in a different coordinate space. There are other cases, such as \f$\frac{1}{x}\f$, /// a value in a different coordinate space. There are other cases, such as \f$\frac{1}{x}\f$,
/// where \f$x=0\f$ is undefined, and respectively the return value is also \f$NaN\f$. <br> <br> /// where \f$x=0\f$ is undefined, and respectively the return value is also \f$NaN\f$. <br> <br>
/// ///
@@ -603,14 +519,6 @@ constexpr genBType isnan(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isnan(const vector<genType, i...>& x) {
return vector<genBType, i...>(fennec::isnan(x[i]) ...);
}
// Is Inf ============================================================================================================== // Is Inf ==============================================================================================================
/// ///
@@ -627,19 +535,11 @@ constexpr genBType isinf(genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isinf(const vector<genType, i...>& x) {
return vector<genBType, i...>(fennec::isinf(x[i]) ...);
}
// Bit Conversion ====================================================================================================== // Bit Conversion ======================================================================================================
/// ///
/// \copydetails fennec::floatBitsToUint(fennec::genFType) /// \copydetails fennec::floatBitsToUint
template<typename genType, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType)) template<typename genType, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr genIType floatBitsToInt(genType x) { constexpr genIType floatBitsToInt(genType x) {
return fennec::bit_cast<genIType>(x); return fennec::bit_cast<genIType>(x);
@@ -669,7 +569,7 @@ constexpr genUType floatBitsToUint(genType x) {
/// ///
/// \copydetails fennec::uintBitsToFloat(fennec::genUType) /// \copydetails fennec::uintBitsToFloat
template<typename genType = float_t, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType)) template<typename genType = float_t, typename genIType = int_t> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr genType intBitsToFloat(genIType x) { constexpr genType intBitsToFloat(genIType x) {
return fennec::bit_cast<genType>(x); return fennec::bit_cast<genType>(x);
@@ -696,29 +596,6 @@ constexpr genType uintBitsToFloat(genUType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genIType, i...> floatBitsToInt(const vector<genType, i...>& x) {
return vector<genIType, i...>(fennec::bit_cast<genIType>(x[i])...);
}
template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genUType, i...> floatBitsToUint(const vector<genType, i...>& x) {
return vector<genUType, i...>(fennec::bit_cast<genUType>(x[i])...);
}
template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genType, i...> intBitsToFloat(const vector<genIType, i...>& x) {
return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}
template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genType, i...> uintBitsToFloat(const vector<genUType, i...>& x) {
return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}
// fma ================================================================================================================= // fma =================================================================================================================
@@ -738,14 +615,6 @@ constexpr genType fma(genType a, genType b, genType c) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> fma(const vector<genType, i...>& a, const vector<genType, i...>& b, const vector<genType, i...>& c) {
return vector<genType, i...>(fennec::fma(a[i], b[i], c[i]) ...);
}
// frexp =============================================================================================================== // frexp ===============================================================================================================
@@ -769,14 +638,6 @@ constexpr genType frexp(genType x, genIType& exp) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> frexp(const vector<genType, i...>& x, vector<genIType, i...>& exp) {
return vector<genType, i...>(fennec::frexp(x[i], exp[i])...);
}
// ldexp =============================================================================================================== // ldexp ===============================================================================================================
@@ -802,14 +663,6 @@ constexpr genType ldexp(genType x, genIType exp) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> ldexp(const vector<genType, i...>& x, const vector<genIType, i...>& exp) {
return vector<genType, i...>(fennec::ldexp(x[i], exp[i])...);
}
/// @} /// @}
@@ -837,24 +690,6 @@ constexpr genType min(genType x, genType y) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> min(genType x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::min(x, y[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, genType y) {
return vector<genType, i...>(fennec::min(x[i], y) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::min(x[i], y[i]) ...);
}
// Max ================================================================================================================= // Max =================================================================================================================
/// ///
@@ -872,24 +707,6 @@ constexpr genType max(genType x, genType y) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> max(genType x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::max(x, y[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, genType y) {
return vector<genType, i...>(fennec::max(x[i], y) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::max(x[i], y[i]) ...);
}
// Clamp =============================================================================================================== // Clamp ===============================================================================================================
/// ///
@@ -908,19 +725,6 @@ constexpr genType clamp(genType x, genType minVal, genType maxVal) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, genType minVal, genType maxVal) {
return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal), maxVal)...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, const vector<genType, i...>& minVal, const vector<genType, i...>& maxVal) {
return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal[i]), maxVal[i])...);
}
/// @} /// @}
@@ -948,19 +752,6 @@ constexpr genType step(genType edge, genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(genType edge, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::step(edge, x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(const vector<genType, i...>& edge, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::step(edge[i], x[i]) ...);
}
// Smoothstep ========================================================================================================== // Smoothstep ==========================================================================================================
/// ///
@@ -989,19 +780,6 @@ constexpr genType smoothstep(genType edge0, genType edge1, genType x) {
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(genType edge0, genType edge1, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::smoothstep(edge0, edge1, x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(const vector<genType, i...>& edge0, const vector<genType, i...>& edge1, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::smoothstep(edge0[i], edge1[i], x[i]) ...);
}
// Mix ================================================================================================================= // Mix =================================================================================================================
/// ///
@@ -1022,18 +800,6 @@ constexpr genType mix(genType x, genType y, genType a) {
return x * (genType(1.0) - a) + y * a; return x * (genType(1.0) - a) + y * a;
} }
// Vector Specializations ----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genType a) {
return x * (genType(1.0) - a) + y * a;
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genType, i...>& a) {
return x * (genType(1.0) - a) + y * a;
}
// Mix (Bool) ========================================================================================================== // Mix (Bool) ==========================================================================================================
@@ -1058,7 +824,178 @@ constexpr genType mix(genType x, genType y, genBType a) {
} }
// Vector Specializations ---------------------------------------------------------------------------------------------- // Internal ============================================================================================================
#ifndef FENNEC_DOXYGEN
template<typename genType, size_t...i>
constexpr vector<genType, i...> sign(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sign(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> abs(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::abs(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> floor(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::floor(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> ceil(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::ceil(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> round(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::round(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> trunc(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::trunc(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> roundEven(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::roundEven(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> fract(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::fract(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, genType y) {
return x - y * fennec::floor(x / y);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> mod(const vector<genType, i...>& x, const vector<genType, i...>& y) {
return x - y * fennec::floor(x / y);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> modf(const vector<genType, i...>& x, vector<genType, i...>& I) {
I = fennec::floor(x); return fennec::fract(x);
}
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isnan(const vector<genType, i...>& x) {
return vector<genBType, i...>(fennec::isnan(x[i]) ...);
}
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType>)
constexpr vector<genBType, i...> isinf(const vector<genType, i...>& x) {
return vector<genBType, i...>(fennec::isinf(x[i]) ...);
}
template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genIType, i...> floatBitsToInt(const vector<genType, i...>& x) {
return vector<genIType, i...>(fennec::bit_cast<genIType>(x[i])...);
}
template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genUType, i...> floatBitsToUint(const vector<genType, i...>& x) {
return vector<genUType, i...>(fennec::bit_cast<genUType>(x[i])...);
}
template<typename genType = float_t, typename genIType = int_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genIType> and is_signed_v<genIType> and sizeof(genType) == sizeof(genIType))
constexpr vector<genType, i...> intBitsToFloat(const vector<genIType, i...>& x) {
return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}
template<typename genType = float_t, typename genUType = uint_t, size_t...i> requires(is_floating_point_v<genType> and is_integral_v<genUType> and is_unsigned_v<genUType> and sizeof(genType) == sizeof(genUType))
constexpr vector<genType, i...> uintBitsToFloat(const vector<genUType, i...>& x) {
return vector<genType, i...>(fennec::bit_cast<genType>(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> fma(const vector<genType, i...>& a, const vector<genType, i...>& b, const vector<genType, i...>& c) {
return vector<genType, i...>(fennec::fma(a[i], b[i], c[i]) ...);
}
template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> frexp(const vector<genType, i...>& x, vector<genIType, i...>& exp) {
return vector<genType, i...>(fennec::frexp(x[i], exp[i])...);
}
template<typename genType, typename genIType = int_t, size_t...i> requires(is_integral_v<genIType>)
constexpr vector<genType, i...> ldexp(const vector<genType, i...>& x, const vector<genIType, i...>& exp) {
return vector<genType, i...>(fennec::ldexp(x[i], exp[i])...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> min(genType x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::min(x, y[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, genType y) {
return vector<genType, i...>(fennec::min(x[i], y) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> min(const vector<genType, i...>& x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::min(x[i], y[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> max(genType x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::max(x, y[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, genType y) {
return vector<genType, i...>(fennec::max(x[i], y) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> max(const vector<genType, i...>& x, const vector<genType, i...>& y) {
return vector<genType, i...>(fennec::max(x[i], y[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, genType minVal, genType maxVal) {
return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal), maxVal)...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> clamp(const vector<genType, i...>& x, const vector<genType, i...>& minVal, const vector<genType, i...>& maxVal) {
return vector<genType, i...>(fennec::min(fennec::max(x[i], minVal[i]), maxVal[i])...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(genType edge, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::step(edge, x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> step(const vector<genType, i...>& edge, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::step(edge[i], x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(genType edge0, genType edge1, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::smoothstep(edge0, edge1, x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> smoothstep(const vector<genType, i...>& edge0, const vector<genType, i...>& edge1, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::smoothstep(edge0[i], edge1[i], x[i]) ...);
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genType a) {
return x * (genType(1.0) - a) + y * a;
}
template<typename genType, size_t...i> requires(is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genType, i...>& a) {
return x * (genType(1.0) - a) + y * a;
}
template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>) template<typename genType, typename genBType = bool_t, size_t...i> requires(is_bool_v<genBType> and is_floating_point_v<genType>)
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genBType a) { constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, genBType a) {
@@ -1075,6 +1012,7 @@ template<typename genType, typename genBType = bool_t, size_t...i> requires(is_b
constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genBType, i...>& a) { constexpr vector<genType, i...> mix(const vector<genType, i...>& x, const vector<genType, i...>& y, const vector<genBType, i...>& a) {
return genDType((a[i] ? y[i] : x[i])...); return genDType((a[i] ? y[i] : x[i])...);
} }
#endif // FENNEC_DOXYGEN
/// @} /// @}

View File

@@ -24,15 +24,15 @@
namespace fennec namespace fennec
{ {
template<typename ScalarT, size_t...IndicesV> struct vector; // Forward def for vectors template<typename ScalarT, size_t...IndicesV> requires(is_scalar_v<ScalarT>) struct vector; // Forward def for vectors
template<typename ScalarT, size_t RowsV, size_t...ColIndicesV> struct matrix; // Forward def for matrices template<typename ScalarT, size_t RowsV, size_t...ColIndicesV> requires(is_scalar_v<ScalarT>) struct matrix; // Forward def for matrices
// Simplified interface for creating sized vectors or matrices // Simplified interface for creating sized vectors or matrices
template<typename ScalarT, size_t SizeV> using vec template<typename ScalarT, size_t SizeV> using vec
= decltype(detail::_gen_vector<vector, ScalarT>(make_index_sequence<SizeV>{})); // Gets the type returned by this function = decltype(detail::_gen_vector<vector, ScalarT>(make_index_metasequence<SizeV>{})); // Gets the type returned by this function
template<typename ScalarT, size_t ColsV, size_t RowsV> using mat template<typename ScalarT, size_t ColsV, size_t RowsV> using mat
= decltype(detail::_gen_matrix<matrix, ScalarT, RowsV>(make_index_sequence<ColsV>{})); // Gets the type returned by this function = decltype(detail::_gen_matrix<matrix, ScalarT, RowsV>(make_index_metasequence<ColsV>{})); // Gets the type returned by this function
} }

View File

@@ -19,7 +19,7 @@
#ifndef FENNEC_MATH_DETAIL_TYPES_H #ifndef FENNEC_MATH_DETAIL_TYPES_H
#define FENNEC_MATH_DETAIL_TYPES_H #define FENNEC_MATH_DETAIL_TYPES_H
#include <fennec/lang/const_sequences.h> #include <fennec/lang/metasequences.h>
namespace fennec namespace fennec
{ {
@@ -28,11 +28,11 @@ namespace detail
{ {
template<template<typename, size_t...> typename VectorT, typename ScalarT, size_t...IndicesV> template<template<typename, size_t...> typename VectorT, typename ScalarT, size_t...IndicesV>
VectorT<ScalarT, IndicesV...> _gen_vector(const_index_sequence<IndicesV...>); // Helper for substituting a size N with sequence of integers VectorT<ScalarT, IndicesV...> _gen_vector(index_metasequence<IndicesV...>); // Helper for substituting a size N with sequence of integers
template<template<typename, size_t...> typename MatrixT, typename ScalarT, size_t RowsV, size_t...IndicesV> template<template<typename, size_t...> typename MatrixT, typename ScalarT, size_t RowsV, size_t...IndicesV>
MatrixT<ScalarT, RowsV, IndicesV...> _gen_matrix(const_index_sequence<IndicesV...>); // Helper for substituting a size Columns with sequence of integers MatrixT<ScalarT, RowsV, IndicesV...> _gen_matrix(index_metasequence<IndicesV...>); // Helper for substituting a size Columns with sequence of integers
} }

View File

@@ -42,45 +42,45 @@
/// \code #include <fennec/math/exponential.h> \endcode /// \code #include <fennec/math/exponential.h> \endcode
/// ///
/// ///
/// \section Exponential Functions /// \section fennec_math_exponential_section_functions Exponential Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_exponential_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_exponential_functions">
/// <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>
/// \ref fennec::pow(fennec::genType, fennec::genType) "genFType pow(genFType x, genFType y)" /// \ref fennec::pow "genFType pow(genFType x, genFType y)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::pow(fennec::genType, fennec::genType) /// \copydetails fennec::pow
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::exp(fennec::genType)"genFType exp(genFType x)" /// \ref fennec::exp"genFType exp(genFType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::exp(fennec::genType) /// \copydetails fennec::exp
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::exp2(fennec::genType) "genFType exp2(genFType x)" /// \ref fennec::exp2 "genFType exp2(genFType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::exp2(fennec::genType) /// \copydetails fennec::exp2
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::log(fennec::genType) "genFType log(genFType x)" /// \ref fennec::log "genFType log(genFType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::log(fennec::genType) /// \copydetails fennec::log
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::log2(fennec::genType) "genFType log2(genFType x)" /// \ref fennec::log2 "genFType log2(genFType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::log2(fennec::genType) /// \copydetails fennec::log2
/// ///
/// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br> /// <tr><td width="50%" style="vertical-align: top" class="odd_c"> <br>
/// \ref fennec::sqrt(fennec::genType) "genFType sqrt(genFType x)" /// \ref fennec::sqrt "genFType sqrt(genFType x)"
/// <td width="50%" style="vertical-align: top" class="odd_c"> /// <td width="50%" style="vertical-align: top" class="odd_c">
/// \copydetails fennec::sqrt(fennec::genType) /// \copydetails fennec::sqrt
/// ///
/// <tr><td width="50%" style="vertical-align: top"> <br> /// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::inversesqrt(fennec::genType) "genFType inversesqrt(genFType x)" /// \ref fennec::inversesqrt "genFType inversesqrt(genFType x)"
/// <td width="50%" style="vertical-align: top"> /// <td width="50%" style="vertical-align: top">
/// \copydetails fennec::inversesqrt(fennec::genType) /// \copydetails fennec::inversesqrt
/// ///
/// </table> /// </table>
/// ///
@@ -110,14 +110,6 @@ constexpr genType pow(genType x, genType y) {
} }
// Vector Specialization -----------------------------------------------------------------------------------------------
template<typename genType, size_t...i>
constexpr vector<genType, i...> pow(const vector<genType, i...> & x, const vector<genType, i...> & y) {
return vector<genType, i...>(fennec::pow(x[i], y[i]) ...);
}
// exp ================================================================================================================= // exp =================================================================================================================
/// ///
@@ -132,13 +124,6 @@ constexpr genType exp(genType x) {
} }
// Vector Specialization -----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> constexpr vector<genType, i...> exp(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::exp(x[i]) ...);
}
// exp2 ================================================================================================================ // exp2 ================================================================================================================
/// ///
@@ -152,13 +137,6 @@ template<typename genType> constexpr genType exp2(genType x) {
} }
// Vector Specialization -----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> constexpr vector<genType, i...> exp2(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::exp2(x[i]) ...);
}
// log ================================================================================================================= // log =================================================================================================================
/// ///
@@ -173,13 +151,6 @@ template<typename genType> constexpr genType log(genType x) {
} }
// Vector Specialization -----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> constexpr genType log(const vector<genType, i...>& x) {
return vector<genType, i...>(log(x[i]) ...);
}
// log2 ================================================================================================================ // log2 ================================================================================================================
/// ///
@@ -195,13 +166,6 @@ template<typename genType> constexpr genType log2(genType x) {
} }
// Vector Specialization -----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> constexpr genType log2(const vector<genType, i...>& x) {
return vector<genType, i...>(log2(x[i]) ...);
}
// sqrt ================================================================================================================ // sqrt ================================================================================================================
/// ///
@@ -216,13 +180,6 @@ template<typename genType> constexpr genType sqrt(genType x) {
} }
// Vector Specialization -----------------------------------------------------------------------------------------------
template<typename genType, size_t...i> constexpr genType sqrt(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sqrt(x[i]) ...);
}
// inversesqrt ========================================================================================================= // inversesqrt =========================================================================================================
/// ///
@@ -237,12 +194,39 @@ template<typename genType> constexpr genType inversesqrt(genType x) {
} }
// Vector Specialization ----------------------------------------------------------------------------------------------- // Internal ============================================================================================================
#ifndef FENNEC_DOXYGEN
template<typename genType, size_t...i>
constexpr vector<genType, i...> pow(const vector<genType, i...> & x, const vector<genType, i...> & y) {
return vector<genType, i...>(fennec::pow(x[i], y[i]) ...);
}
template<typename genType, size_t...i> constexpr vector<genType, i...> exp(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::exp(x[i]) ...);
}
template<typename genType, size_t...i> constexpr vector<genType, i...> exp2(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::exp2(x[i]) ...);
}
template<typename genType, size_t...i> constexpr genType log(const vector<genType, i...>& x) {
return vector<genType, i...>(log(x[i]) ...);
}
template<typename genType, size_t...i> constexpr genType log2(const vector<genType, i...>& x) {
return vector<genType, i...>(log2(x[i]) ...);
}
template<typename genType, size_t...i> constexpr genType sqrt(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sqrt(x[i]) ...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> inversesqrt(const vector<genType, i...>& x) { constexpr vector<genType, i...> inversesqrt(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::inversesqrt(x[i]) ...); return vector<genType, i...>(fennec::inversesqrt(x[i]) ...);
} }
#endif // FENNEC_DOXYGEN
} }

View File

@@ -21,7 +21,9 @@
#include <fennec/lang/limits.h> #include <fennec/lang/limits.h>
#include <fennec/math/common.h>
#include <fennec/math/trigonometric.h> #include <fennec/math/trigonometric.h>
#include <fennec/math/ext/quaternion.h> #include <fennec/math/ext/quaternion.h>
namespace fennec namespace fennec

View File

@@ -19,6 +19,7 @@
#ifndef FENNEC_MATH_EXT_QUATERNION_H #ifndef FENNEC_MATH_EXT_QUATERNION_H
#define FENNEC_MATH_EXT_QUATERNION_H #define FENNEC_MATH_EXT_QUATERNION_H
#include <fennec/math/geometric.h>
#include <fennec/math/vector_base.h> #include <fennec/math/vector_base.h>
namespace fennec namespace fennec
@@ -31,15 +32,13 @@ template<typename genType> using qua = quaternion<genType>;
using quat = qua<float>; using quat = qua<float>;
using dquat = qua<double>; using dquat = qua<double>;
///
/// \brief Dot Function #ifndef FENNEC_DOXYGEN
/// \param x the first quaternion
/// \param y the second quaternion
/// \returns The sum of component products.
template<typename genType> template<typename genType>
constexpr genType dot(const qua<genType>& x, const qua<genType>& y) { constexpr genType dot(const qua<genType>& x, const qua<genType>& y) {
return x.w*y.w + x.x*y.x + x.y*y.y + x.z*y.z; return x.w*y.w + x.x*y.x + x.y*y.y + x.z*y.z;
} }
#endif
/// ///
/// \brief Square Norm Function /// \brief Square Norm Function
@@ -53,7 +52,7 @@ constexpr genType sqnorm(const qua<genType>& q) {
/// ///
/// \brief Norm Function /// \brief Norm Function
/// \param q The Quaternion /// \param q The Quaternion
/// \returns The Hamilton Tensor of `q` /// \returns The Hamilton Tensor of \f$q\f$
template<typename genType> template<typename genType>
constexpr genType norm(const qua<genType>& q) { constexpr genType norm(const qua<genType>& q) {
return fennec::sqrt(sqnorm(q)); return fennec::sqrt(sqnorm(q));
@@ -62,7 +61,7 @@ constexpr genType norm(const qua<genType>& q) {
/// ///
/// \brief Unit Function (Versor) /// \brief Unit Function (Versor)
/// \param q The Quaternion /// \param q The Quaternion
/// \returns A Quaternion of `q` with norm `1` /// \returns A Quaternion of \f$q\f$ with norm \f$1\f$
template<typename genType> template<typename genType>
constexpr qua<genType> unit(const qua<genType>& q) { constexpr qua<genType> unit(const qua<genType>& q) {
genType n = fennec::norm(q); genType n = fennec::norm(q);
@@ -78,18 +77,20 @@ constexpr qua<genType> reciprocal(const qua<genType>& q) {
return ~q / fennec::sqnorm(q); return ~q / fennec::sqnorm(q);
} }
///
/// \brief quaternion structure type
/// \tparam ScalarT the base scalar type
template<typename ScalarT> template<typename ScalarT>
struct quaternion : detail::vector_base_type<ScalarT, 4> struct quaternion : detail::vector_base_type<ScalarT, 4>
{ {
public: public:
// Typedefs ============================================================================================================ // Typedefs ============================================================================================================
using base_type = detail::vector_base_type<ScalarT, 4>; using base_type = detail::vector_base_type<ScalarT, 4>; //!< base vector_storage type
using scalar_t = ScalarT; using scalar_t = ScalarT; //!< scalar type
using quat_t = quaternion; using quat_t = quaternion; //!< quaternion type
using vec3_t = vec<scalar_t, 3>; using vec3_t = vec<scalar_t, 3>; //!< scalar equivalent vec3 type
using vec4_t = vec<scalar_t, 4>; using vec4_t = vec<scalar_t, 4>; //!< scalar equivalent vec4 type
using base_type::data; using base_type::data;
using base_type::x, base_type::y, base_type::z, base_type::w; using base_type::x, base_type::y, base_type::z, base_type::w;
@@ -98,7 +99,7 @@ public:
// Constructors ======================================================================================================== // Constructors ========================================================================================================
/// ///
/// \brief Default Constructor, creates a quaternion such that the real part is `1` and all others are `0` /// \brief Default Constructor, creates a quaternion such that the real part is \f$1\f$ and all others are \f$0\f$
constexpr quaternion() { constexpr quaternion() {
data = { 0, 0, 0, 1 }; data = { 0, 0, 0, 1 };
} }
@@ -106,9 +107,9 @@ public:
/// ///
/// \brief Component Constructor /// \brief Component Constructor
/// \param w The scalar component /// \param w The scalar component
/// \param x The coefficient of the `i` component /// \param x The coefficient of the \f$i\f$ component
/// \param y The coefficient of the `j` component /// \param y The coefficient of the \f$j\f$ component
/// \param z The coefficient of the `k` component /// \param z The coefficient of the \f$k\f$ component
constexpr quaternion(scalar_t w, scalar_t x, scalar_t y, scalar_t z) { constexpr quaternion(scalar_t w, scalar_t x, scalar_t y, scalar_t z) {
data = { x, y, z, w }; data = { x, y, z, w };
} }
@@ -159,6 +160,7 @@ public:
/// ///
/// \brief Copy Assignment Operator /// \brief Copy Assignment Operator
/// \param q The quaternion to copy /// \param q The quaternion to copy
/// \returns a reference to self
constexpr quat_t& operator=(const quat_t& q) { constexpr quat_t& operator=(const quat_t& q) {
data = q.data; data = q.data;
return *this; return *this;
@@ -167,6 +169,7 @@ public:
/// ///
/// \brief Move Assignment Operator /// \brief Move Assignment Operator
/// \param q The quaternion to move /// \param q The quaternion to move
/// \returns a reference to self
constexpr quat_t& operator=(quat_t&& q) noexcept { constexpr quat_t& operator=(quat_t&& q) noexcept {
data = q.data; data = q.data;
return *this; return *this;
@@ -179,7 +182,7 @@ public:
/// \brief Equality Operator /// \brief Equality Operator
/// \param lhs the left hand side of the expression /// \param lhs the left hand side of the expression
/// \param rhs the right hand side of the expression /// \param rhs the right hand side of the expression
/// \returns `true` when all components of `lhs` and `rhs` are equal, false otherwise /// \returns \f$true\f$ when all components of \f$lhs\f$ and \f$rhs\f$ are equal, false otherwise
constexpr friend bool operator==(const quat_t& lhs, const quat_t& rhs) { constexpr friend bool operator==(const quat_t& lhs, const quat_t& rhs) {
return lhs.data == rhs.data; return lhs.data == rhs.data;
} }
@@ -188,7 +191,7 @@ public:
/// \brief Inequality Operator /// \brief Inequality Operator
/// \param lhs the left hand side of the expression /// \param lhs the left hand side of the expression
/// \param rhs the right hand side of the expression /// \param rhs the right hand side of the expression
/// \returns `true` when any component of `lhs` and `rhs` are not equal, false otherwise /// \returns \f$true\f$ when any component of \f$lhs\f$ and \f$rhs\f$ are not equal, false otherwise
constexpr friend bool operator!=(const quat_t& lhs, const quat_t& rhs) { constexpr friend bool operator!=(const quat_t& lhs, const quat_t& rhs) {
return lhs.data != rhs.data; return lhs.data != rhs.data;
} }
@@ -199,7 +202,7 @@ public:
/// ///
/// \brief Unary Negation Operator /// \brief Unary Negation Operator
/// \param rhs The quaternion to negate /// \param rhs The quaternion to negate
/// \returns A quaternion with each component of `rhs` negated. /// \returns A quaternion with each component of \f$rhs\f$ negated.
constexpr friend quat_t operator-(const quat_t& rhs) { constexpr friend quat_t operator-(const quat_t& rhs) {
return quat_t(-rhs.w, -rhs.x, -rhs.y, -rhs.z); return quat_t(-rhs.w, -rhs.x, -rhs.y, -rhs.z);
} }
@@ -207,7 +210,7 @@ public:
/// ///
/// \brief Unary Conjugation Operator /// \brief Unary Conjugation Operator
/// \param rhs The quaternion to conjugate /// \param rhs The quaternion to conjugate
/// \returns A quaternion with each vector component of `rhs` negated. /// \returns A quaternion with each vector component of \f$rhs\f$ negated.
constexpr friend quat_t operator~(const quat_t& rhs) { constexpr friend quat_t operator~(const quat_t& rhs) {
return quat_t(rhs.w, -rhs.x, -rhs.y, -rhs.z); return quat_t(rhs.w, -rhs.x, -rhs.y, -rhs.z);
} }
@@ -219,7 +222,7 @@ public:
/// \brief Quaternion-Scalar Multiplication Operator /// \brief Quaternion-Scalar Multiplication Operator
/// \param lhs The quaternion /// \param lhs The quaternion
/// \param rhs The scalar /// \param rhs The scalar
/// \returns A quaternion with each component of `lhs` multiplied by `rhs` /// \returns A quaternion with each component of \f$lhs\f$ multiplied by \f$rhs\f$
constexpr friend quat_t operator*(const quat_t& lhs, scalar_t rhs) { constexpr friend quat_t operator*(const quat_t& lhs, scalar_t rhs) {
return quat_t(lhs.w * rhs, lhs.x * rhs, lhs.y * rhs, lhs.z * rhs); return quat_t(lhs.w * rhs, lhs.x * rhs, lhs.y * rhs, lhs.z * rhs);
} }
@@ -228,7 +231,7 @@ public:
/// \brief Scalar-Quaternion Multiplication Operator /// \brief Scalar-Quaternion Multiplication Operator
/// \param lhs The scalar /// \param lhs The scalar
/// \param rhs The quaternion /// \param rhs The quaternion
/// \returns A quaternion with each component of `rhs` multiplied by `lhs` /// \returns A quaternion with each component of \f$rhs\f$ multiplied by \f$lhs\f$
constexpr friend quat_t operator*(scalar_t lhs, const quat_t& rhs) { constexpr friend quat_t operator*(scalar_t lhs, const quat_t& rhs) {
return quat_t(lhs * rhs.w, lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); return quat_t(lhs * rhs.w, lhs * rhs.x, lhs * rhs.y, lhs * rhs.z);
} }
@@ -237,7 +240,7 @@ public:
/// \brief Quaternion-Scalar Division Operator /// \brief Quaternion-Scalar Division Operator
/// \param lhs The quaternion /// \param lhs The quaternion
/// \param rhs The scalar /// \param rhs The scalar
/// \returns A quaternion with each component of `lhs` divided by `rhs` /// \returns A quaternion with each component of \f$lhs\f$ divided by \f$rhs\f$
constexpr friend quat_t operator/(const quat_t& lhs, scalar_t rhs) { constexpr friend quat_t operator/(const quat_t& lhs, scalar_t rhs) {
return quat_t(lhs.w / rhs, lhs.x / rhs, lhs.y / rhs, lhs.z / rhs); return quat_t(lhs.w / rhs, lhs.x / rhs, lhs.y / rhs, lhs.z / rhs);
} }
@@ -246,7 +249,7 @@ public:
/// \brief Scalar-Quaternion Division Operator /// \brief Scalar-Quaternion Division Operator
/// \param lhs The scalar /// \param lhs The scalar
/// \param rhs The quaternion /// \param rhs The quaternion
/// \returns A quaternion with each component of `rhs` divided by `lhs` /// \returns A quaternion with each component of \f$rhs\f$ divided by \f$lhs\f$
constexpr friend quat_t operator/(scalar_t lhs, const quat_t& rhs) { constexpr friend quat_t operator/(scalar_t lhs, const quat_t& rhs) {
return quat_t(lhs / rhs.w, lhs / rhs.x, lhs / rhs.y, lhs / rhs.z); return quat_t(lhs / rhs.w, lhs / rhs.x, lhs / rhs.y, lhs / rhs.z);
} }
@@ -258,7 +261,7 @@ public:
/// \brief Quaternion-Scalar Multiplication Assignment Operator /// \brief Quaternion-Scalar Multiplication Assignment Operator
/// \param lhs The quaternion /// \param lhs The quaternion
/// \param rhs The scalar /// \param rhs The scalar
/// \returns `lhs` with each component multiplied by `rhs` /// \returns \f$lhs\f$ with each component multiplied by \f$rhs\f$
constexpr friend quat_t& operator*=(quat_t& lhs, scalar_t rhs) { constexpr friend quat_t& operator*=(quat_t& lhs, scalar_t rhs) {
lhs.x *= rhs; lhs.x *= rhs;
lhs.y *= rhs; lhs.y *= rhs;
@@ -271,7 +274,7 @@ public:
/// \brief Quaternion-Scalar Division Assignment Operator /// \brief Quaternion-Scalar Division Assignment Operator
/// \param lhs The quaternion /// \param lhs The quaternion
/// \param rhs The scalar /// \param rhs The scalar
/// \returns `lhs` with each component divided by `rhs` /// \returns \f$lhs\f$ with each component divided by \f$rhs\f$
constexpr friend quat_t& operator/=(const quat_t& lhs, scalar_t rhs) { constexpr friend quat_t& operator/=(const quat_t& lhs, scalar_t rhs) {
lhs.x /= rhs; lhs.x /= rhs;
lhs.y /= rhs; lhs.y /= rhs;
@@ -283,7 +286,11 @@ public:
// Quaternion Vector Arithmetic Operators ============================================================================== // Quaternion Vector Arithmetic Operators ==============================================================================
/// \brief ///
/// \brief Quaternion-Vector Multiplication Operator
/// \param q the quaternion
/// \param v the vector
/// \returns the linear algebraic product of \f$q\f$ and \f$v\f$
constexpr friend vec3_t operator*(const quat_t& q, const vec3_t& v) { constexpr friend vec3_t operator*(const quat_t& q, const vec3_t& v) {
const vec3_t u = q.xyz; const vec3_t u = q.xyz;
const vec3_t uv = fennec::cross(u, v); const vec3_t uv = fennec::cross(u, v);
@@ -291,23 +298,43 @@ public:
return v + (uv*q.w + uuv) * scalar_t(2); return v + (uv*q.w + uuv) * scalar_t(2);
} }
///
/// \brief Vector-Quaternion Multiplication Operator
/// \param v the vector
/// \param q the quaternion
/// \returns the linear algebraic product of \f$v\f$ and \f$q\f$
constexpr friend vec3_t operator*(const vec3_t& v, const quat_t& q) { constexpr friend vec3_t operator*(const vec3_t& v, const quat_t& q) {
return fennec::reciprocal(q) * v; return fennec::reciprocal(q) * v;
} }
///
/// \brief Quaternion-Vector Multiplication Operator
/// \param q the quaternion
/// \param v the vector
/// \returns the linear algebraic product of \f$q\f$ and \f$v\f$
constexpr friend vec4_t operator*(const quat_t& q, const vec4_t& v) { constexpr friend vec4_t operator*(const quat_t& q, const vec4_t& v) {
return vec4_t(q * v.xyz, v.w); return vec4_t(q * v.xyz, v.w);
} }
///
/// \brief Vector-Quaternion Multiplication Operator
/// \param v the vector
/// \param q the quaternion
/// \returns the linear algebraic product of \f$v\f$ and \f$q\f$
constexpr friend vec4_t operator*(const vec4_t& v, const quat_t& q) { constexpr friend vec4_t operator*(const vec4_t& v, const quat_t& q) {
return fennec::reciprocal(q) * v; return fennec::reciprocal(q) * v;
} }
// Quaternion Quaternion Arithmetic Operators ========================================================================== // Quaternion-Quaternion Arithmetic Operators ==========================================================================
constexpr friend quaternion operator+(const quaternion& lhs, const quaternion& rhs) { ///
return quaternion( /// \brief Quaternion-Quaternion Addition Operator
/// \param lhs the left hand side
/// \param rhs the right hand side
/// \returns the component-wise sum of \f$lhs\f$ and \f$rhs\f$
constexpr friend quat_t operator+(const quat_t& lhs, const quat_t& rhs) {
return quat_t(
lhs.w + rhs.w, lhs.w + rhs.w,
lhs.x + rhs.x, lhs.x + rhs.x,
lhs.y + rhs.y, lhs.y + rhs.y,
@@ -315,8 +342,13 @@ public:
); );
} }
constexpr friend quaternion operator-(const quaternion& lhs, const quaternion& rhs) { ///
return quaternion( /// \brief Quaternion-Quaternion Subtraction Operator
/// \param lhs the left hand side
/// \param rhs the right hand side
/// \returns the component-wise difference of \f$lhs\f$ and \f$rhs\f$
constexpr friend quat_t operator-(const quat_t& lhs, const quat_t& rhs) {
return quat_t(
lhs.w - rhs.w, lhs.w - rhs.w,
lhs.x - rhs.x, lhs.x - rhs.x,
lhs.y - rhs.y, lhs.y - rhs.y,
@@ -324,8 +356,13 @@ public:
); );
} }
constexpr friend quaternion operator*(const quaternion& lhs, const quaternion& rhs) { ///
return quaternion( /// \brief Quaternion-Quaternion Multiplication Operator
/// \param lhs the left hand side
/// \param rhs the right hand side
/// \returns the linear algebraic product of \f$lhs\f$ and \f$rhs\f$
constexpr friend quat_t operator*(const quat_t& lhs, const quat_t& rhs) {
return quat_t(
lhs.w*rhs.w - lhs.x*rhs.x - lhs.y*rhs.y - lhs.z * rhs.z, lhs.w*rhs.w - lhs.x*rhs.x - lhs.y*rhs.y - lhs.z * rhs.z,
lhs.w*rhs.x + lhs.x*rhs.w + lhs.y*rhs.z - lhs.z * rhs.y, lhs.w*rhs.x + lhs.x*rhs.w + lhs.y*rhs.z - lhs.z * rhs.y,
lhs.w*rhs.y - lhs.x*rhs.z + lhs.y*rhs.w + lhs.z * rhs.x, lhs.w*rhs.y - lhs.x*rhs.z + lhs.y*rhs.w + lhs.z * rhs.x,
@@ -336,7 +373,12 @@ public:
// Quaternion Quaternion Arithmetic Assignment Operators =============================================================== // Quaternion Quaternion Arithmetic Assignment Operators ===============================================================
constexpr friend quaternion& operator+=(quaternion& lhs, const quaternion& rhs) { ///
/// \brief Quaternion-Quaternion Addition Assignment Operator
/// \param lhs the left hand side
/// \param rhs the right hand side
/// \returns the component-wise sum of \f$lhs\f$ and \f$rhs\f$ stored in \f$lhs\f$
constexpr friend quat_t& operator+=(quat_t& lhs, const quat_t& rhs) {
lhs.w += rhs.w; lhs.w += rhs.w;
lhs.x += rhs.x; lhs.x += rhs.x;
lhs.y += rhs.y; lhs.y += rhs.y;
@@ -344,7 +386,12 @@ public:
return lhs; return lhs;
} }
constexpr friend quaternion& operator-=(quaternion& lhs, const quaternion& rhs) { ///
/// \brief Quaternion-Quaternion Subtraction Assignment Operator
/// \param lhs the left hand side
/// \param rhs the right hand side
/// \returns the component-wise difference of \f$lhs\f$ and \f$rhs\f$ stored in \f$lhs\f$
constexpr friend quat_t& operator-=(quat_t& lhs, const quat_t& rhs) {
lhs.w -= rhs.w; lhs.w -= rhs.w;
lhs.x -= rhs.x; lhs.x -= rhs.x;
lhs.y -= rhs.y; lhs.y -= rhs.y;
@@ -352,7 +399,12 @@ public:
return lhs; return lhs;
} }
constexpr friend quaternion& operator*=(quaternion& lhs, const quaternion& rhs) { ///
/// \brief Quaternion-Quaternion Multiplication Assignment Operator
/// \param lhs the left hand side
/// \param rhs the right hand side
/// \returns the linear algebraic product of \f$lhs\f$ and \f$rhs\f$ stored in \f$lhs\f$
constexpr friend quat_t& operator*=(quat_t& lhs, const quat_t& rhs) {
return lhs = lhs * rhs; return lhs = lhs * rhs;
} }

View File

@@ -0,0 +1,56 @@
// =====================================================================================================================
// 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 rect.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_MATH_EXT_RECT_H
#define FENNEC_MATH_EXT_RECT_H
#include <fennec/math/vector.h>
namespace fennec
{
///
/// \brief a math structure representing the bounds of a rectangle
/// \tparam ScalarT the scalar type
template<typename ScalarT>
struct rectangle {
tvec2<ScalarT> position; //!< the cartesian position of the box
tvec2<ScalarT> size; //!< the cartesian size of the box
};
using rect = rectangle<float_t>; //!< a floating point rectangle
using irect = rectangle<int32_t>; //!< an integer rectangle
using urect = rectangle<uint32_t>; //!< an unsigned integer rectangle
using drect = rectangle<double_t>; //!< a double-precision floating point rectangle
}
#endif // FENNEC_MATH_EXT_RECT_H

View File

@@ -32,7 +32,7 @@ namespace fennec
/// \param x a vector containing the position /// \param x a vector containing the position
/// \returns An identity matrix with the last column set to x /// \returns An identity matrix with the last column set to x
template<typename genType> template<typename genType>
constexpr mat<genType, 3, 3> translation(const vec<genType, 2>& x) { constexpr mat<genType, 3, 3> translation(const vector<genType, 0, 1>& x) {
return mat<genType, 3, 3>( return mat<genType, 3, 3>(
1, 0, 0, 1, 0, 0,
0, 1, 0, 0, 1, 0,
@@ -45,7 +45,7 @@ constexpr mat<genType, 3, 3> translation(const vec<genType, 2>& x) {
/// \param x a vector containing the position /// \param x a vector containing the position
/// \returns An identity matrix with the last column set to x /// \returns An identity matrix with the last column set to x
template<typename genType> template<typename genType>
constexpr mat<genType, 4, 4> translation(const vec<genType, 3>& x) { constexpr mat<genType, 4, 4> translation(const vector<genType, 0, 1, 2>& x) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
1, 0, 0, 0, 1, 0, 0, 0,
0, 1, 0, 0, 0, 1, 0, 0,
@@ -59,7 +59,7 @@ constexpr mat<genType, 4, 4> translation(const vec<genType, 3>& x) {
/// \param x a vector containing the scale for each axis /// \param x a vector containing the scale for each axis
/// \returns A diagonal matrix with the terms of x /// \returns A diagonal matrix with the terms of x
template<typename genType> template<typename genType>
constexpr mat<genType, 3, 3> scaling(const vec<genType, 2>& x) { constexpr mat<genType, 3, 3> scaling(const vector<genType, 0, 1>& x) {
return mat<genType, 3, 3>( return mat<genType, 3, 3>(
x.x, 0, 0, x.x, 0, 0,
0, x.y, 0, 0, x.y, 0,
@@ -72,7 +72,7 @@ constexpr mat<genType, 3, 3> scaling(const vec<genType, 2>& x) {
/// \param x a vector containing the scale for each axis /// \param x a vector containing the scale for each axis
/// \returns A diagonal matrix with the terms of x /// \returns A diagonal matrix with the terms of x
template<typename genType> template<typename genType>
constexpr mat<genType, 4, 4> scaling(const vec<genType, 3>& x) { constexpr mat<genType, 4, 4> scaling(const vector<genType, 0, 1, 2>& x) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
x.x, 0, 0, 0, x.x, 0, 0, 0,
0, x.y, 0, 0, 0, x.y, 0, 0,
@@ -81,6 +81,15 @@ constexpr mat<genType, 4, 4> scaling(const vec<genType, 3>& x) {
); );
} }
template<typename genType>
constexpr mat<genType, 3, 3> shear(const vector<genType, 0, 1>& x) {
return mat<genType, 3, 3>(
1, x.x, 0,
x.y, 1, 0,
0, 0, 1
);
}
/// ///
/// \brief Create a 2d rotation matrix /// \brief Create a 2d rotation matrix
/// \param a the angle of the rotation in radians /// \param a the angle of the rotation in radians
@@ -106,7 +115,7 @@ constexpr mat<genType, 3, 3> rotation(const genType a) {
/// \param a The rotation about the axis in radians /// \param a The rotation about the axis in radians
/// \returns A rotation matrix about A /// \returns A rotation matrix about A
template<typename genType> template<typename genType>
constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) { constexpr mat<genType, 4, 4> rotation(const vector<genType, 0, 1, 2>& A, genType a) {
// https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle // https://en.wikipedia.org/wiki/Rotation_matrix#Rotation_matrix_from_axis_and_angle
// Calculate sin and cos terms // Calculate sin and cos terms
@@ -114,8 +123,8 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) {
const genType s = fennec::sin(a); const genType s = fennec::sin(a);
// Calculate axis term // Calculate axis term
const vec<genType, 3> u = (fennec::one<genType>() - c) * A; const vector<genType, 0, 1, 2> u = (fennec::one<genType>() - c) * A;
const vec<genType, 3> v = s * A; const vector<genType, 0, 1, 2> v = s * A;
// Calculate the Matrix // Calculate the Matrix
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
@@ -126,20 +135,20 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& A, genType a) {
); );
} }
template<typename genType> // template<typename genType>
constexpr qua<genType> rotation(const vec<genType, 3>& axis, genType angle) { // constexpr qua<genType> rotation(const vector<genType, 0, 1, 2>& axis, genType angle) {
vec<genType, 3> a = fennec::normalize(axis); // vector<genType, 0, 1, 2> a = fennec::normalize(axis);
const genType s = fennec::sin(angle); // const genType s = fennec::sin(angle);
//
return qua<genType>(fennec::cos(angle * genType(0.5)), s * a); // return qua<genType>(fennec::cos(angle * genType(0.5)), s * a);
} // }
/// \brief enum to denote the unit-axis of rotation /// \brief enum to denote the unit-axis of rotation
enum rot_ enum rotation_
{ {
rot_x = 0 rotation_x = 0
, rot_y , rotation_y
, rot_z , rotation_z
}; };
/// ///
@@ -147,7 +156,7 @@ enum rot_
/// \tparam axis the unit axis to rotate around /// \tparam axis the unit axis to rotate around
/// \param a the angle, in radians /// \param a the angle, in radians
/// \returns a matrix that rotates vectors around a unit axis /// \returns a matrix that rotates vectors around a unit axis
template<typename genType, rot_ axis> template<typename genType, rotation_ axis>
constexpr mat<genType, 4, 4> rotation(genType a) { constexpr mat<genType, 4, 4> rotation(genType a) {
// https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations // https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations
@@ -156,7 +165,7 @@ constexpr mat<genType, 4, 4> rotation(genType a) {
const genType s = fennec::sin(a); const genType s = fennec::sin(a);
// Calculate the matrix // Calculate the matrix
if constexpr(axis == rot_x) { if constexpr(axis == rotation_x) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
1, 0, 0, 0, 1, 0, 0, 0,
0, c, -s, 0, 0, c, -s, 0,
@@ -164,7 +173,7 @@ constexpr mat<genType, 4, 4> rotation(genType a) {
0, 0, 0, 1 0, 0, 0, 1
); );
} }
else if constexpr(axis == rot_y) { else if constexpr(axis == rotation_y) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
c, 0, s, 0, c, 0, s, 0,
0, 0, 0, 0, 0, 0, 0, 0,
@@ -172,7 +181,7 @@ constexpr mat<genType, 4, 4> rotation(genType a) {
0, 0, 0, 1 0, 0, 0, 1
); );
} }
else if constexpr(axis == rot_z) { else if constexpr(axis == rotation_z) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
c, -s, 0, 0, c, -s, 0, 0,
s, c, 0, 0, s, c, 0, 0,
@@ -202,7 +211,7 @@ enum order_
/// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll) /// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll)
/// \returns a matrix that rotates vectors /// \returns a matrix that rotates vectors
template<typename genType, order_ order = order_zxy> template<typename genType, order_ order = order_zxy>
constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& E) { constexpr mat<genType, 4, 4> rotation(const vector<genType, 0, 1, 2>& E) {
// expanded using a CAS // expanded using a CAS
// Calculate sin and cos terms // Calculate sin and cos terms
@@ -221,40 +230,35 @@ constexpr mat<genType, 4, 4> rotation(const vec<genType, 3>& E) {
sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0, sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_xzy) {
else if constexpr(order == order_xzy) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg, -sg, cg*sb, 0, cb*cg, -sg, cg*sb, 0,
ca*cb*sg + sa*sb, ca*cg, cb*sa*sg - ca*sb, 0, ca*cb*sg + sa*sb, ca*cg, cb*sa*sg - ca*sb, 0,
cb*sa*sg - ca*sb, cg*sa, ca*cb + sa*sb*sg, 0, cb*sa*sg - ca*sb, cg*sa, ca*cb + sa*sb*sg, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_yxz) {
else if constexpr(order == order_yxz) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0, cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0,
ca*sg, ca*cg, -sa, 0, ca*sg, ca*cg, -sa, 0,
cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0, cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_yzx) {
else if constexpr(order == order_yzx) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0, cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0,
sg, ca*cg, -cg*sa, 0, sg, ca*cg, -cg*sa, 0,
-cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0, -cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_zxy) {
else if constexpr(order == order_zxy) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg - sa*sb*sg, -ca*sg, cb*sa*sg + cg*sb, 0, cb*cg - sa*sb*sg, -ca*sg, cb*sa*sg + cg*sb, 0,
cb*sg + cg*sa*sb, ca*cg, sb*sg - cb*cg*sa, 0, cb*sg + cg*sa*sb, ca*cg, sb*sg - cb*cg*sa, 0,
-ca*sb, sa, ca*cb, 0, -ca*sb, sa, ca*cb, 0,
0, 0, 0, 1 0, 0, 0, 1
); );
} } else if constexpr(order == order_zyx) {
else if constexpr(order == order_zyx) {
return mat<genType, 4, 4>( return mat<genType, 4, 4>(
cb*cg, cg*sa*sb - ca*sg, ca*cg*sb + sa*sg, 0, cb*cg, cg*sa*sb - ca*sg, ca*cg*sb + sa*sg, 0,
cb*sg, ca*cg + sa*sb*sg, ca*sb*sg - cg*sa, 0, cb*sg, ca*cg + sa*sb*sg, ca*sb*sg - cg*sa, 0,

View File

@@ -19,7 +19,6 @@
#ifndef FENNEC_MATH_EXT_TRIGONOMETRIC_H #ifndef FENNEC_MATH_EXT_TRIGONOMETRIC_H
#define FENNEC_MATH_EXT_TRIGONOMETRIC_H #define FENNEC_MATH_EXT_TRIGONOMETRIC_H
#include <fennec/math/trigonometric.h>
#include <fennec/math/ext/quaternion.h> #include <fennec/math/ext/quaternion.h>
namespace fennec namespace fennec
@@ -27,6 +26,7 @@ namespace fennec
// Angle Conversions =================================================================================================== // Angle Conversions ===================================================================================================
#ifndef FENNEC_DOXYGEN
template<typename genType> template<typename genType>
constexpr qua<genType> radians(const qua<genType>& degrees) { constexpr qua<genType> radians(const qua<genType>& degrees) {
return qua<genType>(degrees * 0.01745329251994329576923690768489); return qua<genType>(degrees * 0.01745329251994329576923690768489);
@@ -37,7 +37,6 @@ constexpr qua<genType> degrees(const qua<genType>& radians) {
return qua<genType>(radians * 57.29577951308232087679815481410517); return qua<genType>(radians * 57.29577951308232087679815481410517);
} }
// Trigonometric Functions ============================================================================================= // Trigonometric Functions =============================================================================================
template<typename genType> template<typename genType>
@@ -162,6 +161,7 @@ constexpr qua<genType> atanh(const qua<genType>& x) {
fennec::atanh(x.z) fennec::atanh(x.z)
); );
} }
#endif
} }

View File

@@ -43,7 +43,7 @@
/// \code #include <fennec/math/geometric.h> \endcode /// \code #include <fennec/math/geometric.h> \endcode
/// ///
/// ///
/// \section Geometric Functions /// \section fennec_math_geometric_section_functions Geometric Functions
/// ///
/// <table width="100%" class="fieldtable" id="table_fennec_math_geometric_functions"> /// <table width="100%" class="fieldtable" id="table_fennec_math_geometric_functions">
/// <tr><th style="vertical-align: top">Syntax /// <tr><th style="vertical-align: top">Syntax
@@ -118,7 +118,7 @@ namespace fennec
// dot ----------------------------------------------------------------------------------------------------------------- // dot -----------------------------------------------------------------------------------------------------------------
/// ///
/// \brief Returns the dot product of \f$x\f$ and \f$y\f$, i.e., \f$x_0 \cdot y_0 + x_0 \cdot y_0 + \ldots\f$ /// \brief Returns the dot product of \f$x\f$ and \f$y\f$, i.e., \f$x_0 \cdot y_0 + x_1 \cdot y_1 + \ldots\f$
/// ///
/// \returns the dot product of \f$x\f$ and \f$y\f$, i.e., \f$x_0 \cdot y_0 + x_0 \cdot y_0 + \ldots\f$ <br><br> /// \returns the dot product of \f$x\f$ and \f$y\f$, i.e., \f$x_0 \cdot y_0 + x_0 \cdot y_0 + \ldots\f$ <br><br>
/// \details we can represent this in linear algebra as the following, <br><br> /// \details we can represent this in linear algebra as the following, <br><br>

View File

@@ -46,7 +46,7 @@
/// ///
/// \code #include <fennec/math/math.h> \endcode /// \code #include <fennec/math/math.h> \endcode
/// ///
/// The \ref fennec Math Library is composed of the modules listed in below. /// The fennecMath Library is composed of the modules listed in below.
/// The overarching goal of this math library is to implement the math types and functions of the /// The overarching goal of this math library is to implement the math types and functions of the
/// [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf). /// [OpenGL 4.6 Shading Language Specification](https://registry.khronos.org/OpenGL/specs/gl/GLSLangSpec.4.60.pdf).
/// ///

View File

@@ -118,7 +118,7 @@ using dmat4x4 = tmat4x4<double_t>; ///< Specification for size glsl double matri
/// ///
/// \brief Multiply matrix \$x\f$ by matrix \f$y\f$ component-wise. /// \brief Multiply matrix \f$x\f$ by matrix \f$y\f$ component-wise.
/// \details Multiply matrix x by matrix y component-wise, i.e., result[i][j] is the scalar product of x[i][j] and y[i][j].<br><br> /// \details Multiply matrix x by matrix y component-wise, i.e., result[i][j] is the scalar product of x[i][j] and y[i][j].<br><br>
/// Note: to get linear algebraic matrix multiplication, use /// Note: to get linear algebraic matrix multiplication, use
/// the multiply operator (*) /// the multiply operator (*)
@@ -160,29 +160,29 @@ constexpr mat<scalar, rows, sizeof...(cols)> transpose(const matrix<scalar, rows
} }
/// ///
/// \brief Returns the determinant of m. /// \brief Returns the determinant of \f$m\f$.
/// \param m the matrix
/// \returns the determinant of m. /// \returns the determinant of m.
template<typename scalar, size_t rows, size_t...cols> template<typename scalar, size_t rows, size_t...cols>
constexpr scalar determinant(const matrix<scalar, rows, cols...>&) noexcept { constexpr scalar determinant(const matrix<scalar, rows, cols...>&) noexcept {
// ReSharper disable once CppStaticAssertFailure
static_assert(false, "implementation undefined"); static_assert(false, "implementation undefined");
return 0; return 0;
} }
///
/// \brief Returns the determinant of \f$m\f$.
/// \returns \f$m^{-1}\f$
template<typename scalar, size_t rows, size_t...cols> template<typename scalar, size_t rows, size_t...cols>
constexpr matrix<scalar, rows, cols...> inverse(const matrix<scalar, rows, cols...>&) noexcept { constexpr matrix<scalar, rows, cols...> inverse(const matrix<scalar, rows, cols...>&) noexcept {
// ReSharper disable once CppStaticAssertFailure
static_assert(false, "implementation undefined"); static_assert(false, "implementation undefined");
return 1; return 1;
} }
/// ///
/// \brief /// \brief matrix structure
/// \tparam ScalarT /// \tparam ScalarT The scalar type
/// \tparam RowsV /// \tparam RowsV The number of rows
/// \tparam ColIndicesV /// \tparam ColIndicesV The indices of the columns
template<typename ScalarT, size_t RowsV, size_t...ColIndicesV> template<typename ScalarT, size_t RowsV, size_t...ColIndicesV> requires(is_scalar_v<ScalarT>)
struct matrix struct matrix
{ {
// Assertions ========================================================================================================== // Assertions ==========================================================================================================
@@ -251,7 +251,6 @@ struct matrix
/// \param mat matrix to copy /// \param mat matrix to copy
constexpr matrix(const matrix_t& mat) constexpr matrix(const matrix_t& mat)
: data{ mat.data } { : data{ mat.data } {
} }
/// ///
@@ -261,7 +260,34 @@ struct matrix
/// \param mat matrix to move /// \param mat matrix to move
constexpr matrix(matrix_t&& mat) noexcept constexpr matrix(matrix_t&& mat) noexcept
: data{ mat.data } { : data{ mat.data } {
}
///
/// \brief scalar conversion operator
/// \tparam OScalarT the scalar type of the other matrix
/// \param mat the matrix to convert
template<typename OScalarT>
constexpr matrix(const matrix<OScalarT, RowsV, ColIndicesV...>& mat)
: matrix() {
for (size_t i = 0; i < columns; ++i) {
for (size_t j = 0; j < rows; ++j) {
data[i][j] = scalar_t(mat[i][j]);
}
}
}
///
/// \brief scalar conversion operator
/// \tparam OScalarT the scalar type of the other matrix
/// \param mat the matrix to convert
template<typename OScalarT>
constexpr matrix(matrix<OScalarT, RowsV, ColIndicesV...>&& mat) noexcept
: matrix() {
for (size_t i = 0; i < columns; ++i) {
for (size_t j = 0; j < rows; ++j) {
data[i][j] = scalar_t(mat[i][j]);
}
}
} }
@@ -331,7 +357,9 @@ struct matrix
/// @{ /// @{
/// ///
/// \copydetails matrix::operator[](size_t) const /// \details
/// \param i the index
/// \returns the column at index \f$i\f$
constexpr column_t& operator[](size_t i) { constexpr column_t& operator[](size_t i) {
return data[i]; return data[i];
} }
@@ -347,16 +375,21 @@ struct matrix
} }
/// ///
/// \copydetails matrix::operator()(size_t, size_t) const /// \details
/// \param i the column
/// \param j the row
/// \returns the element in column \f$i\f$, row \f$j\f$
constexpr scalar_t& operator[](size_t i, size_t j) { constexpr scalar_t& operator[](size_t i, size_t j) {
return data[i][j]; return data[i][j];
} }
/// ///
/// \brief returns the cell in row \p j column \p i /// \brief returns the cell in row \p j column \p i
///
/// \details
/// \param i the column /// \param i the column
/// \param j the row /// \param j the row
/// \returns the cell at the specified index. /// \returns the element in column \f$i\f$, row \f$j\f$
constexpr scalar_t operator[](size_t i, size_t j) const { constexpr scalar_t operator[](size_t i, size_t j) const {
return data[i][j]; return data[i][j];
} }
@@ -604,7 +637,7 @@ struct matrix
/// \brief matrix comparison operator /// \brief matrix comparison operator
/// \param lhs the first matrix /// \param lhs the first matrix
/// \param rhs the second matrix /// \param rhs the second matrix
/// \returns a boolean value that contains \f$true\f$ when all components of `lhs` and `rhs` are equal and \f$false\f$ otherwise /// \returns a boolean value that contains \f$true\f$ when all components of \f$lhs\f$ and \f$rhs\f$ are equal and \f$false\f$ otherwise
constexpr friend bool operator==(const matrix_t& lhs, const matrix_t& rhs) { constexpr friend bool operator==(const matrix_t& lhs, const matrix_t& rhs) {
return lhs.data == rhs.data; return lhs.data == rhs.data;
} }
@@ -613,7 +646,7 @@ struct matrix
/// \brief matrix comparison operator /// \brief matrix comparison operator
/// \param lhs the first matrix /// \param lhs the first matrix
/// \param rhs the second matrix /// \param rhs the second matrix
/// \returns a boolean value that contains \f$true\f$ when all components of `lhs` and `rhs` are not equal and \f$false\f$ otherwise /// \returns a boolean value that contains \f$true\f$ when all components of \f$lhs\f$ and \f$rhs\f$ are not equal and \f$false\f$ otherwise
constexpr friend bool operator!=(const matrix_t& lhs, const matrix_t& rhs) { constexpr friend bool operator!=(const matrix_t& lhs, const matrix_t& rhs) {
return lhs.data != rhs.data; return lhs.data != rhs.data;
} }
@@ -622,6 +655,7 @@ struct matrix
/// \brief performs a linear algebraic matrix multiplication /// \brief performs a linear algebraic matrix multiplication
/// \param lhs the rows to multiply with /// \param lhs the rows to multiply with
/// \param rhs the columns to multiply with /// \param rhs the columns to multiply with
/// \returns a matrix with the result of the multiplication
template<size_t ORowsV, size_t...OColIndicesV> requires(columns == ORowsV) template<size_t ORowsV, size_t...OColIndicesV> requires(columns == ORowsV)
constexpr friend matrix<scalar_t, RowsV, OColIndicesV...> operator*(const matrix_t& lhs, const matrix<scalar_t, ORowsV, OColIndicesV...>& rhs) { constexpr friend matrix<scalar_t, RowsV, OColIndicesV...> operator*(const matrix_t& lhs, const matrix<scalar_t, ORowsV, OColIndicesV...>& rhs) {
return matrix<scalar_t, RowsV, OColIndicesV...>( return matrix<scalar_t, RowsV, OColIndicesV...>(
@@ -629,6 +663,15 @@ struct matrix
); );
} }
///
/// \brief performs a linear algebraic matrix multiplication assignment
/// \param rhs the columns to multiply with
/// \returns a reference to self
template<size_t ORowsV, size_t...OColIndicesV> requires(columns == ORowsV)
constexpr matrix<scalar_t, RowsV, OColIndicesV...>& operator*=(const matrix<scalar_t, ORowsV, OColIndicesV...>& rhs) {
return *this = *this * rhs;
}
/// @} /// @}
@@ -637,13 +680,15 @@ struct matrix
public: public:
///
/// \param mat the matrix to transpose
/// \returns \f$m^T\f$
static constexpr matrix_t transpose(const transpose_t& mat) { static constexpr matrix_t transpose(const transpose_t& mat) {
return matrix_t(fennec::row(mat, ColIndicesV)...); return matrix_t(fennec::row(mat, ColIndicesV)...);
} }
private: private:
// ReSharper disable once CppMemberFunctionMayBeStatic
template<size_t i0 = 0> template<size_t i0 = 0>
constexpr void _construct() { constexpr void _construct() {
// base case, does nothing, this will get optimized away // base case, does nothing, this will get optimized away

View File

@@ -41,10 +41,10 @@
/// ///
/// \code #include <fennec/math/scalar.h> \endcode /// \code #include <fennec/math/scalar.h> \endcode
/// ///
/// The \ref fennec Library considers any type that passes ```is_arithmetic<T>``` to be a \ref scalar "Scalar." Bools are /// The fennecLibrary considers any type that passes ```is_arithmetic<T>``` to be a \ref scalar "Scalar." Bools are
/// supported as a logical type. /// supported as a logical type.
/// ///
/// The GLSL Specification, and \ref fennec respectively, defines the following scalar types: /// The GLSL Specification, and fennecrespectively, defines the following scalar types:
/// ///
/// | Type | Corresponding Type | Meaning | /// | Type | Corresponding Type | Meaning |
/// |----------|-----------------------|------------------------------| /// |----------|-----------------------|------------------------------|
@@ -65,10 +65,10 @@
namespace fennec namespace fennec
{ {
using byte = uint8_t; using byte = uint8_t; //!< a single byte type
using ubyte = uint8_t; using ubyte = uint8_t; //!< a single unsigned byte type
using ushort = uint16_t; using ushort = uint16_t; //!< an unsigned short type
using uint = uint32_t; using uint = uint32_t; //!< an unsigned int type
} }

View File

@@ -41,7 +41,7 @@
/// ///
/// ///
#include <fennec/lang/const_sequences.h> #include <fennec/lang/metasequences.h>
#include <fennec/math/swizzle_storage.h> #include <fennec/math/swizzle_storage.h>
@@ -79,7 +79,7 @@ public:
/// \return The Value of the Swizzle as a Vector /// \return The Value of the Swizzle as a Vector
constexpr VectorT decay() const { constexpr VectorT decay() const {
VectorT res; VectorT res;
return decay_impl(res, make_index_sequence<size>{}); return decay_impl(res, make_index_metasequence<size>{});
} }
/// ///
@@ -93,7 +93,7 @@ public:
private: private:
template<size_t...VecIndicesV> template<size_t...VecIndicesV>
constexpr VectorT& decay_impl(VectorT& vec, const_index_sequence<VecIndicesV...>) { constexpr VectorT& decay_impl(VectorT& vec, index_metasequence<VecIndicesV...>) {
return ((vec[VecIndicesV] = this->data[IndicesV]), ..., vec); return ((vec[VecIndicesV] = this->data[IndicesV]), ..., vec);
} }
}; };

View File

@@ -23,26 +23,26 @@
namespace fennec::detail namespace fennec::detail
{ {
// ///
// \brief Backing storage struct for \ref fennec::swizzle "swizzle" /// \brief Backing storage struct for \ref fennec::swizzle "swizzle"
// \tparam DataT Data Type /// \tparam DataT Data Type
// \tparam ScalarT Element Types /// \tparam ScalarT Element Types
// \tparam IndicesV Swizzle Order /// \tparam IndicesV Swizzle Order
template<typename DataT, typename ScalarT, size_t...IndicesV> template<typename DataT, typename ScalarT, size_t...IndicesV>
struct swizzle_storage struct swizzle_storage
{ {
// ///
// \brief an array containing the indices of this swizzle /// \brief an array containing the indices of this swizzle
inline static constexpr size_t indices[] = { IndicesV... }; inline static constexpr size_t indices[] = { IndicesV... };
// ///
// \brief an array containing the scalar values of this swizzle /// \brief an array containing the scalar values of this swizzle
DataT data; DataT data;
// ///
// \brief element access, returns a copy of the value at index \p i /// \brief element access, returns a copy of the value at index \p i
// \param i the index /// \param i the index
// \returns a copy of the scalar value at index \p i /// \returns a copy of the scalar value at index \p i
constexpr ScalarT operator[](size_t i) const noexcept { constexpr ScalarT operator[](size_t i) const noexcept {
return data[indices[i]]; return data[indices[i]];
} }

View File

@@ -145,6 +145,8 @@
/// ///
#include <fennec/math/detail/_math.h> #include <fennec/math/detail/_math.h>
#include <fennec/math/vector.h>
#include <fennec/lang/types.h>
namespace fennec namespace fennec
{ {
@@ -167,11 +169,6 @@ constexpr genType radians(genType degrees) {
return genType(degrees * 0.01745329251994329576923690768489); return genType(degrees * 0.01745329251994329576923690768489);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> radians(const vector<genType, i...>& degrees) {
return vector<genType, i...>(degrees * 0.01745329251994329576923690768489);
}
/// ///
/// \brief Converts \f$radians\f$ to \f$degrees\f$, i.e., \f$radians\cdot\frac{\pi}{180}\f$ /// \brief Converts \f$radians\f$ to \f$degrees\f$, i.e., \f$radians\cdot\frac{\pi}{180}\f$
@@ -186,11 +183,6 @@ constexpr genType degrees(genType radians) {
return genType(radians * 57.29577951308232087679815481410517); return genType(radians * 57.29577951308232087679815481410517);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> degrees(const vector<genType, i...>& radians) {
return genType(radians * 57.29577951308232087679815481410517);
}
/// @} /// @}
@@ -213,11 +205,6 @@ constexpr genType sin(genType x) {
return ::sin(x); return ::sin(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> sin(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sin(x[i]) ...);
}
/// ///
/// \brief The Standard Trigonometric Cosine /// \brief The Standard Trigonometric Cosine
@@ -232,11 +219,6 @@ constexpr genType cos(genType x) {
return ::cos(x); return ::cos(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> cos(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::cos(x[i]) ...);
}
/// ///
/// \brief The Standard Trigonometric Tangent /// \brief The Standard Trigonometric Tangent
@@ -251,11 +233,6 @@ constexpr genType tan(genType x) {
return ::tan(x); return ::tan(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> tan(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::tan(x[i]) ...);
}
/// @} /// @}
@@ -277,11 +254,6 @@ constexpr genType asin(genType x) {
return ::asin(x); return ::asin(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> asin(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::asin(x[i]) ...);
}
/// ///
/// \brief Arc Cosine. Returns an angle \f$\theta\f$ whose cosine is /a x. /// \brief Arc Cosine. Returns an angle \f$\theta\f$ whose cosine is /a x.
@@ -297,11 +269,6 @@ constexpr genType acos(genType x) {
return ::acos(x); return ::acos(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> acos(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::acos(x[i]) ...);
}
/// ///
/// \brief Arc Tangent. Returns an angle \f$\theta\f$ whose tangent is /a y_over_x. /// \brief Arc Tangent. Returns an angle \f$\theta\f$ whose tangent is /a y_over_x.
@@ -317,11 +284,6 @@ constexpr genType atan(genType y_over_x) {
return ::atan(y_over_x); return ::atan(y_over_x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> atan(const vector<genType, i...>& y_over_x) {
return vector<genType, i...>(fennec::atan(y_over_x[i]) ...);
}
/// ///
/// \brief Arc Tangent. Returns an angle whose tangent is \f$\frac{y}{x}\f$. /// \brief Arc Tangent. Returns an angle whose tangent is \f$\frac{y}{x}\f$.
@@ -338,11 +300,6 @@ constexpr genType atan(genType y, genType x) {
return ::atan2(y, x); return ::atan2(y, x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> atan(const vector<genType, i...>& y, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::atan(y[i], x[i]) ...);
}
/// @} /// @}
@@ -364,11 +321,6 @@ constexpr genType sinh(genType x) {
return ::sinh(x); return ::sinh(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> sinh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sinh(x[i]) ...);
}
/// ///
/// \brief Returns the Hyperbolic Cosine Function, \f$\frac{{e}^{x}+{e}^{-x}}{2}\f$ /// \brief Returns the Hyperbolic Cosine Function, \f$\frac{{e}^{x}+{e}^{-x}}{2}\f$
@@ -381,11 +333,6 @@ constexpr genType cosh(genType x) {
return ::cosh(x); return ::cosh(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> cosh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::cosh(x[i]) ...);
}
/// ///
/// \brief Returns the Hyperbolic Tangent Function, \f$\frac{\text{sinh}(x)}{\text{cosh}(x)}\f$ /// \brief Returns the Hyperbolic Tangent Function, \f$\frac{\text{sinh}(x)}{\text{cosh}(x)}\f$
@@ -398,10 +345,6 @@ constexpr genType tanh(genType x) {
return ::tanh(x); return ::tanh(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> tanh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::tanh(x[i]) ...);
}
/// @} /// @}
@@ -421,11 +364,6 @@ constexpr genType asinh(genType x) {
return ::asinh(x); return ::asinh(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> asinh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::asinh(x[i]) ...);
}
/// ///
/// \brief The Inverse Hyperbolic Cosine Function /// \brief The Inverse Hyperbolic Cosine Function
@@ -439,11 +377,6 @@ constexpr genType acosh(genType x) {
return ::acosh(x); return ::acosh(x);
} }
template<typename genType, size_t...i>
constexpr vector<genType, i...> acosh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::acosh(x[i]) ...);
}
/// ///
/// \brief The Inverse Hyperbolic Tangent Function /// \brief The Inverse Hyperbolic Tangent Function
@@ -457,12 +390,88 @@ constexpr genType atanh(genType x) {
return ::atanh(x); return ::atanh(x);
} }
/// @}
// Internal ============================================================================================================
#ifndef FENNEC_DOXYGEN
template<typename genType, size_t...i>
constexpr vector<genType, i...> radians(const vector<genType, i...>& degrees) {
return vector<genType, i...>(degrees * 0.01745329251994329576923690768489);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> degrees(const vector<genType, i...>& radians) {
return genType(radians * 57.29577951308232087679815481410517);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> sin(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sin(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> cos(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::cos(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> tan(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::tan(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> asin(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::asin(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> acos(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::acos(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> atan(const vector<genType, i...>& y_over_x) {
return vector<genType, i...>(fennec::atan(y_over_x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> atan(const vector<genType, i...>& y, const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::atan(y[i], x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> sinh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::sinh(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> cosh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::cosh(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> tanh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::tanh(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> asinh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::asinh(x[i]) ...);
}
template<typename genType, size_t...i>
constexpr vector<genType, i...> acosh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::acosh(x[i]) ...);
}
template<typename genType, size_t...i> template<typename genType, size_t...i>
constexpr vector<genType, i...> atanh(const vector<genType, i...>& x) { constexpr vector<genType, i...> atanh(const vector<genType, i...>& x) {
return vector<genType, i...>(fennec::atanh(x[i]) ...); return vector<genType, i...>(fennec::atanh(x[i]) ...);
} }
#endif
/// @}

View File

@@ -46,33 +46,33 @@
/// <table width="100%" class="fieldtable" id="table_fennec_math_vector_types"> /// <table width="100%" class="fieldtable" id="table_fennec_math_vector_types">
/// <tr><th>Type <th>Corresponding Type <th>Brief /// <tr><th>Type <th>Corresponding Type <th>Brief
/// <tr><th colspan=3 style="text-align: center;">Floats /// <tr><th colspan=3 style="text-align: center;">Floats
/// <tr><td>```vec2``` <td>\ref fennec::vec2 <td>\copybrief fennec::vec2 /// <tr><td>``\f$vec2\f$`` <td>\ref fennec::vec2 <td>\copybrief fennec::vec2
/// <tr><td>```vec3``` <td>\ref fennec::vec3 <td>\copybrief fennec::vec3 /// <tr><td>``\f$vec3\f$`` <td>\ref fennec::vec3 <td>\copybrief fennec::vec3
/// <tr><td>```vec4``` <td>\ref fennec::vec4 <td>\copybrief fennec::vec4 /// <tr><td>``\f$vec4\f$`` <td>\ref fennec::vec4 <td>\copybrief fennec::vec4
/// <tr><th colspan=3 style="text-align: center;">Doubles /// <tr><th colspan=3 style="text-align: center;">Doubles
/// <tr><td>```dvec2```<td>\ref fennec::dvec2 <td>\copybrief fennec::dvec2 /// <tr><td>``\f$dvec2\f$``<td>\ref fennec::dvec2 <td>\copybrief fennec::dvec2
/// <tr><td>```dvec3```<td>\ref fennec::dvec3 <td>\copybrief fennec::dvec3 /// <tr><td>``\f$dvec3\f$``<td>\ref fennec::dvec3 <td>\copybrief fennec::dvec3
/// <tr><td>```dvec4```<td>\ref fennec::dvec4 <td>\copybrief fennec::dvec4 /// <tr><td>``\f$dvec4\f$``<td>\ref fennec::dvec4 <td>\copybrief fennec::dvec4
/// <tr><th colspan=3 style="text-align: center;">Booleans /// <tr><th colspan=3 style="text-align: center;">Booleans
/// <tr><td>```bvec2``` <td>\ref fennec::bvec2 <td>\copybrief fennec::bvec2 /// <tr><td>``\f$bvec2\f$`` <td>\ref fennec::bvec2 <td>\copybrief fennec::bvec2
/// <tr><td>```bvec3``` <td>\ref fennec::bvec3 <td>\copybrief fennec::bvec3 /// <tr><td>``\f$bvec3\f$`` <td>\ref fennec::bvec3 <td>\copybrief fennec::bvec3
/// <tr><td>```bvec4``` <td>\ref fennec::bvec4 <td>\copybrief fennec::bvec4 /// <tr><td>``\f$bvec4\f$`` <td>\ref fennec::bvec4 <td>\copybrief fennec::bvec4
/// <tr><th colspan=3 style="text-align: center;">Integers /// <tr><th colspan=3 style="text-align: center;">Integers
/// <tr><td>```ivec2``` <td>\ref fennec::ivec2 <td>\copybrief fennec::ivec2 /// <tr><td>``\f$ivec2\f$`` <td>\ref fennec::ivec2 <td>\copybrief fennec::ivec2
/// <tr><td>```ivec3``` <td>\ref fennec::ivec3 <td>\copybrief fennec::ivec3 /// <tr><td>``\f$ivec3\f$`` <td>\ref fennec::ivec3 <td>\copybrief fennec::ivec3
/// <tr><td>```ivec4``` <td>\ref fennec::ivec4 <td>\copybrief fennec::ivec4 /// <tr><td>``\f$ivec4\f$`` <td>\ref fennec::ivec4 <td>\copybrief fennec::ivec4
/// <tr><th colspan=3 style="text-align: center;">Unsigned Integers /// <tr><th colspan=3 style="text-align: center;">Unsigned Integers
/// <tr><td>```uvec2``` <td>\ref fennec::uvec2 <td>\copybrief fennec::uvec2 /// <tr><td>``\f$uvec2\f$`` <td>\ref fennec::uvec2 <td>\copybrief fennec::uvec2
/// <tr><td>```uvec3``` <td>\ref fennec::uvec3 <td>\copybrief fennec::uvec3 /// <tr><td>``\f$uvec3\f$`` <td>\ref fennec::uvec3 <td>\copybrief fennec::uvec3
/// <tr><td>```uvec4``` <td>\ref fennec::uvec4 <td>\copybrief fennec::uvec4 /// <tr><td>``\f$uvec4\f$`` <td>\ref fennec::uvec4 <td>\copybrief fennec::uvec4
/// </table> /// </table>
/// ///
/// ///
/// ///
/// \section vector_components Components /// \section vector_components Components
/// ///
/// Vectors are usually made up of one to four components, named ```x```, ```y```, ```z```, and ```w```. /// Vectors are usually made up of one to four components, named ``\f$x\f$``, ``\f$y\f$``, ``\f$z\f$``, and ``\f$w\f$``.
/// Each component also has aliases for usage in colors ```rgba```, and texture coordinates ```stpq```. Accessing a /// Each component also has aliases for usage in colors ``\f$rgba\f$``, and texture coordinates ``\f$stpq\f$``. Accessing a
/// component outside the vector will cause an error at compile time, for example: /// component outside the vector will cause an error at compile time, for example:
/// ///
/// \code{.cpp} /// \code{.cpp}
@@ -103,6 +103,7 @@
/// ///
/// ///
#include <fennec/containers/initializer_list.h>
#include <fennec/math/detail/_fwd.h> #include <fennec/math/detail/_fwd.h>
#include <fennec/math/vector_base.h> #include <fennec/math/vector_base.h>
@@ -120,7 +121,7 @@ namespace fennec
/// \tparam ScalarT The type of the Components /// \tparam ScalarT The type of the Components
/// \tparam SizeV The number of Components /// \tparam SizeV The number of Components
template<typename ScalarT, size_t SizeV> template<typename ScalarT, size_t SizeV>
using vec = decltype(detail::_gen_vector<vector, ScalarT>(make_index_sequence<SizeV>{})); using vec = decltype(detail::_gen_vector<vector, ScalarT>(make_index_metasequence<SizeV>{}));
/// ///
@@ -177,7 +178,7 @@ using dvec4 = tvec4<double_t>;
/// \tparam ScalarT base \ref scalar type of each Component /// \tparam ScalarT base \ref scalar type of each Component
/// \tparam IndicesV index of each Component /// \tparam IndicesV index of each Component
/// \nosubgrouping /// \nosubgrouping
template<typename ScalarT, size_t... IndicesV> template<typename ScalarT, size_t... IndicesV> requires(is_scalar_v<ScalarT>)
struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)> struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
{ {
// Assertions ========================================================================================================== // Assertions ==========================================================================================================
@@ -337,6 +338,27 @@ struct vector : detail::vector_base_type<ScalarT, sizeof...(IndicesV)>
vector::_construct<0>(args...); vector::_construct<0>(args...);
} }
///
/// \brief initializer list constructor
/// \param init the initializer list
constexpr vector(initializer_list<ScalarT> init) {
size_t i = 0;
for (ScalarT x : init) {
data[i++] = x;
}
}
///
/// \brief initializer list conversion constructor
/// \param init the initializer list
template<typename OScalarT>
constexpr vector(initializer_list<OScalarT> init) {
size_t i = 0;
for (OScalarT x : init) {
data[i++] = ScalarT(x);
}
}
/// @} /// @}
@@ -1086,22 +1108,22 @@ private:
} }
template<size_t OffsetV> template<size_t OffsetV>
constexpr void _insert(ScalarT& x) { constexpr void _insert(const ScalarT& x) {
data[OffsetV] = x; data[OffsetV] = x;
} }
template<size_t OffsetV, typename OScalarT> template<size_t OffsetV, typename OScalarT>
constexpr void _insert(OScalarT& x) { constexpr void _insert(const OScalarT& x) {
data[OffsetV] = ScalarT(x); data[OffsetV] = ScalarT(x);
} }
template<size_t OffsetV = 0, typename OScalarT, size_t... OIndicesV> template<size_t OffsetV = 0, typename OScalarT, size_t... OIndicesV>
constexpr void _insert(vector<OScalarT, OIndicesV...>& vec) { constexpr void _insert(const vector<OScalarT, OIndicesV...>& vec) {
((data[OffsetV + OIndicesV] = fennec::forward<OScalarT>(vec[OIndicesV])), ...); ((data[OffsetV + OIndicesV] = fennec::forward<OScalarT>(vec[OIndicesV])), ...);
} }
template<size_t OffsetV = 0, typename OVectorT, typename ODataT, typename OScalarT, size_t... OIndicesV> template<size_t OffsetV = 0, typename OVectorT, typename ODataT, typename OScalarT, size_t... OIndicesV>
constexpr void _insert(swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) { constexpr void _insert(const swizzle<OVectorT, ODataT, OScalarT, OIndicesV...>& vec) {
size_t i = 0; size_t i = 0;
((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...); ((data[OffsetV + (i++)] = vec.data[OIndicesV]), ...);
} }

View File

@@ -30,54 +30,55 @@
namespace fennec::detail namespace fennec::detail
{ {
// ///
// \brief helper class for generating vectors /// \brief helper class for generating vectors
// \tparam scalar base scalar type /// \tparam scalar base scalar type
// \tparam size size of the vector type /// \tparam size size of the vector type
template<typename scalar, size_t size> template<typename scalar, size_t size>
struct vector_base_type_helper struct vector_base_type_helper
{ {
// ///
// \var SizeV /// \var SizeV
// \brief size of the vector type /// \brief size of the vector type
inline static constexpr size_t SizeV = size; inline static constexpr size_t SizeV = size;
// ///
// \brief Base scalar type /// \brief Base scalar type
using ScalarT = scalar; using ScalarT = scalar;
// ///
// \brief Base vector type /// \brief Base vector type
using VectorT = vec<ScalarT, SizeV>; using VectorT = vec<ScalarT, SizeV>;
// ///
// \brief Backing array holding the elements /// \brief Backing array holding the elements
using DataT = array<ScalarT, SizeV>; using DataT = array<ScalarT, SizeV>;
// ///
// \brief Helper for generating a swizzle from a set of indices /// \brief Helper for generating a swizzle from a set of indices
// \tparam IndicesV Indices of the vector to pull from /// \tparam IndicesV Indices of the vector to pull from
template<size_t...IndicesV> struct SwizzleGen template<size_t...IndicesV> struct SwizzleGen
{ {
// \brief generated swizzle type /// \brief generated swizzle type
using type = swizzle<VectorT, DataT, ScalarT, IndicesV...>; using type = swizzle<VectorT, DataT, ScalarT, IndicesV...>;
}; };
// Specialization for single component swizzles to decay into a scalar ///
/// \tparam IndicesV Indices of the vector to pull from
template<size_t IndexV> struct SwizzleGen<IndexV> template<size_t IndexV> struct SwizzleGen<IndexV>
{ {
// \brief decayed scalar type /// \brief decayed scalar type
using type = ScalarT; using type = ScalarT;
}; };
// ///
// \brief backing storage type /// \brief backing storage type
using StorageT = vector_storage<SizeV, SwizzleGen, DataT>; using StorageT = vector_storage<SizeV, SwizzleGen, DataT>;
}; };
// ///
// \brief helper for getting the storage type of the vector constructed with the provided size and scalar type /// \brief helper for getting the storage type of the vector constructed with the provided size and scalar type
template<typename ScalarT, size_t SizeV> template<typename ScalarT, size_t SizeV>
using vector_base_type = typename vector_base_type_helper<ScalarT, SizeV>::StorageT; using vector_base_type = typename vector_base_type_helper<ScalarT, SizeV>::StorageT;

View File

@@ -33,19 +33,20 @@
namespace fennec::detail namespace fennec::detail
{ {
// ///
// \brief backing storage type for vectors /// \brief backing storage type for vectors
// \tparam SizeV size of the vector /// \tparam SizeV size of the vector
// \tparam SwizzleGenT generator for swizzles /// \tparam SwizzleGenT generator for swizzles
// \tparam DataT backing data type /// \tparam DataT backing data type
template<size_t SizeV, template<size_t...> class SwizzleGenT, typename DataT> struct vector_storage; template<size_t SizeV, template<size_t...> class SwizzleGenT, typename DataT> struct vector_storage;
///
// specialization for single component vectors /// \tparam SwizzleGenT generator for swizzles
/// \tparam DataT backing data type
template <template <size_t...> class SwizzleGenT, typename DataT> template <template <size_t...> class SwizzleGenT, typename DataT>
struct vector_storage<1, SwizzleGenT, DataT> struct vector_storage<1, SwizzleGenT, DataT>
{ {
template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; //!< swizzle gen type
vector_storage() = default; vector_storage() = default;
@@ -65,13 +66,15 @@ namespace fennec::detail
}; };
// specialization for two component vectors ///
/// \tparam SwizzleGenT generator for swizzles
/// \tparam DataT backing data type
template <template <size_t...> class SwizzleGenT, typename DataT> template <template <size_t...> class SwizzleGenT, typename DataT>
struct vector_storage<2, SwizzleGenT, DataT> struct vector_storage<2, SwizzleGenT, DataT>
{ {
// //
// alias to allow for increased legibility // alias to allow for increased legibility
template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; //!< swizzle gen type
vector_storage() = default; vector_storage() = default;
@@ -114,11 +117,13 @@ namespace fennec::detail
}; };
// specialization for three component vectors ///
/// \tparam SwizzleGenT generator for swizzles
/// \tparam DataT backing data type
template <template <size_t...> class SwizzleGenT, typename DataT> template <template <size_t...> class SwizzleGenT, typename DataT>
struct vector_storage<3, SwizzleGenT, DataT> struct vector_storage<3, SwizzleGenT, DataT>
{ {
template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; //!< swizzle gen type
vector_storage() = default; vector_storage() = default;
@@ -250,11 +255,13 @@ namespace fennec::detail
}; };
// specialization for four component vectors ///
/// \tparam SwizzleGenT generator for swizzles
/// \tparam DataT backing data type
template <template <size_t...> class SwizzleGenT, typename DataT> template <template <size_t...> class SwizzleGenT, typename DataT>
struct vector_storage<4, SwizzleGenT, DataT> struct vector_storage<4, SwizzleGenT, DataT>
{ {
template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; template<size_t...IndicesV> using swizzle = typename SwizzleGenT<IndicesV...>::type; //!< swizzle gen type
vector_storage() = default; vector_storage() = default;

View File

@@ -71,7 +71,7 @@ namespace fennec
/// ///
/// \brief check if \p T is a fennec::vector type /// \brief check if \p T is a fennec::vector type
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct is_vector : detail::_is_vector_helper<remove_cvr_t<T>>{}; template<typename T> struct is_vector : detail::_is_vector_helper<remove_cvref_t<T>>{};
/// ///
/// \brief shorthand for ```is_vector<T>::value``` /// \brief shorthand for ```is_vector<T>::value```
@@ -81,7 +81,7 @@ template<typename T> constexpr bool is_vector_v = is_vector<T>::value;
/// ///
/// \brief Get the number of Components in \p T, returns 1 for types that pass ```is_arithmetic<T>```, returns \ref vector::N for \ref vector "Vector" Types, and returns 0 for all other cases /// \brief Get the number of Components in \p T, returns 1 for types that pass ```is_arithmetic<T>```, returns \ref vector::N for \ref vector "Vector" Types, and returns 0 for all other cases
/// \tparam T type to check /// \tparam T type to check
template<typename T> struct component_count : detail::_component_count_helper<remove_cvr_t<T>>{}; template<typename T> struct component_count : detail::_component_count_helper<remove_cvref_t<T>>{};
/// ///
/// \brief shorthand for ```component_count<T>::value``` /// \brief shorthand for ```component_count<T>::value```

View File

@@ -130,7 +130,7 @@ public:
/// \brief Checks if this allocator type is always equal to another allocator of similar type /// \brief Checks if this allocator type is always equal to another allocator of similar type
using is_always_equal = detect_t<false_type, _is_always_equal, Alloc>; using is_always_equal = detect_t<false_type, _is_always_equal, Alloc>;
/// \brief Rebinds the allocator type to produce an element type of type `TypeT` /// \brief Rebinds the allocator type to produce an element type of type \f$TypeT\f$
template<typename TypeT> using rebind = typename _rebind<Alloc, TypeT>::type; template<typename TypeT> using rebind = typename _rebind<Alloc, TypeT>::type;
// TODO: allocator_traits static functions // TODO: allocator_traits static functions
@@ -138,67 +138,93 @@ public:
/// ///
/// \brief Allocator implementation, uses `new` and `delete` operators. /// \brief Allocator implementation, uses \f$new\f$ and \f$delete\f$ operators.
/// \tparam T The data type to allocate /// \tparam T The data type to allocate
template<typename T> template<typename T>
class allocator class allocator
{ {
public: public:
///
/// \brief Alias for the data type used for metaprogramming /// \brief Alias for the data type used for metaprogramming
using value_t = T; using value_t = T;
///
/// \brief Metaprogramming utility to rebind an allocator to a different data type /// \brief Metaprogramming utility to rebind an allocator to a different data type
template<typename R> using rebind = allocator<R>; template<typename R> using rebind = allocator<R>;
///
/// \brief Default Constructor /// \brief Default Constructor
constexpr allocator() = default; constexpr allocator() = default;
///
/// \brief Default Destructor /// \brief Default Destructor
constexpr ~allocator() = default; constexpr ~allocator() = default;
///
/// \brief Copy Constructor /// \brief Copy Constructor
constexpr allocator(const allocator&) = default; constexpr allocator(const allocator&) = default;
///
/// \brief Copy Assignment /// \brief Copy Assignment
/// \returns A reference to self
constexpr allocator& operator=(const allocator&) = default; constexpr allocator& operator=(const allocator&) = default;
///
/// \brief Equality operator /// \brief Equality operator
/// \returns \f$true\f$
constexpr bool_t operator==(const allocator&) { constexpr bool_t operator==(const allocator&) {
return true; return true;
} }
///
/// \brief Inequality operator /// \brief Inequality operator
/// \returns \f$false\f$
constexpr bool_t operator!=(const allocator&) { constexpr bool_t operator!=(const allocator&) {
return false; return false;
} }
///
/// \brief Equality operator for allocators of same type but with different data type /// \brief Equality operator for allocators of same type but with different data type
/// \returns \f$false\f$
template<typename U> constexpr bool_t operator==(const allocator<U>&) { template<typename U> constexpr bool_t operator==(const allocator<U>&) {
return true; return false;
} }
///
/// \brief Inequality operator for allocators of same type but with different data type /// \brief Inequality operator for allocators of same type but with different data type
/// \returns \f$true\f$
template<typename U> constexpr bool_t operator!=(const allocator<U>&) { template<typename U> constexpr bool_t operator!=(const allocator<U>&) {
return true; return true;
} }
/// \brief Allocate a block of memory large enough to hold `n` elements of type `T` ///
/// \brief Allocate a block of memory large enough to hold \f$n\f$ elements of type \f$T\f$
/// \param n The number of elements
/// \returns A pointer to the allocated block
constexpr T* allocate(size_t n) { constexpr T* allocate(size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T))); return static_cast<T*>(::operator new(n * sizeof(T)));
} }
/// \brief Allocate a block of memory large enough to hold `n` elements of type `T` ///
/// \brief Allocate a block of memory large enough to hold \f$n\f$ elements of type \f$T\f$
/// \param n The number of elements
/// \param align The alignment
/// \returns A pointer to the allocated block
constexpr T* allocate(size_t n, align_t align) { constexpr T* allocate(size_t n, align_t align) {
return static_cast<T*>(::operator new(n * sizeof(T), align)); return static_cast<T*>(::operator new(n * sizeof(T), align));
} }
/// \brief Deallocate a block of memory with type `T` ///
/// \brief Deallocate a block of memory with type \f$T\f$
/// \param ptr The block to release
constexpr void deallocate(T* ptr) { constexpr void deallocate(T* ptr) {
return ::operator delete(ptr); return ::operator delete(ptr);
} }
/// \brief Deallocate a block of memory with type `T` ///
/// \brief Deallocate a block of memory with type \f$T\f$
/// \param ptr The block to release
/// \param align The alignment
constexpr void deallocate(T* ptr, align_t align) { constexpr void deallocate(T* ptr, align_t align) {
return ::operator delete(ptr, align); return ::operator delete(ptr, align);
} }
@@ -206,67 +232,94 @@ public:
/// ///
/// \brief Allocator implementation, uses `new` and `delete` operators. /// \brief Allocator implementation, uses \f$new\f$ and \f$delete\f$ operators.
/// \tparam T The data type to allocate /// \tparam T The data type to allocate
template<typename T> template<typename T>
class allocator<T[]> class allocator<T[]>
{ {
public: public:
///
/// \brief Alias for the data type used for metaprogramming /// \brief Alias for the data type used for metaprogramming
using value_t = T; using value_t = T;
///
/// \brief Metaprogramming utility to rebind an allocator to a different data type /// \brief Metaprogramming utility to rebind an allocator to a different data type
template<typename R> using rebind = allocator<R>; template<typename R> using rebind = allocator<R>;
///
/// \brief Default Constructor /// \brief Default Constructor
constexpr allocator() = default; constexpr allocator() = default;
///
/// \brief Default Destructor /// \brief Default Destructor
constexpr ~allocator() = default; constexpr ~allocator() = default;
///
/// \brief Copy Constructor /// \brief Copy Constructor
constexpr allocator(const allocator&) = default; constexpr allocator(const allocator&) = default;
///
/// \brief Copy Assignment /// \brief Copy Assignment
/// \returns A reference to self
constexpr allocator& operator=(const allocator&) = default; constexpr allocator& operator=(const allocator&) = default;
///
/// \brief Equality operator /// \brief Equality operator
/// \returns \f$true\f$
constexpr bool_t operator==(const allocator&) { constexpr bool_t operator==(const allocator&) {
return true; return true;
} }
///
/// \brief Inequality operator /// \brief Inequality operator
/// \returns \f$false\f$
constexpr bool_t operator!=(const allocator&) { constexpr bool_t operator!=(const allocator&) {
return false; return false;
} }
///
/// \brief Equality operator for allocators of same type but with different data type /// \brief Equality operator for allocators of same type but with different data type
/// \returns \f$false\f$
template<typename U> constexpr bool_t operator==(const allocator<U>&) { template<typename U> constexpr bool_t operator==(const allocator<U>&) {
return true; return false;
} }
///
/// \brief Inequality operator for allocators of same type but with different data type /// \brief Inequality operator for allocators of same type but with different data type
/// \returns \f$true\f$
template<typename U> constexpr bool_t operator!=(const allocator<U>&) { template<typename U> constexpr bool_t operator!=(const allocator<U>&) {
return true; return true;
} }
/// \brief Allocate a block of memory large enough to hold `n` elements of type `T` ///
/// \brief Allocate a block of memory large enough to hold \f$n\f$ elements of type \f$T\f$
/// \param n The number of elements
/// \returns A pointer to the allocated block
constexpr T* allocate(size_t n) { constexpr T* allocate(size_t n) {
return static_cast<T*>(::operator new[](n * sizeof(T))); return static_cast<T*>(::operator new[](n * sizeof(T)));
} }
/// \brief Allocate a block of memory large enough to hold `n` elements of type `T` ///
/// \brief Allocate a block of memory large enough to hold \f$n\f$ elements of type \f$T\f$
/// \param n The number of elements
/// \param align The alignment
/// \returns A pointer to the allocated block
constexpr T* allocate(size_t n, align_t align) { constexpr T* allocate(size_t n, align_t align) {
return static_cast<T*>(::operator new[](n * sizeof(T), align)); return static_cast<T*>(::operator new[](n * sizeof(T), align));
} }
/// \brief Deallocate a block of memory with type `T` ///
/// \brief Deallocate a block of memory with type \f$T\f$
/// \param ptr The block to release
constexpr void deallocate(T* ptr) { constexpr void deallocate(T* ptr) {
return ::operator delete[](ptr); return ::operator delete[](ptr);
} }
/// \brief Deallocate a block of memory with type `T` ///
/// \brief Deallocate a block of memory with type \f$T\f$
/// \param ptr The block to release
/// \param align The alignment
constexpr void deallocate(T* ptr, align_t align) { constexpr void deallocate(T* ptr, align_t align) {
return ::operator delete[](ptr, align); return ::operator delete[](ptr, align);
} }
@@ -280,7 +333,7 @@ public:
/// \details This simply acts as a proxy for allocating memory. It does not call any constructors or /// \details This simply acts as a proxy for allocating memory. It does not call any constructors or
/// initialize any values as if they were the provided data type. Any operations present work /// initialize any values as if they were the provided data type. Any operations present work
/// only on individual bytes. /// only on individual bytes.
template<typename T, class AllocT> template<typename T, class AllocT = allocator<T>>
struct allocation struct allocation
{ {
public: public:
@@ -300,14 +353,14 @@ public:
// Cosntructors ======================================================================================================== // Cosntructors ========================================================================================================
/// ///
/// \brief Default Constructor, initializes internal data to `null` and the capacity to `0` /// \brief Default Constructor, initializes internal data to \f$null\f$ and the capacity to \f$0\f$
constexpr allocation() noexcept constexpr allocation() noexcept
: _data(nullptr), _capacity(0), _alignment(zero<align_t>()) { : _data(nullptr), _capacity(0), _alignment(zero<align_t>()) {
} }
/// ///
/// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes /// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes
/// \param n The number of elements of type `T` to allocate for /// \param n The number of elements of type \f$T\f$ to allocate for
explicit constexpr allocation(size_t n) noexcept explicit constexpr allocation(size_t n) noexcept
: _data(nullptr), _capacity(0), _alignment(zero<align_t>()) { : _data(nullptr), _capacity(0), _alignment(zero<align_t>()) {
callocate(n); callocate(n);
@@ -325,7 +378,7 @@ public:
/// ///
/// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes /// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes
/// \param n The number of elements of type `T` to allocate for /// \param n The number of elements of type \f$T\f$ to allocate for
/// \param align The alignment of the allocation /// \param align The alignment of the allocation
constexpr allocation(size_t n, align_t align) noexcept constexpr allocation(size_t n, align_t align) noexcept
: _data(nullptr) : _data(nullptr)
@@ -349,7 +402,7 @@ public:
/// \brief Allocator Constructor /// \brief Allocator Constructor
/// \param alloc The allocation object to copy. /// \param alloc The allocation object to copy.
/// ///
/// \details This constructor should be used when the type `AllocT` needs internal data. /// \details This constructor should be used when the type \f$AllocT\f$ needs internal data.
explicit constexpr allocation(const alloc_t& alloc) noexcept explicit constexpr allocation(const alloc_t& alloc) noexcept
: _alloc(alloc) : _alloc(alloc)
, _data(nullptr) , _data(nullptr)
@@ -359,14 +412,15 @@ public:
/// ///
/// \brief Sized Allocator Constructor /// \brief Sized Allocator Constructor
/// \param n The number of elements of type `T` to allocate for /// \param n The number of elements of type \f$T\f$ to allocate for
/// \param alloc The allocation object to copy. /// \param alloc The allocation object to copy.
/// ///
/// \details This constructor should be used when the type `AllocT` needs internal data. /// \details This constructor should be used when the type \f$AllocT\f$ needs internal data.
constexpr allocation(size_t n, const alloc_t& alloc) noexcept constexpr allocation(size_t n, const alloc_t& alloc) noexcept
: _alloc(alloc) : _alloc(alloc)
, _data(nullptr) , _data(nullptr)
, _capacity(0) { , _capacity(0)
, _alignment(zero<align_t>()) {
callocate(n); callocate(n);
} }
@@ -377,7 +431,7 @@ public:
/// \param n the number of elements /// \param n the number of elements
/// \param alloc The allocation object to copy. /// \param alloc The allocation object to copy.
/// ///
/// \details This constructor should be used when the type `AllocT` needs internal data. /// \details This constructor should be used when the type \f$AllocT\f$ needs internal data.
constexpr allocation(const T* data, size_t n, const alloc_t& alloc) constexpr allocation(const T* data, size_t n, const alloc_t& alloc)
: allocation(n, alloc) { : allocation(n, alloc) {
fennec::memmove(static_cast<void*>(_data), data, n); fennec::memmove(static_cast<void*>(_data), data, n);
@@ -385,11 +439,11 @@ public:
/// ///
/// \brief Sized Allocator Constructor /// \brief Sized Allocator Constructor
/// \param n The number of elements of type `T` to allocate for /// \param n The number of elements of type \f$T\f$ to allocate for
/// \param align The alignment of the allocation /// \param align The alignment of the allocation
/// \param alloc The allocation object to copy. /// \param alloc The allocation object to copy.
/// ///
/// \details This constructor should be used when the type `AllocT` needs internal data. /// \details This constructor should be used when the type \f$AllocT\f$ needs internal data.
constexpr allocation(size_t n, align_t align, const alloc_t& alloc) noexcept constexpr allocation(size_t n, align_t align, const alloc_t& alloc) noexcept
: _alloc(alloc) : _alloc(alloc)
, _data(nullptr) , _data(nullptr)
@@ -406,7 +460,7 @@ public:
/// \param align The alignment of the allocation /// \param align The alignment of the allocation
/// \param alloc The allocation object to copy. /// \param alloc The allocation object to copy.
/// ///
/// \details This constructor should be used when the type `AllocT` needs internal data. /// \details This constructor should be used when the type \f$AllocT\f$ needs internal data.
constexpr allocation(const T* data, size_t n, align_t align, const alloc_t& alloc) constexpr allocation(const T* data, size_t n, align_t align, const alloc_t& alloc)
: allocation(n, align, alloc) { : allocation(n, align, alloc) {
fennec::memmove(_data, data, n); fennec::memmove(_data, data, n);
@@ -424,7 +478,7 @@ public:
} }
/// ///
/// \brief Move Constructor, moves the data in `alloc` to the new object and cleans `alloc` so that it /// \brief Move Constructor, moves the data in \f$alloc\f$ to the new object and cleans \f$alloc\f$ so that it
/// can safely destruct /// can safely destruct
/// \param alloc The allocation to move /// \param alloc The allocation to move
constexpr allocation(allocation&& alloc) noexcept constexpr allocation(allocation&& alloc) noexcept
@@ -439,7 +493,10 @@ public:
/// ///
/// \brief Default Destructor, releases the memory block if still present /// \brief Default Destructor, releases the memory block if still present
constexpr ~allocation() noexcept { constexpr ~allocation() noexcept {
if (_data) _alloc.deallocate(_data); if (_data) {
_alloc.deallocate(_data);
_data = nullptr;
}
} }
@@ -448,9 +505,9 @@ public:
/// ///
/// \brief Copy Assignment Operator /// \brief Copy Assignment Operator
/// \param alloc the allocation to copy /// \param alloc the allocation to copy
/// \returns a reference to `this` /// \returns a reference to \f$this\f$
constexpr allocation& operator=(const allocation& alloc) { constexpr allocation& operator=(const allocation& alloc) {
allocation::allocate(alloc.capacity()); allocation::allocate(alloc.capacity(), alloc.alignment());
fennec::memmove(_data, alloc, size()); fennec::memmove(_data, alloc, size());
return *this; return *this;
} }
@@ -458,24 +515,27 @@ public:
/// ///
/// \brief Move Assignment Operator /// \brief Move Assignment Operator
/// \param alloc the allocation to copy /// \param alloc the allocation to copy
/// \returns a reference to `this` /// \returns a reference to \f$this\f$
constexpr allocation& operator=(allocation&& alloc) noexcept { constexpr allocation& operator=(allocation&& alloc) noexcept {
// Copy contents // Copy contents
fennec::swap(_alloc, alloc._alloc); fennec::swap(_alloc, alloc._alloc);
fennec::swap(_data, alloc._data); fennec::swap(_data, alloc._data);
fennec::swap(_capacity, alloc._capacity); fennec::swap(_capacity, alloc._capacity);
fennec::swap(_alignment, alloc._alignment);
return *this; return *this;
} }
// Allocation and Deallocation ===================================================================================== // Allocation and Deallocation =========================================================================================
/// ///
/// \brief Allocate a block of memory for the allocation. /// \brief Allocate a block of memory for the allocation.
/// If there is already an allocated block of memory, the previous allocation is released. /// If there is already an allocated block of memory, the previous allocation is released.
/// \param n The number of elements of type `T` to allocate for ///
/// \param n The number of elements of type \f$T\f$ to allocate for
/// \param align The alignment to use
constexpr void allocate(size_t n, align_t align = zero<align_t>()) noexcept { constexpr void allocate(size_t n, align_t align = zero<align_t>()) noexcept {
deallocate(); deallocate();
@@ -489,7 +549,9 @@ public:
/// ///
/// \brief Allocate a block of memory for the allocation. /// \brief Allocate a block of memory for the allocation.
/// If there is already an allocated block of memory, the previous allocation is released. /// If there is already an allocated block of memory, the previous allocation is released.
/// \param n The number of elements of type `T` to allocate for ///
/// \param n The number of elements of type \f$T\f$ to allocate for
/// \param align The alignment to use
constexpr void callocate(size_t n, align_t align = zero<align_t>()) noexcept { constexpr void callocate(size_t n, align_t align = zero<align_t>()) noexcept {
allocate(n, align); allocate(n, align);
fennec::memset(static_cast<void*>(_data), 0, _capacity * sizeof(T)); fennec::memset(static_cast<void*>(_data), 0, _capacity * sizeof(T));
@@ -514,6 +576,9 @@ public:
/// ///
/// \brief Reallocate the block with a new size. /// \brief Reallocate the block with a new size.
/// Contents are copied to the new allocation. /// Contents are copied to the new allocation.
///
/// \param n The number of elements of type \f$T\f$ to allocate for
/// \param align The alignment to use
constexpr void reallocate(size_t n, align_t align = zero<align_t>()) noexcept { constexpr void reallocate(size_t n, align_t align = zero<align_t>()) noexcept {
if (_data == nullptr) { if (_data == nullptr) {
allocate(n, align); allocate(n, align);
@@ -533,6 +598,9 @@ public:
/// ///
/// \brief Reallocate the block with a new size. /// \brief Reallocate the block with a new size.
/// Contents are copied to the new allocation. /// Contents are copied to the new allocation.
///
/// \param n The number of elements of type \f$T\f$ to allocate for
/// \param align The alignment to use
constexpr void creallocate(size_t n, align_t align = zero<align_t>()) noexcept { constexpr void creallocate(size_t n, align_t align = zero<align_t>()) noexcept {
if (_data == nullptr) { if (_data == nullptr) {
callocate(n, align); callocate(n, align);
@@ -556,36 +624,58 @@ public:
// Access ============================================================================================================== // Access ==============================================================================================================
///
/// \param i The index to access
/// \returns a reference to the value at position \f$i\f$ in the allocation
constexpr value_t& operator[](size_t i) { constexpr value_t& operator[](size_t i) {
assertd(i < capacity(), "Array Out of Bounds"); assertd(i < capacity(), "Array Out of Bounds");
return _data[i]; return _data[i];
} }
///
/// \brief Array Access Operator
/// \param i The index to access
/// \returns a reference to the value at position \f$i\f$ in the allocation
constexpr const value_t& operator[](size_t i) const { constexpr const value_t& operator[](size_t i) const {
assertd(i < capacity(), "Array Out of Bounds"); assertd(i < capacity(), "Array Out of Bounds");
return _data[i]; return _data[i];
} }
///
/// \returns The underlying pointer.
constexpr operator value_t*() { constexpr operator value_t*() {
return _data; return _data;
} }
///
/// \brief Dereference Operators
/// \returns The underlying pointer.
constexpr operator const value_t*() const { constexpr operator const value_t*() const {
return _data; return _data;
} }
///
/// \returns A pointer to the start of the allocation.
value_t* begin() { value_t* begin() {
return _data; return _data;
} }
value_t* end() { ///
return _data + capacity(); /// \brief Iterator Begin
} /// \returns A pointer to the start of the allocation.
const value_t* begin() const { const value_t* begin() const {
return _data; return _data;
} }
///
/// \returns A pointer to the element one after the last.
value_t* end() {
return _data + capacity();
}
///
/// \brief Iterator End
/// \returns A pointer to the element one after the last.
const value_t* end() const { const value_t* end() const {
return _data + capacity(); return _data + capacity();
} }
@@ -596,7 +686,7 @@ public:
/// ///
/// \brief Clear the block of memory, setting all bytes to 0. /// \brief Clear the block of memory, setting all bytes to 0.
constexpr void clear() noexcept { constexpr void clear() noexcept {
fennec::memset(_data, 0, _capacity * sizeof(T)); fennec::memset(static_cast<void*>(_data), 0, _capacity * sizeof(T));
} }
/// ///
@@ -607,7 +697,7 @@ public:
} }
/// ///
/// \brief Getter for the number of elements `n` of type `T` that the allocation can hold. /// \brief Getter for the number of elements \f$n\f$ of type \f$T\f$ that the allocation can hold.
/// \returns the size of the allocation in elements /// \returns the size of the allocation in elements
constexpr size_t capacity() const { constexpr size_t capacity() const {
return _capacity; return _capacity;
@@ -627,6 +717,13 @@ public:
return _data; return _data;
} }
///
/// \brief Getter for the alignment of the allocation.
/// \returns the alignment of the allocation
constexpr align_t alignment() const {
return _alignment;
}
private: private:
alloc_t _alloc; // Allocator object alloc_t _alloc; // Allocator object
value_t* _data; // Handle for the memory block value_t* _data; // Handle for the memory block

View File

@@ -16,8 +16,8 @@
// 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_LANG_BYTES_H #ifndef FENNEC_MEMORY_BYTES_H
#define FENNEC_LANG_BYTES_H #define FENNEC_MEMORY_BYTES_H
#include <fennec/lang/assert.h> #include <fennec/lang/assert.h>
#include <fennec/lang/types.h> #include <fennec/lang/types.h>
@@ -89,7 +89,7 @@ public:
/// ///
/// \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 byte at `i` /// \returns a reference to the byte at \f$i\f$
constexpr byte_t& operator[](int i) { constexpr byte_t& operator[](int i) {
assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const"); assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const");
assertd(i >= 0 && (size_t)i < _size, "Array Out of Bounds"); assertd(i >= 0 && (size_t)i < _size, "Array Out of Bounds");
@@ -99,7 +99,7 @@ public:
/// ///
/// \brief Const Array Access Operator /// \brief Const Array Access Operator
/// \param i the index to access /// \param i the index to access
/// \returns a copy of the byte at `i` /// \returns a copy of the byte at \f$i\f$
constexpr byte_t operator[](int i) const { constexpr byte_t operator[](int i) const {
assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const"); assertd(not _const, "Attempted to Access Const-Qualified Memory as Non-Const");
assertd(i >= 0 && (size_t)i < _size, "Array Out of Bounds"); assertd(i >= 0 && (size_t)i < _size, "Array Out of Bounds");
@@ -109,7 +109,7 @@ public:
/// ///
/// \brief Cast Function /// \brief Cast Function
/// \tparam T type to cast to /// \tparam T type to cast to
/// \returns a pointer to the underlying buffer interpreted as an array of `T` /// \returns a pointer to the underlying buffer interpreted as an array of \f$T\f$
template<typename T> template<typename T>
constexpr T* cast() { constexpr T* cast() {
void* temp = _arr; void* temp = _arr;
@@ -119,7 +119,7 @@ public:
/// ///
/// \brief Const Cast Function /// \brief Const Cast Function
/// \tparam T type to cast to /// \tparam T type to cast to
/// \returns a pointer to the underlying buffer interpreted as an array of `T` /// \returns a pointer to the underlying buffer interpreted as an array of \f$T\f$
template<typename T> template<typename T>
constexpr const T* cast() const { constexpr const T* cast() const {
const void* temp = _carr; const void* temp = _carr;
@@ -139,6 +139,10 @@ private:
/// \brief Byte Array Hash Function /// \brief Byte Array Hash Function
template<> template<>
struct hash<byte_array> { struct hash<byte_array> {
///
/// \brief hash operator
/// \param bytes the byte_array to hash
/// \returns the murmur2 hash of the byte array
size_t operator()(const byte_array& bytes) const { size_t operator()(const byte_array& bytes) const {
// Murmur2 // Murmur2
@@ -186,4 +190,4 @@ struct hash<byte_array> {
} }
#endif // FENNEC_LANG_BYTES_H #endif // FENNEC_MEMORY_BYTES_H

View File

@@ -73,6 +73,8 @@ using ::wmemcmp;
/// \param rhs The second object, interpreted as an array of bytes /// \param rhs The second object, interpreted as an array of bytes
/// \param n0 The size, in bytes, of lhs /// \param n0 The size, in bytes, of lhs
/// \param n1 The size, in bytes, of rhs /// \param n1 The size, in bytes, of rhs
/// \returns \f$0\f$ if the first \f$min(n0, n1)\f$ bytes of \f$lhs\f$ and \f$rhs\f$ are equivalent. Otherwise, returns \f$1\f$
/// for the first byte \f$b\f$ where \f$lhs[b] > \f$ rhs[b]\f$, and \f$-1\f$ for \f$
constexpr int memcmp_s(const void* lhs, size_t n0, const void* rhs, size_t n1) { constexpr int memcmp_s(const void* lhs, size_t n0, const void* rhs, size_t n1) {
return memcmp(lhs, rhs, n0 < n1 ? n0 : n1); return memcmp(lhs, rhs, n0 < n1 ? n0 : n1);
} }
@@ -98,6 +100,7 @@ using ::wmemcpy;
/// \param src The source object, interpreted as an array of bytes /// \param src The source object, interpreted as an array of bytes
/// \param n0 The size, in bytes, of dst /// \param n0 The size, in bytes, of dst
/// \param n1 The size, in bytes, of src /// \param n1 The size, in bytes, of src
/// \returns \f$dst\f$
constexpr void* memcpy_s(void* dst, size_t n0, const void* src, size_t n1) { constexpr void* memcpy_s(void* dst, size_t n0, const void* src, size_t n1) {
return memcpy(dst, src, n0 < n1 ? n0 : n1); return memcpy(dst, src, n0 < n1 ? n0 : n1);
} }
@@ -117,6 +120,7 @@ using ::wmemmove;
/// \param src The source object, interpreted as an array of bytes /// \param src The source object, interpreted as an array of bytes
/// \param n0 The size, in bytes, of dst /// \param n0 The size, in bytes, of dst
/// \param n1 The size, in bytes, of src /// \param n1 The size, in bytes, of src
/// \returns \f$dst\f$
constexpr void* memmove_s(void* dst, size_t n0, const void* src, size_t n1) { constexpr void* memmove_s(void* dst, size_t n0, const void* src, size_t n1) {
return memmove(dst, src, n0 < n1 ? n0 : n1); return memmove(dst, src, n0 < n1 ? n0 : n1);
} }

View File

@@ -53,28 +53,53 @@ struct nothrow_t
/// \returns the page size for the current environment /// \returns the page size for the current environment
size_t pagesize(); size_t pagesize();
///
/// \brief Default construct the object of type \f$TypeT\f$ at \f$ptr\f$
/// \tparam TypeT the type to construct
/// \param ptr the pointer to the object to construct
template<typename TypeT> void construct(TypeT* ptr) { template<typename TypeT> void construct(TypeT* ptr) {
new(ptr) TypeT(); new(ptr) TypeT();
} }
///
/// \brief Copy construct the object of type \f$TypeT\f$ at \f$ptr\f$
/// \tparam TypeT the type to construct
/// \param ptr the pointer to the object to construct
/// \param val the value to copy
template<typename TypeT> void construct(TypeT* ptr, const TypeT& val) { template<typename TypeT> void construct(TypeT* ptr, const TypeT& val) {
new(ptr) TypeT(val); new(ptr) TypeT(val);
} }
///
/// \brief Move construct the object of type \f$TypeT\f$ at \f$ptr\f$
/// \tparam TypeT the type to construct
/// \param ptr the pointer to the object to construct
/// \param val the value to take ownership of
template<typename TypeT> void construct(TypeT* ptr, TypeT&& val) { template<typename TypeT> void construct(TypeT* ptr, TypeT&& val) {
new(ptr) TypeT(fennec::forward<TypeT>(val)); new(ptr) TypeT(fennec::forward<TypeT>(val));
} }
///
/// \brief Variadic construct the object of type \f$TypeT\f$ at \f$ptr\f$
/// \tparam TypeT the type to construct
/// \tparam ArgsT the argument types
/// \param ptr the pointer to the object to construct
/// \param args the argument values
template<typename TypeT, typename...ArgsT> void construct(TypeT* ptr, ArgsT&&...args) { template<typename TypeT, typename...ArgsT> void construct(TypeT* ptr, ArgsT&&...args) {
new(ptr) TypeT(fennec::forward<ArgsT>(args)...); new(ptr) TypeT(fennec::forward<ArgsT>(args)...);
} }
///
/// \brief Destruct the object of type \f$TypeT\f$ at \f$ptr\f$
/// \tparam TypeT the type to destruct
/// \param ptr the pointer to the object to destruct
template<typename TypeT> void destruct(TypeT* ptr) { template<typename TypeT> void destruct(TypeT* ptr) {
ptr->~TypeT(); ptr->~TypeT();
} }
} }
#ifndef FENNEC_DOXYGEN
void* operator new (fennec::size_t size); void* operator new (fennec::size_t size);
void* operator new[](fennec::size_t size); void* operator new[](fennec::size_t size);
void* operator new (fennec::size_t size, const fennec::nothrow_t&); void* operator new (fennec::size_t size, const fennec::nothrow_t&);
@@ -103,6 +128,7 @@ void operator delete[](void* ptr, fennec::align_t, const fennec::nothrow_t&) noe
template<typename TypeT> constexpr void* operator new (fennec::size_t, TypeT* ptr) { return ptr; } template<typename TypeT> constexpr void* operator new (fennec::size_t, TypeT* ptr) { return ptr; }
template<typename TypeT> constexpr void* operator new[](fennec::size_t, TypeT* ptr) { return ptr; } template<typename TypeT> constexpr void* operator new[](fennec::size_t, TypeT* ptr) { return ptr; }
#endif
#endif // FENNEC_MEMORY_NEW_H #endif // FENNEC_MEMORY_NEW_H

View File

@@ -32,15 +32,17 @@ template<typename ClassT>
struct pointer_traits struct pointer_traits
: detail::_ptr_traits_impl<ClassT, detail::_ptr_get_element<ClassT>> {}; : detail::_ptr_traits_impl<ClassT, detail::_ptr_get_element<ClassT>> {};
// overload for C-Style Pointers ///
/// \brief overload for C-Style pointers
/// \tparam ElemT the element type
template<typename ElemT> template<typename ElemT>
struct pointer_traits<ElemT*> : detail::_ptr_traits_ptr_to<ElemT*, ElemT> struct pointer_traits<ElemT*> : detail::_ptr_traits_ptr_to<ElemT*, ElemT>
{ {
using pointer_t = ElemT*; using pointer_t = ElemT*; //!< the pointer type
using element_t = ElemT; using element_t = ElemT; //!< the element type
using diff_t = ptrdiff_t; using diff_t = ptrdiff_t; //!< the difference type
template<typename U> using rebind = U*; template<typename U> using rebind = U*; //!< rebind type
}; };
} }

View File

@@ -20,12 +20,13 @@
#define FENNEC_MEMORY_POINTERS_H #define FENNEC_MEMORY_POINTERS_H
#include <fennec/lang/type_traits.h> #include <fennec/lang/type_traits.h>
#include <fennec/lang/utility.h>
namespace fennec namespace fennec
{ {
/// ///
/// \brief Struct for wrapping C++ `delete` /// \brief Struct for wrapping C++ \f$delete\f$
/// \tparam TypeT The type of the buffer to be deleted /// \tparam TypeT The type of the buffer to be deleted
template<typename TypeT> template<typename TypeT>
struct default_delete struct default_delete
@@ -41,16 +42,17 @@ struct default_delete
constexpr default_delete(const default_delete<ConvT>&) noexcept {} constexpr default_delete(const default_delete<ConvT>&) noexcept {}
/// ///
/// \brief Function Call Operator, calls `delete` on `ptr` /// \brief Function Call Operator, calls \f$delete\f$ on \f$ptr\f$
/// \param ptr Memory resource to delete /// \param ptr Memory resource to delete
constexpr void operator()(TypeT* ptr) const noexcept { constexpr void operator()(TypeT* ptr) const noexcept {
static_assert(not is_void_v<TypeT>, "cannot delete a pointer to an incomplete type"); static_assert(not is_void_v<TypeT>, "cannot delete a pointer to an incomplete type");
static_assert(not sizeof(TypeT) > 0, "cannot delete a pointer to an incomplete type"); static_assert(is_complete_v<TypeT>, "cannot delete a pointer to an incomplete type");
delete ptr; delete ptr;
} }
}; };
// Overload for Arrays ///
/// \tparam TypeT The type of the buffer to be deleted
template<typename TypeT> template<typename TypeT>
struct default_delete<TypeT[]> struct default_delete<TypeT[]>
{ {
@@ -65,12 +67,12 @@ struct default_delete<TypeT[]>
constexpr default_delete(const default_delete<ConvT(*)[]>&) noexcept {} constexpr default_delete(const default_delete<ConvT(*)[]>&) noexcept {}
/// ///
/// \brief Function Call Operator, calls `delete` on `ptr` /// \brief Function Call Operator, calls \f$delete\f$ on \f$ptr\f$
/// \param ptr Memory resource to delete /// \param ptr Memory resource to delete
template<class ArrT> requires requires { is_convertible_v<ArrT(*)[], TypeT(*)[]> == true; } template<class ArrT> requires requires { is_convertible_v<ArrT(*)[], TypeT(*)[]> == true; }
constexpr void operator()(TypeT* ptr) const noexcept { constexpr void operator()(TypeT* ptr) const noexcept {
static_assert(not is_void_v<TypeT>, "cannot delete a pointer to an incomplete type"); static_assert(not is_void_v<TypeT>, "cannot delete a pointer to an incomplete type");
static_assert(not sizeof(TypeT) > 0, "cannot delete a pointer to an incomplete type"); static_assert(is_complete_v<TypeT>, "cannot delete a pointer to an incomplete type");
delete[] ptr; delete[] ptr;
} }
}; };
@@ -91,6 +93,9 @@ public:
/// \brief pointer to element type /// \brief pointer to element type
using pointer_t = element_t*; using pointer_t = element_t*;
/// \brief pointer to element type
using const_pointer_t = const element_t*;
/// \brief the deleter /// \brief the deleter
using delete_t = DeleteT; using delete_t = DeleteT;
@@ -100,28 +105,19 @@ public:
/// ///
/// \brief Nullptr Constructor, creates a unique_ptr that owns nothing. /// \brief Nullptr Constructor, creates a unique_ptr that owns nothing.
constexpr unique_ptr(nullptr_t) noexcept : unique_ptr(nullptr) {} constexpr unique_ptr(nullptr_t) noexcept : unique_ptr(nullptr, delete_t()) {}
/// ///
/// \brief Pointer Constructor, creates a unique_ptr that owns `ptr` with deleter `del` /// \brief Pointer Constructor, creates a unique_ptr that owns \f$ptr\f$ with deleter \f$del\f$
/// \param ptr The resource to own /// \param ptr The resource to own
/// \param del The deleter /// \param del The deleter
explicit constexpr unique_ptr(pointer_t ptr, const delete_t& del) explicit constexpr unique_ptr(pointer_t ptr, const delete_t& del = delete_t())
: _delete(del) : _delete(del)
, _handle(ptr) { , _handle(ptr) {
} }
/// ///
/// \brief Pointer Constructor, creates a unique_ptr that owns `ptr` with deleter `del` /// \brief Move Constructor, transfers ownership from \f$other\f$
/// \param ptr The resource to own
/// \param del The deleter
explicit constexpr unique_ptr(pointer_t ptr, delete_t&& del)
: _delete(del)
, _handle(ptr) {
}
///
/// \brief Move Constructor, transfers ownership from `other`
/// \param other The unique_ptr to take ownership from /// \param other The unique_ptr to take ownership from
constexpr unique_ptr(unique_ptr&& other) constexpr unique_ptr(unique_ptr&& other)
: _handle(other._handle) { : _handle(other._handle) {
@@ -134,30 +130,101 @@ public:
/// ///
/// \brief Default Constructor, if it owns a resource, it deletes it using `delete_t` /// \brief Default Constructor, if it owns a resource, it deletes it using `delete_t`
constexpr ~unique_ptr() { constexpr ~unique_ptr() {
if(_handle) _delete(_handle); reset();
} }
constexpr unique_ptr& operator=(const unique_ptr&) = delete; constexpr unique_ptr& operator=(const unique_ptr&) = delete;
///
/// \brief move constructor
/// \param r the pointer to take ownership of
/// \returns a reference to self
constexpr unique_ptr& operator=(unique_ptr&& r) noexcept { constexpr unique_ptr& operator=(unique_ptr&& r) noexcept {
_delete = r._delete; _delete = r._delete;
_handle = r._handle; fennec::swap(_handle, r._handle);
r._handle = nullptr;
return *this; return *this;
} }
///
/// \brief reset the pointer, destroying the held object
/// \param ptr the new pointer to own
void reset(pointer_t ptr) {
if(_handle) {
_delete(_handle);
}
_handle = ptr;
}
///
/// \brief reset the pointer, destroying the held object
void reset(nullptr_t = nullptr) {
if(_handle) {
_delete(_handle);
_handle = nullptr;
}
}
///
/// \brief releases the held pointer, returning it
/// \returns the released pointer
pointer_t release() { pointer_t release() {
pointer_t retval = _handle; pointer_t retval = _handle;
_handle = nullptr; _handle = nullptr;
return retval; return retval;
} }
///
/// \returns a reference to the held pointer
pointer_t get() const {
return _handle;
}
///
/// \returns \f$true\f$ if there is not a held pointer, \f$false\f$ otherwise
bool empty() {
return _handle == nullptr;
}
///
/// \returns the held object for access
pointer_t operator->() {
return _handle;
}
///
/// \brief access operator
/// \returns the held object for access
const_pointer_t operator->() const {
return _handle;
}
///
/// \brief implicit boolean conversion
/// \returns \f$true\f$ if there is a held pointer, \f$false\f$ otherwise
operator bool() const {
return _handle != nullptr;
}
element_t& operator*() const {
return *_handle;
}
private: private:
delete_t _delete; delete_t _delete;
pointer_t _handle; pointer_t _handle;
}; };
///
/// \brief Creates a unique pointer holding an object of type \f$TypeT\f$
/// \tparam TypeT The type
/// \tparam ArgsT The constructor arguments, automatically deduced
/// \param args The constructor arguments
/// \returns A unique pointer holding a heap allocated object of type \f$TypeT\f$ constructed with arguments \f$args\f$
template<typename TypeT, typename...ArgsT>
unique_ptr<TypeT> make_unique(ArgsT&&...args) {
return unique_ptr<TypeT>(new TypeT(fennec::forward<ArgsT>(args)...));
}
} }
#endif // FENNEC_MEMORY_POINTERS_H #endif // FENNEC_MEMORY_POINTERS_H

View File

@@ -1,83 +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_PLATFORM_INTERFACE_DISPLAY_H
#define FENNEC_PLATFORM_INTERFACE_DISPLAY_H
#include <fennec/langproc/strings/string.h>
#include <fennec/lang/types.h>
#include <fennec/lang/typeuuid.h>
#include <fennec/platform/interface/fwd.h>
namespace fennec
{
class display : public typed<display>
{
public:
struct pixel_format {
uint8_t depth;
uint8_t r, g, b;
};
struct config {
pixel_format format;
};
virtual bool connected() const = 0;
virtual ~display();
virtual window* create_window(window* parent) = 0;
const pixel_format& get_color_format() const {
return _config.format;
}
virtual void select_context();
virtual void* get_native_handle() = 0;
platform* get_platform() { return _platform; }
gfxcontext* get_context() { return _context; }
const string name;
protected:
platform* _platform;
gfxcontext* _context;
config _config;
template<typename DisplayT>
explicit display(platform* platform, const cstring& name, DisplayT* type)
: typed(type)
, name(name)
, _platform(platform)
, _context(nullptr)
, _config {
.format = {
.depth = 24,
.r = 8,
.g = 8,
.b = 8,
}
} {
}
};
}
#endif // FENNEC_PLATFORM_INTERFACE_DISPLAY_H

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