diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d2ea1..b2c7ddc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,6 +31,7 @@ set(FENNEC_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) # include scripts include("${FENNEC_SOURCE_DIR}/cmake/platform.cmake") include("${FENNEC_SOURCE_DIR}/cmake/build.cmake") +include("${FENNEC_SOURCE_DIR}/cmake/compiler.cmake") # find dependencies find_package(Doxygen) @@ -170,6 +171,9 @@ add_library(fennec STATIC ${FENNEC_EXTRA_SOURCES} + include/fennec/lang/type_operators.h + include/fennec/containers/tuple.h + include/fennec/containers/detail/__tuple.h ) add_dependencies(fennec metaprogramming) diff --git a/READINGS.md b/READINGS.md new file mode 100644 index 0000000..6d6696f --- /dev/null +++ b/READINGS.md @@ -0,0 +1,16 @@ + + + +# Readings + +Here is a list of relevant books and articles on various concepts related to +developing a game engine and its subsystems. + + - Game Engine Architecture, Ed. 3 – Jason Gregory + - https://www.gameenginebook.com/ + - OpenGL 4 Shading Language Cookbook, Ed. 3 – David A. Wolff + - https://www.packtpub.com/en-us/product/opengl-4-shading-language-cookbook-9781789340662 + - Design Patterns: Elements of Reusable Object-Oriented Software – Erich Gamma, Richard Helm, Ralph Johnson, John Vilssides + - https://www.oreilly.com/library/view/design-patterns-elements/0201633612/ + - Head First Design Patterns – Eric FReeman, Elisabeth Robson, Bert Bates, Kathy Sierra + - https://www.oreilly.com/library/view/head-first-design/0596007124/ \ No newline at end of file diff --git a/cmake/compiler.cmake b/cmake/compiler.cmake index df4c55a..1599f51 100644 --- a/cmake/compiler.cmake +++ b/cmake/compiler.cmake @@ -16,8 +16,6 @@ # along with this program. If not, see . # ====================================================================================================================== -list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_COMPILER_GCC=0) - if(${CMAKE_CXX_COMPILER_ID} MATCHES "GNU") set(FENNEC_COMPILER "GCC") include("${FENNEC_SOURCE_DIR}/cmake/gcc.cmake") diff --git a/cmake/gcc.cmake b/cmake/gcc.cmake index e916f64..6e5a838 100644 --- a/cmake/gcc.cmake +++ b/cmake/gcc.cmake @@ -20,4 +20,4 @@ add_compile_options("-mxsave" "-Wall" "-Wextra" "-pedantic" "-Werror") set(FENNEC_PRIVATE_LINK_OPTIONS "-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates") -list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_COMPILER=FENNEC_COMPILER_GCC) \ No newline at end of file +list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_COMPILER_GCC=1) \ No newline at end of file diff --git a/cmake/linux.cmake b/cmake/linux.cmake index 8d5fad0..dadf5f0 100644 --- a/cmake/linux.cmake +++ b/cmake/linux.cmake @@ -18,6 +18,9 @@ macro(fennec_check_platform) + # unix + include("${FENNEC_SOURCE_DIR}/cmake/unix.cmake") + # compile definitions list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_PLATFORM_NAME="Linux" @@ -30,8 +33,8 @@ macro(fennec_check_platform) ) # includes - include("${FENNEC_SOURCE_DIR}/cmake/libwayland.cmake") + include("${FENNEC_SOURCE_DIR}/cmake/wayland.cmake") # tests - fennec_check_libwayland() + fennec_check_wayland() endmacro() \ No newline at end of file diff --git a/cmake/unix.cmake b/cmake/unix.cmake new file mode 100644 index 0000000..fb488ea --- /dev/null +++ b/cmake/unix.cmake @@ -0,0 +1,22 @@ +# ====================================================================================================================== +# 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 . +# ====================================================================================================================== + +# compile definitions +list(APPEND FENNEC_COMPILE_DEFINITIONS + FENNEC_PLATFORM_UNIX=1 +) \ No newline at end of file diff --git a/cmake/libwayland.cmake b/cmake/wayland.cmake similarity index 98% rename from cmake/libwayland.cmake rename to cmake/wayland.cmake index ca69324..da49b40 100644 --- a/cmake/libwayland.cmake +++ b/cmake/wayland.cmake @@ -18,7 +18,7 @@ # https://gist.github.com/mariobadr/acc3c8adf4b4e722705be38c3deac59a -macro(fennec_check_libwayland) +macro(fennec_check_wayland) set(WAYLAND_CLIENT_FOUND 0) find_path( diff --git a/doxy/Doxyfile b/doxy/Doxyfile index 26d134c..df913ab 100644 --- a/doxy/Doxyfile +++ b/doxy/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = fennec # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = 1.0.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/include/fennec/containers/detail/__tuple.h b/include/fennec/containers/detail/__tuple.h new file mode 100644 index 0000000..c6fe199 --- /dev/null +++ b/include/fennec/containers/detail/__tuple.h @@ -0,0 +1,59 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +#ifndef FENNEC_CONTAINERS_DETAIL_TUPLE_H +#define FENNEC_CONTAINERS_DETAIL_TUPLE_H +#include +#include + +namespace fennec::detail +{ + +// leaves +template +struct __tuple_leaf { + T value; + + template + __tuple_leaf(ArgsT&&...args) : value(args...) { + } + + constexpr operator T&() { + return value; + } + + constexpr operator const T&() const { + return value; + } +}; + +// proxy +template +struct __tuple; + +template +struct __tuple, TypesT...> : __tuple_leaf... { + + template + __tuple(ArgsT&&...args) : __tuple_leaf(args)... { + } +}; + +} + +#endif // FENNEC_CONTAINERS_DETAIL_TUPLE_H diff --git a/include/fennec/containers/map.h b/include/fennec/containers/map.h index 23ff305..34aca16 100644 --- a/include/fennec/containers/map.h +++ b/include/fennec/containers/map.h @@ -27,6 +27,24 @@ namespace fennec // TODO: Document +/* Ramblings + * + * Definitions: + * user = Programmer using this data structure + * + * The STL unordered-map is very contrived. Some of its functionality encourages younger programmers to use + * the exception model. Ideally, I would like this structure to never throw an error with typical use. + * + * The array access operator is, in my opinion, poorly implemented. I do not think that this operator should handle + * insertions and should handle access only. This is the only data structure in STL that has this behavior, no other + * data structure modifies contents by inherently calling operator[]. + * + * Currently, I am considering implementing this as the following: + * Access will be handled only via operator[]. Return value will be an optional which forces user validation. + * Insertions will be handled only via an insert/emplace function. + * Deletions will be handled only via an erase function. +*/ + template, typename Alloc = allocator>> struct map { public: @@ -36,13 +54,136 @@ public: using alloc_t = typename allocator_traits::template rebind; using hash_t = Hash; - map() = default; - ~map() = default; + // We only want to hash the key + struct key_hash : hash_t { + constexpr size_t operator()(const elem_t& p) const { + return hash_t::operator()(p.first); + } + }; + // We only want to compare the keys + struct node_equals : equality { + constexpr bool operator()(const elem_t& a, const elem_t& b) const { + return equality::operator()(a.first, b.first); + } + }; + + constexpr map() = default; + constexpr ~map() = default; + + constexpr value_t* operator[](const KeyT& key) { + union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers + pair root; + pair val; + + ~U() { + root.~pair(); + } + } trick = { .root = { key, 0 } }; + auto it = _set.find(trick.val); + if (it == _set.end()) { + return nullptr; + } + return &_set.at(it)->second; + } + + constexpr const value_t* operator[](const KeyT& key) const { + union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers + pair root; + pair val; + + ~U() { + root.~pair(); + } + } trick = { .root = { key, 0 } }; // Only initialize root + auto it = _set.find(trick.val); + if (it == _set.end()) { + return nullptr; + } + return &_set.at(it)->second; + } + + template + constexpr value_t* operator[](ArgsT&&...args) { + union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers + pair root; + pair val; + + ~U() { + root.~pair(); + } + } trick = { .root = { key_t(fennec::forward(args)...), 0 } }; // Only initialize root + auto it = _set.find(trick.val); + if (it == _set.end()) { + return nullptr; + } + return &_set.at(it)->second; + } + + template + constexpr const value_t* operator[](ArgsT&&...args) const { + union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers + pair root; + pair val; + + ~U() { + root.~pair(); + } + } trick = { .root = { key_t(fennec::forward(args)...), 0 } }; // Only initialize root + auto it = _set.find(trick.val); + if (it == _set.end()) { + return nullptr; + } + return &_set.at(it)->second; + } + + constexpr void insert(elem_t&& pair) { + auto it = _set.find(pair); + if (it == _set.end()) { + _set.at(it)->second = fennec::move(pair.second); + return; + } + _set.insert(fennec::forward(pair)); + } + + template + constexpr void emplace(ArgsT&&...args) { + _set.insert(elem_t(args...)); + } + + constexpr void erase(KeyT&& key) { + union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers + pair root; + pair val; + + ~U() { + root.~pair(); + } + } trick = { .root = { fennec::forward(key), 0 } }; + _set.erase(trick.val); + } + + constexpr void erase(const KeyT& key) { + KeyT val = key; + erase(fennec::move(val)); + } + + template + constexpr void erase(ArgsT&&...args) { + union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers + pair root; + pair val; + + ~U() { + root.~pair(); + } + } trick = { .root = { KeyT(fennec::forward(args)...), 0 } }; + _set.erase(trick.val); + } private: - set _set; + set _set; }; } diff --git a/include/fennec/containers/optional.h b/include/fennec/containers/optional.h index a56cde2..0be5898 100644 --- a/include/fennec/containers/optional.h +++ b/include/fennec/containers/optional.h @@ -39,6 +39,10 @@ template struct optional { public: // Constructors ======================================================================================================== + using reference_t = T&; + using pointer_t = add_pointer_t>; + using const_reference_t = const T&; + using const_pointer_t = const add_pointer_t>; /// /// \brief Default Constructor @@ -203,11 +207,11 @@ public: return _set; } - constexpr T* operator->() noexcept { + constexpr pointer_t operator->() noexcept { return _set ? &_val : nullptr; } - constexpr const T* operator->() const noexcept { + constexpr const pointer_t operator->() const noexcept { return _set ? &_val : nullptr; } diff --git a/include/fennec/containers/pair.h b/include/fennec/containers/pair.h index 6a447a7..06fcc27 100644 --- a/include/fennec/containers/pair.h +++ b/include/fennec/containers/pair.h @@ -19,6 +19,7 @@ #ifndef FENNEC_CONTAINERS_PAIR_H #define FENNEC_CONTAINERS_PAIR_H +#include #include #include @@ -42,6 +43,12 @@ struct pair { , second(fennec::forward(y)) { } + template + constexpr pair(Arg1T&& arg1, Arg2T&& arg2) + : first(fennec::forward(arg1)) + , second(fennec::forward(arg2)) { + } + constexpr pair(const pair&) = default; constexpr pair(pair&&) noexcept = default; diff --git a/include/fennec/containers/set.h b/include/fennec/containers/set.h index f1818c4..1a01d4e 100644 --- a/include/fennec/containers/set.h +++ b/include/fennec/containers/set.h @@ -22,6 +22,7 @@ // https://programming.guide/robin-hood-hashing.html #include +#include #include #include #include @@ -31,15 +32,17 @@ namespace fennec // TODO: Document -template, class Alloc = allocator> +template, class Equals = equality, class Alloc = allocator> struct set { public: using alloc_t = typename allocator_traits::template rebind; using hash_t = Hash; + using equal_t = Equals; using elem_t = T; class iterator; static constexpr size_t npos = -1; + static constexpr double default_load = 0.8; private: struct node { @@ -54,60 +57,71 @@ public: constexpr set() : _alloc() , _hash() - , _size(0) { + , _size(0) + , _load(default_load) { }; constexpr set(const hash_t& hash) : _alloc() , _hash(hash) - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(hash_t&& hash) noexcept : _alloc() , _hash(hash) - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(const alloc_t& alloc) : _alloc(alloc) , _hash() - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(alloc_t&& alloc) noexcept : _alloc(alloc) , _hash() - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(const hash_t& hash, const alloc_t& alloc) : _alloc(alloc) , _hash(hash) - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept : _alloc(alloc) , _hash(hash) - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept : _alloc(alloc) , _hash(hash) - , _size(0) { + , _size(0) + , _load(default_load) { } constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept : _alloc(alloc) , _hash(hash) - , _size(0) {} + , _size(0) + , _load(default_load) { + } constexpr set(const set& set) : _alloc(set._alloc) , _hash(set._hash) - , _size(set._size) { + , _size(set._size) + , _load(default_load) { } constexpr set(set&& set) noexcept @@ -127,7 +141,7 @@ public: } constexpr void insert(elem_t&& val) { - if (_size >= capacity()) { // expand when full + if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full _expand(); } @@ -135,7 +149,7 @@ public: size_t i = _hash(value) % capacity(); // Initial search index int psl = 0; while (_alloc[i].value) { // Search for empty cell - if (*_alloc[i].value == val) { // Check to see if this element is already inserted + if (_equal(*_alloc[i].value, val)) { // Check to see if this element is already inserted return; } if (psl >= _alloc[i].psl) { // When psl is higher, swap @@ -165,8 +179,8 @@ public: int psl = 0; // Loop while there is a value and its psl is greater than our probe - while (_alloc[i].value && _alloc[i].psl > psl) { - if (*_alloc[i].value == val) { + while (_alloc[i].value && _alloc[i].psl <= psl) { + if (_equal(*_alloc[i].value, val)) { return iterator(this, i); } i = (i + 1) % capacity(); ++psl; @@ -175,6 +189,20 @@ public: return iterator(this, npos); } + constexpr elem_t* at(const iterator& it) { + size_t i = it._i; + if (i >= capacity()) return nullptr; + if (not _alloc[i].value) return nullptr; + return &*_alloc[i].value; + } + + constexpr const elem_t* at(const iterator& it) const { + size_t i = it._i; + if (i >= capacity()) return nullopt; + if (not _alloc[i].value) return nullopt; + return &*_alloc[i].value; + } + constexpr bool contains(const elem_t& val) const { return this->find(val) != end(); } @@ -251,6 +279,7 @@ public: private: const set* _set; size_t _i; + friend set; }; constexpr iterator begin() const { @@ -271,10 +300,9 @@ public: private: constexpr void _expand() { set cpy; // Create a new set - cpy._alloc.allocate( + cpy._alloc.callocate( fennec::next_prime2(_alloc.capacity()) ); - fennec::memset(cpy._alloc.data(), 0, cpy._alloc.size()); // rehash for (size_t i = 0; i < capacity(); ++i) { @@ -289,7 +317,9 @@ private: allocation _alloc; hash_t _hash; + equal_t _equal; size_t _size; + double _load; }; } diff --git a/include/fennec/containers/tuple.h b/include/fennec/containers/tuple.h new file mode 100644 index 0000000..45c6556 --- /dev/null +++ b/include/fennec/containers/tuple.h @@ -0,0 +1,64 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +#ifndef FENNEC_CONTAINERS_TUPLE_H +#define FENNEC_CONTAINERS_TUPLE_H + +#include +#include + +namespace fennec +{ + +// TODO: Document + +template struct tuple; + + +template +typename tuple::template elem_t& get(tuple& x) { + using elem_t = typename tuple::template elem_t; + auto it = static_cast>(x); + return it; +} + +template +const typename tuple::template elem_t& get(tuple& x) { + using elem_t = typename tuple::template elem_t; + auto& it = static_cast>(x); + return it; +} + + +template +struct tuple : detail::__tuple, TypesT...> { + +public: + using base_t = detail::__tuple, TypesT...>; + + template + using elem_t = nth_element; + + template + tuple(ArgsT&&...args) : base_t(args...) { + } +}; + +} + +#endif // FENNEC_CONTAINERS_TUPLE_H diff --git a/include/fennec/fproc/strings/cstring.h b/include/fennec/fproc/strings/cstring.h index bddb0f1..9392066 100644 --- a/include/fennec/fproc/strings/cstring.h +++ b/include/fennec/fproc/strings/cstring.h @@ -25,6 +25,7 @@ #include #include +#include namespace fennec { @@ -303,6 +304,13 @@ private: bool _const; }; +template<> +struct hash : hash { + constexpr size_t operator()(const cstring& str) { + return hash::operator()(byte_array(*str, str.size())); + } +}; + } #endif // FENNEC_FPROC_STRINGS_CSTRING_H diff --git a/include/fennec/fproc/strings/string.h b/include/fennec/fproc/strings/string.h index 29daa1e..8ff8d89 100644 --- a/include/fennec/fproc/strings/string.h +++ b/include/fennec/fproc/strings/string.h @@ -154,7 +154,7 @@ public: /// \param i the index to access /// \returns a copy of the character constexpr char operator[](int i) const { - assertd(i >= 0 && i < size(), "Array Out of Bounds"); + assertd(i >= 0 && (size_t)i < size(), "Array Out of Bounds"); return _str[i]; } @@ -431,6 +431,13 @@ private: alloc_t _str; }; +template<> +struct hash : hash { + constexpr size_t operator()(const string& str) const { + return hash::operator()(byte_array(*str, str.size())); + } +}; + } diff --git a/include/fennec/lang/compare.h b/include/fennec/lang/compare.h new file mode 100644 index 0000000..649d864 --- /dev/null +++ b/include/fennec/lang/compare.h @@ -0,0 +1,123 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +#ifndef FENNEC_LANG_COMPARE_H +#define FENNEC_LANG_COMPARE_H + +#include + +namespace fennec +{ + +// equality ============================================================================================================ + +template struct equality; + +template requires has_equals_v +struct equality { + constexpr bool operator()(const T0& x, const T1& y) const { + return x == y; + } +}; + +template requires(not has_equals_v + and has_less_v and has_less_v) +struct equality { + constexpr bool operator()(const T0& x, const T1& y) const { + return not(x < y) and not(y < x); + } +}; + +template requires(not(has_equals_v) + and(not has_less_v or not has_less_v) + and(has_greater_v and has_greater_v)) +struct equality { + constexpr bool operator()(const T0& x, const T1& y) const { + return not(x > y) and not(y > x); + } +}; + + +// inequality ========================================================================================================== + +template struct inequality; + +template requires has_nequals_v +struct inequality { + constexpr bool operator()(const T0& x, const T1& y) const { + return x != y; + } +}; + +template requires has_less_v and has_less_v +struct inequality { + constexpr bool operator()(const T0& x, const T1& y) const { + return (x < y) or (y < x); + } +}; + +template requires has_greater_v and has_greater_v +struct inequality { + constexpr bool operator()(const T0& x, const T1& y) const { + return (x > y) or (y > x); + } +}; + + +// less ================================================================================================================ + +template requires has_less_v +struct less { + constexpr bool operator()(const T0& x, const T1& y) const { + return x < y; + } +}; + + +// less_equal ========================================================================================================== + +template requires has_less_equals_v +struct less_equals { + constexpr bool operator()(const T0& x, const T1& y) const { + return x <= y; + } +}; + + +// less ================================================================================================================ + +template requires has_greater_v +struct greater { + constexpr bool operator()(const T0& x, const T1& y) const { + return x < y; + } +}; + + +// less_equal ========================================================================================================== + +template requires has_greater_equals_v +struct greater_equals { + constexpr bool operator()(const T0& x, const T1& y) const { + return x <= y; + } +}; + +} + +#endif // FENNEC_LANG_COMPARE_H diff --git a/include/fennec/lang/detail/__bits.h b/include/fennec/lang/detail/__bits.h index 2ce0c5c..7540f7d 100644 --- a/include/fennec/lang/detail/__bits.h +++ b/include/fennec/lang/detail/__bits.h @@ -21,123 +21,118 @@ #include -namespace fennec +namespace fennec::detail { -namespace detail -{ - -// helper for bitwise and for 1 byte -constexpr size_t __bit_and_8(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 1; -} - -// helper for bitwise and 2 bytes at once -constexpr size_t __bit_and_16(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 2; -} - -// helper for bitwise and 4 bytes at once -constexpr size_t __bit_and_32(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 4; -} - -// helper for bitwise and 8 bytes at once -constexpr size_t __bit_and_64(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 8; -} - -// helper for selecting size -constexpr size_t __bit_and(void* dst, const void* src, size_t n) { - switch (n) { - case 0: - return 0; - case 1: - return __bit_and_8(dst, src); - case 2: case 3: - return __bit_and_16(dst, src); - case 4: case 5: case 6: case 7: - return __bit_and_32(dst, src); - default: - return __bit_and_64(dst, src); + // helper for bitwise and for 1 byte + constexpr size_t __bit_and_8(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 1; } -} - -// helper for bitwise or for 1 byte -constexpr size_t __bit_or_8(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 1; -} - -// helper for bitwise or 2 bytes at once -constexpr size_t __bit_or_16(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 2; -} - -// helper for bitwise or 4 bytes at once -constexpr size_t __bit_or_32(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 4; -} - -// helper for bitwise or 8 bytes at once -constexpr size_t __bit_or_64(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 8; -} - -// helper for selecting size -constexpr size_t __bit_or(void* dst, const void* src, size_t n) { - switch (n) { - case 0: - return 0; - case 1: - return __bit_or_8(dst, src); - case 2: case 3: - return __bit_or_16(dst, src); - case 4: case 5: case 6: case 7: - return __bit_or_32(dst, src); - default: - return __bit_or_64(dst, src); + // helper for bitwise and 2 bytes at once + constexpr size_t __bit_and_16(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 2; } -} - -// helper for bitwise and 1 byte -constexpr size_t __bit_xor_8(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 1; -} - -// helper for bitwise xor 2 bytes at once -constexpr size_t __bit_xor_16(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 2; -} - -// helper for bitwise xor 4 bytes at once -constexpr size_t __bit_xor_32(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 4; -} - -// helper for bitwise xor 8 bytes at once -constexpr size_t __bit_xor_64(void* dst, const void* src) { - *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 8; -} - -// helper for selecting size -constexpr size_t __bit_xor(void* dst, const void* src, size_t n) { - switch (n) { - case 0: - return 0; - case 1: - return __bit_xor_8(dst, src); - case 2: case 3: - return __bit_xor_16(dst, src); - case 4: case 5: case 6: case 7: - return __bit_xor_32(dst, src); - default: - return __bit_xor_64(dst, src); + // helper for bitwise and 4 bytes at once + constexpr size_t __bit_and_32(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 4; } -} -} + // helper for bitwise and 8 bytes at once + constexpr size_t __bit_and_64(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) & *static_cast(src); return 8; + } + + // helper for selecting size + constexpr size_t __bit_and(void* dst, const void* src, size_t n) { + switch (n) { + case 0: + return 0; + case 1: + return __bit_and_8(dst, src); + case 2: case 3: + return __bit_and_16(dst, src); + case 4: case 5: case 6: case 7: + return __bit_and_32(dst, src); + default: + return __bit_and_64(dst, src); + } + } + + + // helper for bitwise or for 1 byte + constexpr size_t __bit_or_8(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 1; + } + + // helper for bitwise or 2 bytes at once + constexpr size_t __bit_or_16(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 2; + } + + // helper for bitwise or 4 bytes at once + constexpr size_t __bit_or_32(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 4; + } + + // helper for bitwise or 8 bytes at once + constexpr size_t __bit_or_64(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) | *static_cast(src); return 8; + } + + // helper for selecting size + constexpr size_t __bit_or(void* dst, const void* src, size_t n) { + switch (n) { + case 0: + return 0; + case 1: + return __bit_or_8(dst, src); + case 2: case 3: + return __bit_or_16(dst, src); + case 4: case 5: case 6: case 7: + return __bit_or_32(dst, src); + default: + return __bit_or_64(dst, src); + } + } + + + // helper for bitwise and 1 byte + constexpr size_t __bit_xor_8(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 1; + } + + // helper for bitwise xor 2 bytes at once + constexpr size_t __bit_xor_16(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 2; + } + + // helper for bitwise xor 4 bytes at once + constexpr size_t __bit_xor_32(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 4; + } + + // helper for bitwise xor 8 bytes at once + constexpr size_t __bit_xor_64(void* dst, const void* src) { + *static_cast(dst) = *static_cast(dst) ^ *static_cast(src); return 8; + } + + // helper for selecting size + constexpr size_t __bit_xor(void* dst, const void* src, size_t n) { + switch (n) { + case 0: + return 0; + case 1: + return __bit_xor_8(dst, src); + case 2: case 3: + return __bit_xor_16(dst, src); + case 4: case 5: case 6: case 7: + return __bit_xor_32(dst, src); + default: + return __bit_xor_64(dst, src); + } + } } diff --git a/include/fennec/lang/detail/__numeric_transforms.h b/include/fennec/lang/detail/__numeric_transforms.h index 9cc134a..b98d433 100644 --- a/include/fennec/lang/detail/__numeric_transforms.h +++ b/include/fennec/lang/detail/__numeric_transforms.h @@ -22,42 +22,37 @@ #include #include -namespace fennec +namespace fennec::detail { -namespace detail -{ + template struct __make_unsigned : type_identity {}; -template struct __make_unsigned : type_identity {}; - -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; -template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; + template<> struct __make_unsigned : type_identity {}; -template struct __make_signed : type_identity {}; + template struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; -template<> struct __make_signed : type_identity {}; - -} + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; + template<> struct __make_signed : type_identity {}; } diff --git a/include/fennec/lang/detail/__type_sequences.h b/include/fennec/lang/detail/__type_sequences.h index fe13156..180bb4f 100644 --- a/include/fennec/lang/detail/__type_sequences.h +++ b/include/fennec/lang/detail/__type_sequences.h @@ -21,16 +21,20 @@ #include -namespace fennec +namespace fennec::detail { -namespace detail -{ + template struct __first_element : type_identity {}; -template struct __first_element : type_identity{}; + template struct __nth_element; -} + template struct __nth_element : type_identity {}; + template + struct __nth_element : conditional< + n == i, type_identity, + __nth_element + > {}; } #endif // FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H diff --git a/include/fennec/lang/detail/__type_traits.h b/include/fennec/lang/detail/__type_traits.h index c6bb455..f770620 100644 --- a/include/fennec/lang/detail/__type_traits.h +++ b/include/fennec/lang/detail/__type_traits.h @@ -22,53 +22,53 @@ #include #include -namespace fennec +namespace fennec::detail { -namespace detail -{ + // Nothing interesting to note here + template struct __is_void : false_type {}; + template<> struct __is_void : true_type {}; -// Nothing interesting to note here -template struct __is_void : false_type {}; -template<> struct __is_void : true_type {}; + template struct __is_bool : false_type {}; + template<> struct __is_bool : true_type {}; -template struct __is_bool : false_type {}; -template<> struct __is_bool : true_type {}; + template struct __is_null_pointer : false_type {}; + template<> struct __is_null_pointer : true_type {}; -template struct __is_null_pointer : false_type {}; -template<> struct __is_null_pointer : true_type {}; + // Provides definitions for all builtin int types + template struct __is_integral : false_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; + template<> struct __is_integral : true_type {}; -// Provides definitions for all builtin int types -template struct __is_integral : false_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; -template<> struct __is_integral : true_type {}; + // Most unsigned types will underflow `-1` to the types maximum value + template struct __is_signed : bool_constant {}; + template struct __is_unsigned : bool_constant= TypeT(0)> {}; -// Most unsigned types will underflow `-1` to the types maximum value -template struct __is_signed : bool_constant {}; -template struct __is_unsigned : bool_constant= TypeT(0)> {}; + template struct __is_floating_point : false_type {}; + template<> struct __is_floating_point : true_type {}; + template<> struct __is_floating_point : true_type {}; -template struct __is_floating_point : false_type {}; -template<> struct __is_floating_point : true_type {}; -template<> struct __is_floating_point : true_type {}; + template struct __is_pointer : false_type {}; + template struct __is_pointer : true_type {}; -template struct __is_pointer : false_type {}; -template struct __is_pointer : true_type {}; + template U __declval(int); + template T __declval(long); -} + template struct __declval_protector : bool_constant {}; } diff --git a/include/fennec/lang/detail/__type_transforms.h b/include/fennec/lang/detail/__type_transforms.h index a518af2..63083fc 100644 --- a/include/fennec/lang/detail/__type_transforms.h +++ b/include/fennec/lang/detail/__type_transforms.h @@ -21,34 +21,29 @@ #include -namespace fennec +namespace fennec::detail { -namespace detail -{ + template + struct __add_lvalue_reference { + using type = _Tp; + }; -template -struct __add_lvalue_reference { - using type = _Tp; -}; - -template -struct __add_lvalue_reference<_Tp, void_t<_Tp&>> { - using type = _Tp&; -}; + template + struct __add_lvalue_reference<_Tp, void_t<_Tp&>> { + using type = _Tp&; + }; -template -struct __add_rvalue_reference { - using type = _Tp; -}; + template + struct __add_rvalue_reference { + using type = _Tp; + }; -template -struct __add_rvalue_reference<_Tp, void_t<_Tp&&>> { - using type = _Tp&&; -}; - -} + template + struct __add_rvalue_reference<_Tp, void_t<_Tp&&>> { + using type = _Tp&&; + }; } diff --git a/include/fennec/lang/type_operators.h b/include/fennec/lang/type_operators.h new file mode 100644 index 0000000..95b9306 --- /dev/null +++ b/include/fennec/lang/type_operators.h @@ -0,0 +1,112 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +#ifndef FENNEC_LANG_TYPE_OPERATORS_H +#define FENNEC_LANG_TYPE_OPERATORS_H + +namespace fennec +{ + +// https://stackoverflow.com/questions/6534041/how-to-check-whether-operator-exists + +// has_equals ========================================================================================================== + +template +struct has_equals { + // Use SFINAE to check for the operator + template static auto test(U*) -> decltype(declval() == declval()); + template static auto test(...) -> false_type; + + static constexpr bool value = is_same_v(0))>; +}; + +template constexpr bool has_equals_v = has_equals::value; + +// has_nequals ========================================================================================================= + +template +struct has_nequals { + // Use SFINAE to check for the operator + template static auto test(U*) -> decltype(declval() != declval()); + template static auto test(...) -> false_type; + + static constexpr bool value = is_same_v(0))>; +}; + +template constexpr bool has_nequals_v = has_nequals::value; + + +// has_less ============================================================================================================ + +template +struct has_less { + // Use SFINAE to check for the operator + template static auto test(U*) -> decltype(declval() < declval()); + template static auto test(...) -> false_type; + + static constexpr bool value = is_same_v(0))>; +}; + +template constexpr bool has_less_v = has_less::value; + + +// has_less_equals ===================================================================================================== + +template +struct has_less_equals { + // Use SFINAE to check for the operator + template static auto test(U*) -> decltype(declval() <= declval()); + template static auto test(...) -> false_type; + + static constexpr bool value = is_same_v(0))>; +}; + +template constexpr bool has_less_equals_v = has_less_equals::value; + + +// has_greater ========================================================================================================= + +template +struct has_greater { + // Use SFINAE to check for the operator + template static auto test(U*) -> decltype(declval() > declval()); + template static auto test(...) -> false_type; + + static constexpr bool value = is_same_v(0))>; +}; + +template constexpr bool has_greater_v = has_greater::value; + + +// has_greater_equals ================================================================================================== + +template +struct has_greater_equals { + // Use SFINAE to check for the operator + template static auto test(U*) -> decltype(declval() >= declval()); + template static auto test(...) -> false_type; + + static constexpr bool value = is_same_v(0))>; +}; + +template constexpr bool has_greater_equals_v = has_greater_equals::value; + + +} + +#endif // FENNEC_LANG_TYPE_OPERATORS_H diff --git a/include/fennec/lang/type_sequences.h b/include/fennec/lang/type_sequences.h index 44ad140..1632616 100644 --- a/include/fennec/lang/type_sequences.h +++ b/include/fennec/lang/type_sequences.h @@ -69,6 +69,8 @@ template struct first_element : detail::__first_element using first_element_t = typename first_element::type; +template struct nth_element : detail::__nth_element {}; + /// /// \brief Take a Template with a Pack `ClassT` and replace the first `ArgT` of `ArgsT...` with `SubT` diff --git a/include/fennec/lang/type_traits.h b/include/fennec/lang/type_traits.h index 2c63dfd..a5c79d2 100644 --- a/include/fennec/lang/type_traits.h +++ b/include/fennec/lang/type_traits.h @@ -112,6 +112,14 @@ namespace fennec { +// fennec::declval ===================================================================================================== + +template auto declval() noexcept -> decltype(detail::__declval(0)) { + static_assert(detail::__declval_protector{}, "declval must not be used"); + return detail::__declval(0); +} + + // fennec::is_void ===================================================================================================== /// diff --git a/include/fennec/math/swizzle_storage.h b/include/fennec/math/swizzle_storage.h index edf3050..abac0a5 100644 --- a/include/fennec/math/swizzle_storage.h +++ b/include/fennec/math/swizzle_storage.h @@ -20,32 +20,29 @@ #ifndef FENNEC_MATH_SWIZZLE_STORAGE_H #define FENNEC_MATH_SWIZZLE_STORAGE_H -namespace fennec +namespace fennec::detail { -namespace detail -{ - -/// -/// \brief Backing storage struct for \ref fennec::swizzle "swizzle" -/// \tparam DataT Data Type -/// \tparam ScalarT Element Types -/// \tparam IndicesV Swizzle Order +// +// \brief Backing storage struct for \ref fennec::swizzle "swizzle" +// \tparam DataT Data Type +// \tparam ScalarT Element Types +// \tparam IndicesV Swizzle Order template 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... }; - /// - /// \brief an array containing the scalar values of this swizzle + // + // \brief an array containing the scalar values of this swizzle DataT data; - /// - /// \brief element access, returns a copy of the value at index \p i - /// \param i the index - /// \returns a copy of the scalar value at index \p i + // + // \brief element access, returns a copy of the value at index \p i + // \param i the index + // \returns a copy of the scalar value at index \p i constexpr ScalarT operator[](size_t i) const noexcept { return data[indices[i]]; } @@ -53,7 +50,5 @@ struct swizzle_storage } -} - #endif // FENNEC_MATH_SWIZZLE_STORAGE_H diff --git a/include/fennec/math/vector_base.h b/include/fennec/math/vector_base.h index 6286009..7d3751a 100644 --- a/include/fennec/math/vector_base.h +++ b/include/fennec/math/vector_base.h @@ -27,65 +27,60 @@ #include -namespace fennec +namespace fennec::detail { -namespace detail -{ - -/// -/// \brief helper class for generating vectors -/// \tparam scalar base scalar type -/// \tparam size size of the vector type +// +// \brief helper class for generating vectors +// \tparam scalar base scalar type +// \tparam size size of the vector type template struct vector_base_type_helper { - /// - /// \var SizeV - /// \brief size of the vector type + // + // \var SizeV + // \brief size of the vector type inline static constexpr size_t SizeV = size; - /// - /// \brief Base scalar type + // + // \brief Base scalar type using ScalarT = scalar; - /// - /// \brief Base vector type + // + // \brief Base vector type using VectorT = vec; - /// - /// \brief Backing array holding the elements + // + // \brief Backing array holding the elements using DataT = array; - /// - /// \brief Helper for generating a swizzle from a set of indices - /// \tparam IndicesV Indices of the vector to pull from + // + // \brief Helper for generating a swizzle from a set of indices + // \tparam IndicesV Indices of the vector to pull from template struct SwizzleGen { - /// \brief generated swizzle type + // \brief generated swizzle type using type = swizzle; }; // Specialization for single component swizzles to decay into a scalar template struct SwizzleGen { - /// \brief decayed scalar type + // \brief decayed scalar type using type = ScalarT; }; - /// - /// \brief backing storage type + // + // \brief backing storage type using StorageT = vector_storage; }; -/// -/// \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 using vector_base_type = typename vector_base_type_helper::StorageT; } -} - #endif // FENNEC_MATH_VECTOR_BASE_H diff --git a/include/fennec/math/vector_storage.h b/include/fennec/math/vector_storage.h index e34cc2a..e3c634b 100644 --- a/include/fennec/math/vector_storage.h +++ b/include/fennec/math/vector_storage.h @@ -20,7 +20,7 @@ #ifndef FENNEC_MATH_VECTOR_STORAGE_H #define FENNEC_MATH_VECTOR_STORAGE_H -#ifdef __GNUC__ +#if FENNEC_COMPILER_GCC #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" #endif @@ -30,584 +30,579 @@ #pragma warning(disable:4201) #endif -namespace fennec +namespace fennec::detail { -namespace detail -{ - -/// -/// \brief backing storage type for vectors -/// \tparam SizeV size of the vector -/// \tparam SwizzleGenT generator for swizzles -/// \tparam DataT backing data type -template class SwizzleGenT, typename DataT> struct vector_storage; + // + // \brief backing storage type for vectors + // \tparam SizeV size of the vector + // \tparam SwizzleGenT generator for swizzles + // \tparam DataT backing data type + template class SwizzleGenT, typename DataT> struct vector_storage; -// specialization for single component vectors -template