diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml new file mode 100644 index 0000000..c31874a --- /dev/null +++ b/.github/workflows/dev.yml @@ -0,0 +1,51 @@ +name: Test + +on: + pull_request: + branches: [ master ] + paths-ignore: + - '**/*.md' + - '**/*.gitignore' + - '**/Doxyfile' + +jobs: + Build: + strategy: + matrix: + machine: + - os: ubuntu-latest + action: gmake2 + toolset: gcc + - os: ubuntu-latest + action: gmake2 + toolset: clang + - os: windows-latest + action: vs2019 + toolset: msc + runs-on: ${{ matrix.machine.os }} + permissions: + actions: read + contents: read + security-events: write + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Setup premake + uses: abel0b/setup-premake@v2.2 + with: + version: "5.0.0-beta1" + - name: Install GCC + if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'gcc' + run: sudo apt-get update && sudo apt-get install -y gcc g++ + - name: Install Clang & LLVM + if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'clang' + run: sudo apt-get update && sudo apt-get install -y clang llvm lld + - name: Install msbuild to PATH + if: matrix.machine.os == 'windows-latest' && matrix.machine.toolset == 'msc' + uses: microsoft/setup-msbuild@v1.1 + - name: Run premake + run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=C++20 + - name: Build + run: premake5 build --config=debug + - name: Run test + run: premake5 test --config=debug \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index cc73e68..f391d44 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,14 +1,8 @@ -name: Test +name: Staging on: push: branches: [ master ] - pull_request: - branches: [ master ] - paths-ignore: - - '**/*.md' - - '**/*.gitignore' - - '**/Doxyfile' jobs: Build: @@ -49,13 +43,13 @@ jobs: if: matrix.machine.os == 'windows-latest' && matrix.machine.toolset == 'msc' uses: microsoft/setup-msbuild@v1.1 - name: Run premake - run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} + run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=${{ matrix.dialect }} - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: cpp - name: Build - run: premake5 build --config=${{ matrix.config }} --dialect=${{ matrix.dialect }} + run: premake5 build --config=${{ matrix.config }} - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 - name: Run test diff --git a/include/ktl/allocators/cascading.h b/include/ktl/allocators/cascading.h index a6e5ebd..d32a927 100644 --- a/include/ktl/allocators/cascading.h +++ b/include/ktl/allocators/cascading.h @@ -45,10 +45,11 @@ namespace ktl cascading() noexcept : m_Node(nullptr) {} - cascading(const cascading&) noexcept = delete; + cascading(const cascading&) = delete; - cascading(cascading&& other) noexcept : - m_Node(other.m_Node) + cascading(cascading&& other) + noexcept(std::is_nothrow_move_constructible_v) : + m_Node(std::move(other.m_Node)) { other.m_Node = nullptr; } @@ -58,13 +59,14 @@ namespace ktl release(); } - cascading& operator=(const cascading&) noexcept = delete; + cascading& operator=(const cascading&) = delete; - cascading& operator=(cascading&& rhs) noexcept + cascading& operator=(cascading&& rhs) + noexcept(std::is_nothrow_move_assignable_v) { release(); - m_Node = rhs.m_Node; + m_Node = std::move(rhs.m_Node); rhs.m_Node = nullptr; @@ -88,13 +90,16 @@ namespace ktl * @param n The amount of bytes to allocate memory for * @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated */ - void* allocate(size_type n) + void* allocate(size_type n) noexcept( + std::is_nothrow_default_constructible_v && + detail::has_nothrow_allocate_v && + (!detail::has_max_size_v || detail::has_nothrow_max_size_v)) { // Add an initial allocator if (!m_Node) m_Node = detail::aligned_new(detail::ALIGNMENT); - if constexpr (detail::has_max_size::value) + if constexpr (detail::has_max_size_v) { if (n > m_Node->Allocator.max_size()) return nullptr; @@ -125,7 +130,10 @@ namespace ktl * @param p The location in memory to deallocate * @param n The size that was initially allocated */ - void deallocate(void* p, size_type n) noexcept + void deallocate(void* p, size_type n) noexcept( + std::is_nothrow_destructible_v && + detail::has_nothrow_owns_v && + detail::has_nothrow_deallocate_v) { KTL_ASSERT(p != nullptr); @@ -165,7 +173,9 @@ namespace ktl */ template typename std::enable_if, void>::type - construct(T* p, Args&&... args) + construct(T* p, Args&&... args) noexcept( + detail::has_nothrow_owns_v && + detail::has_nothrow_construct_v) { node* next = m_Node; while (next) @@ -179,6 +189,9 @@ namespace ktl next = next->Next; } + // If we ever get to this point, something has gone wrong with the internal allocators + KTL_ASSERT(false); + ::new(p) T(std::forward(args)...); } @@ -189,7 +202,9 @@ namespace ktl */ template typename std::enable_if, void>::type - destroy(T* p) + destroy(T* p) noexcept( + detail::has_nothrow_owns_v && + detail::has_nothrow_destroy_v) { node* next = m_Node; while (next) @@ -203,6 +218,9 @@ namespace ktl next = next->Next; } + // If we ever get to this point, something has gone wrong with the internal allocators + KTL_ASSERT(false); + p->~T(); } #pragma endregion @@ -215,7 +233,8 @@ namespace ktl */ template typename std::enable_if, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v) { return m_Node->Allocator.max_size(); } @@ -226,6 +245,7 @@ namespace ktl * @return Whether the allocator owns @p p */ bool owns(void* p) const + noexcept(detail::has_nothrow_owns_v) { node* next = m_Node; while (next) @@ -241,7 +261,8 @@ namespace ktl #pragma endregion private: - void release() noexcept + void release() + noexcept(std::is_nothrow_destructible_v) { node* next = m_Node; while (next) diff --git a/include/ktl/allocators/fallback.h b/include/ktl/allocators/fallback.h index 656c0b5..181af7b 100644 --- a/include/ktl/allocators/fallback.h +++ b/include/ktl/allocators/fallback.h @@ -29,16 +29,15 @@ namespace ktl public: typedef typename detail::get_size_type_t

size_type; - fallback() noexcept : - m_Primary(), - m_Fallback() {} + fallback() = default; /** * @brief Constructor for forwarding a single argument to the primary allocator */ template>> - explicit fallback(Primary&& primary) noexcept : + typename = std::enable_if_t>> + explicit fallback(Primary&& primary) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_default_constructible_v) : m_Primary(std::forward(primary)), m_Fallback() {} @@ -47,9 +46,10 @@ namespace ktl */ template && - detail::can_construct_v>> - explicit fallback(Primary&& primary, Fallback&& fallback) noexcept : + std::is_constructible_v && + std::is_constructible_v>> + explicit fallback(Primary&& primary, Fallback&& fallback) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : m_Primary(std::forward(primary)), m_Fallback(std::forward(fallback)) {} @@ -58,8 +58,9 @@ namespace ktl */ template>> - explicit fallback(std::tuple&& primary) noexcept : + std::is_constructible_v>> + explicit fallback(std::tuple&& primary) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_default_constructible_v) : m_Primary(std::make_from_tuple

(std::forward>(primary))), m_Fallback() {} @@ -68,32 +69,36 @@ namespace ktl */ template&& - detail::can_construct_v>> - explicit fallback(std::tuple&& primary, std::tuple&& fallback) noexcept : + std::is_constructible_v&& + std::is_constructible_v>> + explicit fallback(std::tuple&& primary, std::tuple&& fallback) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : m_Primary(std::make_from_tuple

(std::forward>(primary))), m_Fallback(std::make_from_tuple(std::forward>(fallback))) {} - fallback(const fallback&) noexcept = default; + fallback(const fallback&) = default; - fallback(fallback&&) noexcept = default; + fallback(fallback&&) = default; - fallback& operator=(const fallback&) noexcept = default; + fallback& operator=(const fallback&) = default; - fallback& operator=(fallback&&) noexcept = default; + fallback& operator=(fallback&&) = default; - bool operator==(const fallback& rhs) const noexcept + bool operator==(const fallback& rhs) const + noexcept(detail::has_nothrow_equal_v

&& detail::has_nothrow_equal_v) { return m_Primary == rhs.m_Primary && m_Fallback == rhs.m_Fallback; } - bool operator!=(const fallback& rhs) const noexcept + bool operator!=(const fallback& rhs) const + noexcept(detail::has_nothrow_not_equal_v

&& detail::has_nothrow_not_equal_v) { return m_Primary != rhs.m_Primary || m_Fallback != rhs.m_Fallback; } #pragma region Allocation void* allocate(size_t n) + noexcept(detail::has_nothrow_allocate_v

&& detail::has_nothrow_allocate_v) { void* ptr = m_Primary.allocate(n); if (!ptr) @@ -102,6 +107,7 @@ namespace ktl } void deallocate(void* p, size_t n) + noexcept(detail::has_nothrow_deallocate_v

&& detail::has_nothrow_deallocate_v) { if (m_Primary.owns(p)) { @@ -116,7 +122,10 @@ namespace ktl #pragma region Construction template typename std::enable_if || detail::has_construct_v, void>::type - construct(T* p, Args&&... args) + construct(T* p, Args&&... args) noexcept( + (!detail::has_construct_v || detail::has_nothrow_construct_v) && + (!detail::has_construct_v || detail::has_nothrow_construct_v) && + std::is_nothrow_constructible_v) { bool owned = m_Primary.owns(p); @@ -143,7 +152,10 @@ namespace ktl template typename std::enable_if || detail::has_destroy_v, void>::type - destroy(T* p) + destroy(T* p) noexcept( + (!detail::has_destroy_v || detail::has_nothrow_destroy_v) && + (!detail::has_destroy_v || detail::has_nothrow_destroy_v) && + std::is_nothrow_destructible_v) { bool owned = m_Primary.owns(p); @@ -172,7 +184,8 @@ namespace ktl #pragma region Utility template typename std::enable_if && detail::has_max_size_v, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v && detail::has_nothrow_max_size_v) { return (std::max)(m_Primary.max_size(), m_Fallback.max_size()); } @@ -180,6 +193,7 @@ namespace ktl template typename std::enable_if && detail::has_owns_v, bool>::type owns(void* p) const + noexcept(detail::has_nothrow_owns_v && detail::has_nothrow_owns_v) { if (m_Primary.owns(p)) return true; diff --git a/include/ktl/allocators/freelist.h b/include/ktl/allocators/freelist.h index 637511a..c7479c9 100644 --- a/include/ktl/allocators/freelist.h +++ b/include/ktl/allocators/freelist.h @@ -37,7 +37,9 @@ namespace ktl }; public: - freelist() noexcept : + template + freelist() + noexcept(std::is_nothrow_default_constructible_v) : m_Alloc(), m_Free(nullptr) {} @@ -46,14 +48,16 @@ namespace ktl */ template>> - explicit freelist(Args&&... args) noexcept : + std::is_constructible_v>> + explicit freelist(Args&&... args) + noexcept(std::is_nothrow_constructible_v) : m_Alloc(std::forward(args)...), m_Free(nullptr) {} - freelist(const freelist&) noexcept = delete; + freelist(const freelist&) = delete; - freelist(freelist&& other) noexcept : + freelist(freelist&& other) + noexcept(std::is_nothrow_move_constructible_v) : m_Alloc(std::move(other.m_Alloc)), m_Free(other.m_Free) { @@ -68,9 +72,10 @@ namespace ktl release(); } - freelist& operator=(const freelist&) noexcept = delete; + freelist& operator=(const freelist&) = delete; - freelist& operator=(freelist&& rhs) noexcept + freelist& operator=(freelist&& rhs) + noexcept(std::is_nothrow_move_assignable_v) { release(); @@ -85,12 +90,14 @@ namespace ktl return *this; } - bool operator==(const freelist& rhs) const noexcept + bool operator==(const freelist& rhs) const + noexcept(detail::has_nothrow_equal_v) { return m_Alloc == rhs.m_Alloc && m_Free == rhs.m_Free; } - bool operator!=(const freelist& rhs) const noexcept + bool operator!=(const freelist& rhs) const + noexcept(detail::has_nothrow_not_equal_v) { return m_Alloc != rhs.m_Alloc || m_Free != rhs.m_Free; } @@ -104,6 +111,7 @@ namespace ktl * @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated */ void* allocate(size_type n) + noexcept(detail::has_nothrow_allocate_v) { if (n > Min && n <= Max) { @@ -126,7 +134,7 @@ namespace ktl * @param p The location in memory to deallocate * @param n The size that was initially allocated */ - void deallocate(void* p, size_type n) + void deallocate(void* p, size_type n) noexcept { KTL_ASSERT(p != nullptr); @@ -150,6 +158,7 @@ namespace ktl template typename std::enable_if, void>::type construct(T* p, Args&&... args) + noexcept(detail::has_nothrow_construct_v) { m_Alloc.construct(p, std::forward(args)...); } @@ -162,6 +171,7 @@ namespace ktl template typename std::enable_if, void>::type destroy(T* p) + noexcept(detail::has_nothrow_destroy_v) { m_Alloc.destroy(p); } @@ -175,7 +185,8 @@ namespace ktl */ template typename std::enable_if, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v) { return m_Alloc.max_size(); } @@ -189,6 +200,7 @@ namespace ktl template typename std::enable_if, bool>::type owns(void* p) const + noexcept(detail::has_nothrow_owns_v) { return m_Alloc.owns(p); } @@ -198,7 +210,7 @@ namespace ktl * @brief Returns a reference to the underlying allocator * @return The allocator */ - Alloc& get_allocator() + Alloc& get_allocator() noexcept { return m_Alloc; } @@ -207,13 +219,14 @@ namespace ktl * @brief Returns a const reference to the underlying allocator * @return The allocator */ - const Alloc& get_allocator() const + const Alloc& get_allocator() const noexcept { return m_Alloc; } private: - void release() noexcept + void release() + noexcept(detail::has_nothrow_deallocate_v) { link* next = m_Free; while (next) diff --git a/include/ktl/allocators/linear_allocator.h b/include/ktl/allocators/linear_allocator.h index 4d9c8e2..4a68f62 100644 --- a/include/ktl/allocators/linear_allocator.h +++ b/include/ktl/allocators/linear_allocator.h @@ -72,7 +72,7 @@ namespace ktl * @param n The amount of bytes to allocate memory for * @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated */ - void* allocate(size_t n) + void* allocate(size_t n) noexcept { size_t totalSize = n + detail::align_to_architecture(n); @@ -125,7 +125,7 @@ namespace ktl * @param p The location of the object in memory * @return Whether the allocator owns @p p */ - bool owns(void* p) const + bool owns(void* p) const noexcept { return p >= m_Data && p < m_Data + Size; } diff --git a/include/ktl/allocators/mallocator.h b/include/ktl/allocators/mallocator.h index ed7024b..2502397 100644 --- a/include/ktl/allocators/mallocator.h +++ b/include/ktl/allocators/mallocator.h @@ -36,7 +36,7 @@ namespace ktl } #pragma region Allocation - void* allocate(size_t n) + void* allocate(size_t n) noexcept { return detail::aligned_malloc(n, detail::ALIGNMENT); } diff --git a/include/ktl/allocators/null_allocator.h b/include/ktl/allocators/null_allocator.h index ee6e739..7cfff85 100644 --- a/include/ktl/allocators/null_allocator.h +++ b/include/ktl/allocators/null_allocator.h @@ -32,7 +32,7 @@ namespace ktl } #pragma region Allocation - void* allocate(size_t n) + void* allocate(size_t n) noexcept { return nullptr; } @@ -44,7 +44,7 @@ namespace ktl #pragma endregion #pragma region Utility - bool owns(void* p) const + bool owns(void* p) const noexcept { return p == nullptr; } diff --git a/include/ktl/allocators/overflow.h b/include/ktl/allocators/overflow.h index 202a78f..96ab73d 100644 --- a/include/ktl/allocators/overflow.h +++ b/include/ktl/allocators/overflow.h @@ -32,7 +32,8 @@ namespace ktl * @brief Construct the allocator with a reference to a stream object * @param stream The stream to use when leaks or corruption happens */ - explicit overflow(Stream& stream) noexcept : + explicit overflow(Stream& stream) + noexcept(std::is_nothrow_default_constructible_v) : m_Stream(stream), m_Alloc(), m_Allocs(0), @@ -43,16 +44,17 @@ namespace ktl */ template>> - explicit overflow(Stream& stream, Args&&... args) noexcept : + std::is_constructible_v>> + explicit overflow(Stream& stream, Args&&... args) + noexcept(std::is_nothrow_constructible_v) : m_Stream(stream), m_Alloc(std::forward(args)...), m_Allocs(0), m_Constructs(0) {} - overflow(const overflow&) noexcept = default; + overflow(const overflow&) = default; - overflow(overflow&&) noexcept = default; + overflow(overflow&&) = default; ~overflow() { @@ -75,16 +77,18 @@ namespace ktl } } - overflow& operator=(const overflow&) noexcept = default; + overflow& operator=(const overflow&) = default; - overflow& operator=(overflow&&) noexcept = default; + overflow& operator=(overflow&&) = default; - bool operator==(const overflow& rhs) const noexcept + bool operator==(const overflow& rhs) const + noexcept(detail::has_nothrow_equal_v) { return m_Alloc == rhs.m_Alloc; } - bool operator!=(const overflow& rhs) const noexcept + bool operator!=(const overflow& rhs) const + noexcept(detail::has_nothrow_not_equal_v) { return m_Alloc != rhs.m_Alloc; } @@ -98,6 +102,7 @@ namespace ktl * @return A location in memory that is at least @p n bytes big or nullptr if it could not be allocated */ void* allocate(size_type n) + noexcept(detail::has_nothrow_allocate_v) { m_Allocs += n; @@ -119,6 +124,7 @@ namespace ktl * @param n The size that was initially allocated */ void deallocate(void* p, size_type n) + noexcept(detail::has_nothrow_deallocate_v) { KTL_ASSERT(p != nullptr); @@ -170,7 +176,9 @@ namespace ktl * @param ...args A range of arguments to use to construct the object */ template - void construct(T* p, Args&&... args) + void construct(T* p, Args&&... args) noexcept( + (detail::has_construct_v && detail::has_nothrow_construct_v) || + std::is_nothrow_constructible_v) { m_Constructs++; @@ -186,7 +194,9 @@ namespace ktl * @param p The location of the object in memory */ template - void destroy(T* p) + void destroy(T* p) noexcept( + (detail::has_destroy_v&& detail::has_nothrow_destroy_v) || + std::is_nothrow_destructible_v) { m_Constructs--; @@ -205,7 +215,8 @@ namespace ktl */ template typename std::enable_if, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v) { return m_Alloc.max_size(); } @@ -219,6 +230,7 @@ namespace ktl template typename std::enable_if, bool>::type owns(void* p) const + noexcept(detail::has_nothrow_owns_v) { return m_Alloc.owns(p); } @@ -228,7 +240,7 @@ namespace ktl * @brief Returns a reference to the underlying allocator * @return The allocator */ - Alloc& get_allocator() + Alloc& get_allocator() noexcept { return m_Alloc; } @@ -237,7 +249,7 @@ namespace ktl * @brief Returns a const reference to the underlying allocator * @return The allocator */ - const Alloc& get_allocator() const + const Alloc& get_allocator() const noexcept { return m_Alloc; } @@ -246,7 +258,7 @@ namespace ktl * @brief Return a reference to the stream that will be used when leaks or corruption occur * @return The stream */ - Stream& get_stream() + Stream& get_stream() noexcept { return m_Stream; } @@ -255,7 +267,7 @@ namespace ktl * @brief Return a const reference to the stream that will be used when leaks or corruption occur * @return The stream */ - const Stream& get_stream() const + const Stream& get_stream() const noexcept { return m_Stream; } diff --git a/include/ktl/allocators/segragator.h b/include/ktl/allocators/segragator.h index 0d5c1ef..82c124c 100644 --- a/include/ktl/allocators/segragator.h +++ b/include/ktl/allocators/segragator.h @@ -26,16 +26,15 @@ namespace ktl public: typedef typename detail::get_size_type_t

size_type; - segragator() noexcept : - m_Primary(), - m_Fallback() {} + segragator() = default; /** * @brief Constructor for forwarding a single argument to the primary allocator */ template>> - explicit segragator(Primary&& primary) noexcept : + typename = std::enable_if_t>> + explicit segragator(Primary&& primary) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_default_constructible_v) : m_Primary(std::forward(primary)), m_Fallback() {} @@ -44,9 +43,10 @@ namespace ktl */ template && - detail::can_construct_v>> - explicit segragator(Primary&& primary, Fallback&& fallback) noexcept : + std::is_constructible_v && + std::is_constructible_v>> + explicit segragator(Primary&& primary, Fallback&& fallback) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : m_Primary(std::forward(primary)), m_Fallback(std::forward(fallback)) {} @@ -55,8 +55,9 @@ namespace ktl */ template>> - explicit segragator(std::tuple&& primary) noexcept : + std::is_constructible_v>> + explicit segragator(std::tuple&& primary) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_default_constructible_v) : m_Primary(std::make_from_tuple

(std::forward>(primary))), m_Fallback() {} @@ -65,32 +66,36 @@ namespace ktl */ template && - detail::can_construct_v>> - explicit segragator(std::tuple&& primary, std::tuple&& fallback) noexcept : + std::is_constructible_v && + std::is_constructible_v>> + explicit segragator(std::tuple&& primary, std::tuple&& fallback) + noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) : m_Primary(std::make_from_tuple

(std::forward>(primary))), m_Fallback(std::make_from_tuple(std::forward>(fallback))) {} - segragator(const segragator&) noexcept = default; + segragator(const segragator&) = default; - segragator(segragator&&) noexcept = default; + segragator(segragator&&) = default; - segragator& operator=(const segragator&) noexcept = default; + segragator& operator=(const segragator&) = default; - segragator& operator=(segragator&&) noexcept = default; + segragator& operator=(segragator&&) = default; - bool operator==(const segragator& rhs) const noexcept + bool operator==(const segragator& rhs) const + noexcept(detail::has_nothrow_equal_v

&& detail::has_nothrow_equal_v) { return m_Primary == rhs.m_Primary && m_Fallback == rhs.m_Fallback; } - bool operator!=(const segragator& rhs) const noexcept + bool operator!=(const segragator& rhs) const + noexcept(detail::has_nothrow_not_equal_v

&& detail::has_nothrow_not_equal_v) { return m_Primary != rhs.m_Primary || m_Fallback != rhs.m_Fallback; } #pragma region Allocation void* allocate(size_t n) + noexcept(detail::has_nothrow_allocate_v

&& detail::has_nothrow_allocate_v) { if (n <= Threshold) return m_Primary.allocate(n); @@ -99,6 +104,7 @@ namespace ktl } void deallocate(void* p, size_t n) + noexcept(detail::has_nothrow_deallocate_v

&& detail::has_nothrow_deallocate_v) { if (n <= Threshold) return m_Primary.deallocate(p, n); @@ -110,7 +116,10 @@ namespace ktl #pragma region Construction template typename std::enable_if || detail::has_construct_v, void>::type - construct(T* p, Args&&... args) + construct(T* p, Args&&... args) noexcept( + (!detail::has_construct_v || detail::has_nothrow_construct_v) && + (!detail::has_construct_v || detail::has_nothrow_construct_v) && + std::is_nothrow_constructible_v) { bool owned = m_Primary.owns(p); @@ -137,7 +146,10 @@ namespace ktl template typename std::enable_if || detail::has_destroy_v, void>::type - destroy(T* p) + destroy(T* p) noexcept( + (!detail::has_destroy_v || detail::has_nothrow_destroy_v) && + (!detail::has_destroy_v || detail::has_nothrow_destroy_v) && + std::is_nothrow_destructible_v) { bool owned = m_Primary.owns(p); @@ -166,7 +178,8 @@ namespace ktl #pragma region Utility template typename std::enable_if && detail::has_max_size_v, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v && detail::has_nothrow_max_size_v) { return (std::max)(m_Primary.max_size(), m_Fallback.max_size()); } @@ -174,6 +187,7 @@ namespace ktl template typename std::enable_if && detail::has_owns_v, bool>::type owns(void* p) const + noexcept(detail::has_nothrow_owns_v && detail::has_nothrow_owns_v) { if (m_Primary.owns(p)) return true; diff --git a/include/ktl/allocators/shared.h b/include/ktl/allocators/shared.h index f238901..f27b574 100644 --- a/include/ktl/allocators/shared.h +++ b/include/ktl/allocators/shared.h @@ -24,8 +24,9 @@ namespace ktl template>> - block(Args&&... alloc) noexcept : + std::is_constructible_v>> + block(Args&&... alloc) + noexcept(std::is_nothrow_constructible_v) : Allocator(std::forward(alloc)...), UseCount(1) {} }; @@ -33,7 +34,9 @@ namespace ktl public: typedef typename detail::get_size_type_t size_type; - shared() noexcept : + template + shared() + noexcept(std::is_nothrow_default_constructible_v) : m_Block(detail::aligned_new(detail::ALIGNMENT)) {} /** @@ -41,8 +44,9 @@ namespace ktl */ template>> - explicit shared(Args&&... alloc) noexcept : + std::is_constructible_v>> + explicit shared(Args&&... alloc) + noexcept(std::is_nothrow_constructible_v) : m_Block(detail::aligned_new(detail::ALIGNMENT, std::forward(alloc)...)) {} shared(const shared& other) noexcept : @@ -52,7 +56,7 @@ namespace ktl } shared(shared&& other) noexcept : - m_Block(std::move(other.m_Block)) + m_Block(other.m_Block) { other.m_Block = nullptr; } @@ -62,7 +66,8 @@ namespace ktl decrement(); } - shared& operator=(const shared& rhs) noexcept + shared& operator=(const shared& rhs) + noexcept(noexcept(decrement())) { decrement(); @@ -73,7 +78,8 @@ namespace ktl return *this; } - shared& operator=(shared&& rhs) noexcept + shared& operator=(shared&& rhs) + noexcept(noexcept(decrement())) { decrement(); @@ -84,23 +90,27 @@ namespace ktl return *this; } - bool operator==(const shared& rhs) const noexcept + bool operator==(const shared& rhs) const + noexcept(detail::has_nothrow_equal_v) { return m_Block == rhs.m_Block && m_Block->Allocator == rhs.m_Block->Allocator; } - bool operator!=(const shared& rhs) const noexcept + bool operator!=(const shared& rhs) const + noexcept(detail::has_nothrow_not_equal_v) { return m_Block != rhs.m_Block || m_Block->Allocator != rhs.m_Block->Allocator; } #pragma region Allocation void* allocate(size_t n) + noexcept(detail::has_nothrow_allocate_v) { return m_Block->Allocator.allocate(n); } void deallocate(void* p, size_t n) + noexcept(detail::has_nothrow_deallocate_v) { m_Block->Allocator.deallocate(p, n); } @@ -110,6 +120,7 @@ namespace ktl template typename std::enable_if, void>::type construct(T* p, Args&&... args) + noexcept(detail::has_nothrow_construct_v) { m_Block->Allocator.construct(p, std::forward(args)...); } @@ -117,6 +128,7 @@ namespace ktl template typename std::enable_if, void>::type destroy(T* p) + noexcept(detail::has_nothrow_destroy_v) { m_Block->Allocator.destroy(p); } @@ -125,7 +137,8 @@ namespace ktl #pragma region Utility template typename std::enable_if, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v) { return m_Block->Allocator.max_size(); } @@ -133,23 +146,24 @@ namespace ktl template typename std::enable_if, bool>::type owns(void* p) const + noexcept(detail::has_nothrow_owns_v) { return m_Block->Allocator.owns(p); } #pragma endregion - Alloc& get_allocator() + Alloc& get_allocator() noexcept { return m_Block->Allocator; } - const Alloc& get_allocator() const + const Alloc& get_allocator() const noexcept { return m_Block->Allocator; } private: - void increment() + void increment() noexcept { if (!m_Block) return; @@ -157,6 +171,7 @@ namespace ktl } void decrement() + noexcept(std::is_nothrow_destructible_v) { if (!m_Block) return; diff --git a/include/ktl/allocators/stack_allocator.h b/include/ktl/allocators/stack_allocator.h index 327d662..2eb8735 100644 --- a/include/ktl/allocators/stack_allocator.h +++ b/include/ktl/allocators/stack_allocator.h @@ -60,7 +60,7 @@ namespace ktl } #pragma region Allocation - void* allocate(size_t n) + void* allocate(size_t n) noexcept { size_t totalSize = n + detail::align_to_architecture(n); @@ -96,7 +96,7 @@ namespace ktl return Size; } - bool owns(void* p) const + bool owns(void* p) const noexcept { return p >= m_Block->Data && p < m_Block->Data + Size; } diff --git a/include/ktl/allocators/threaded.h b/include/ktl/allocators/threaded.h index fd8d9c7..88290f0 100644 --- a/include/ktl/allocators/threaded.h +++ b/include/ktl/allocators/threaded.h @@ -27,8 +27,9 @@ namespace ktl template>> - block(Args&&... alloc) noexcept : + std::is_constructible_v>> + block(Args&&... alloc) + noexcept(std::is_nothrow_constructible_v) : Allocator(std::forward(alloc)...), UseCount(1), Lock() {} @@ -37,7 +38,9 @@ namespace ktl public: typedef typename detail::get_size_type_t size_type; - threaded() noexcept : + template + threaded() + noexcept(std::is_nothrow_default_constructible_v) : m_Block(detail::aligned_new(detail::ALIGNMENT)) {} /** @@ -45,8 +48,9 @@ namespace ktl */ template>> - explicit threaded(Args&&... alloc) noexcept : + std::is_constructible_v>> + explicit threaded(Args&&... alloc) + noexcept(std::is_nothrow_constructible_v) : m_Block(detail::aligned_new(detail::ALIGNMENT, std::forward(alloc)...)) {} threaded(const threaded& other) noexcept : @@ -56,7 +60,7 @@ namespace ktl } threaded(threaded&& other) noexcept : - m_Block(std::move(other.m_Block)) + m_Block(other.m_Block) { other.m_Block = nullptr; } @@ -66,7 +70,8 @@ namespace ktl decrement(); } - threaded& operator=(const threaded& rhs) noexcept + threaded& operator=(const threaded& rhs) + noexcept(noexcept(decrement())) { decrement(); @@ -77,7 +82,8 @@ namespace ktl return *this; } - threaded& operator=(threaded&& rhs) noexcept + threaded& operator=(threaded&& rhs) + noexcept(noexcept(decrement())) { decrement(); @@ -88,25 +94,27 @@ namespace ktl return *this; } - bool operator==(const threaded& rhs) const noexcept + bool operator==(const threaded& rhs) const + noexcept(detail::has_nothrow_equal_v) { return m_Block == rhs.m_Block && m_Block->Allocator == rhs.m_Block->Allocator; } - bool operator!=(const threaded& rhs) const noexcept + bool operator!=(const threaded& rhs) const + noexcept(detail::has_nothrow_not_equal_v) { return m_Block != rhs.m_Block || m_Block->Allocator != rhs.m_Block->Allocator; } #pragma region Allocation - void* allocate(size_t n) + void* allocate(size_t n) // Lock cannot be noexcept { std::lock_guard lock(m_Block->Lock); return m_Block->Allocator.allocate(n); } - void deallocate(void* p, size_t n) + void deallocate(void* p, size_t n) // Lock cannot be noexcept { std::lock_guard lock(m_Block->Lock); @@ -117,7 +125,7 @@ namespace ktl #pragma region Construction template typename std::enable_if, void>::type - construct(T* p, Args&&... args) + construct(T* p, Args&&... args) // Lock cannot be noexcept { std::lock_guard lock(m_Block->Lock); @@ -126,7 +134,7 @@ namespace ktl template typename std::enable_if, void>::type - destroy(T* p) + destroy(T* p) // Lock cannot be noexcept { std::lock_guard lock(m_Block->Lock); @@ -137,7 +145,8 @@ namespace ktl #pragma region Utility template typename std::enable_if, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v) { return m_Block->Allocator.max_size(); } @@ -145,23 +154,24 @@ namespace ktl template typename std::enable_if, bool>::type owns(void* p) const + noexcept(detail::has_nothrow_owns_v) { return m_Block->Allocator.owns(p); } #pragma endregion - Alloc& get_allocator() + Alloc& get_allocator() noexcept { return m_Block->Allocator; } - const Alloc& get_allocator() const + const Alloc& get_allocator() const noexcept { return m_Block->Allocator; } private: - void increment() + void increment() noexcept { if (!m_Block) return; @@ -169,6 +179,7 @@ namespace ktl } void decrement() + noexcept(std::is_nothrow_destructible_v) { if (!m_Block) return; diff --git a/include/ktl/allocators/type_allocator.h b/include/ktl/allocators/type_allocator.h index 2f03bfe..a795e8d 100644 --- a/include/ktl/allocators/type_allocator.h +++ b/include/ktl/allocators/type_allocator.h @@ -40,28 +40,35 @@ namespace ktl * @brief Default constructor * @note Only defined if the underlying allocator defines it */ - type_allocator() noexcept = default; + type_allocator() = default; /** * @brief Constructor for forwarding any arguments to the underlying allocator */ template>> - explicit type_allocator(Args&&... alloc) noexcept : + std::is_constructible_v>> + explicit type_allocator(Args&&... alloc) + noexcept(std::is_nothrow_constructible_v) : m_Alloc(std::forward(alloc)...) {} - type_allocator(const type_allocator&) noexcept = default; + type_allocator(const type_allocator&) = default; - type_allocator(type_allocator&&) noexcept = default; + type_allocator(type_allocator&&) = default; template - type_allocator(const type_allocator& other) noexcept : + type_allocator(const type_allocator& other) + noexcept(std::is_nothrow_constructible_v&>) : m_Alloc(other.m_Alloc) {} - type_allocator& operator=(const type_allocator&) noexcept = default; + template + type_allocator(type_allocator&& other) + noexcept(std::is_nothrow_constructible_v&&>) : + m_Alloc(std::move(other.m_Alloc)) {} + + type_allocator& operator=(const type_allocator&) = default; - type_allocator& operator=(type_allocator&&) noexcept = default; + type_allocator& operator=(type_allocator&&) = default; #pragma region Allocation /** @@ -70,6 +77,7 @@ namespace ktl * @return A location in memory that is at least @p n objects big or nullptr if it could not be allocated */ value_type* allocate(size_t n) + noexcept(noexcept(m_Alloc.allocate(n))) { return reinterpret_cast(m_Alloc.allocate(sizeof(value_type) * n)); } @@ -80,6 +88,7 @@ namespace ktl * @param n The size that was initially allocated */ void deallocate(value_type* p, size_t n) + noexcept(noexcept(m_Alloc.deallocate(p, n))) { m_Alloc.deallocate(p, sizeof(value_type) * n); } @@ -96,6 +105,7 @@ namespace ktl template typename std::enable_if, void>::type construct(value_type* p, Args&&... args) + noexcept(detail::has_nothrow_construct_v) { m_Alloc.construct(p, std::forward(args)...); } @@ -108,6 +118,7 @@ namespace ktl template typename std::enable_if, void>::type destroy(value_type* p) + noexcept(detail::has_nothrow_destroy_v) { m_Alloc.destroy(p); } @@ -121,7 +132,8 @@ namespace ktl */ template typename std::enable_if, size_type>::type - max_size() const noexcept + max_size() const + noexcept(detail::has_nothrow_max_size_v) { return m_Alloc.max_size() / sizeof(T); } @@ -135,6 +147,7 @@ namespace ktl template typename std::enable_if, bool>::type owns(value_type* p) const + noexcept(detail::has_nothrow_owns_v) { return m_Alloc.owns(p); } @@ -144,7 +157,7 @@ namespace ktl * @brief Returns a reference to the underlying allocator * @return The allocator */ - Alloc& get_allocator() + Alloc& get_allocator() noexcept { return m_Alloc; } @@ -153,7 +166,7 @@ namespace ktl * @brief Returns a const reference to the underlying allocator * @return The allocator */ - const Alloc& get_allocator() const + const Alloc& get_allocator() const noexcept { return m_Alloc; } @@ -163,13 +176,15 @@ namespace ktl }; template - bool operator==(const type_allocator& lhs, const type_allocator& rhs) noexcept + bool operator==(const type_allocator& lhs, const type_allocator& rhs) + noexcept(detail::has_nothrow_equal_v) { return lhs.get_allocator() == rhs.get_allocator(); } template - bool operator!=(const type_allocator& lhs, const type_allocator& rhs) noexcept + bool operator!=(const type_allocator& lhs, const type_allocator& rhs) + noexcept(detail::has_nothrow_not_equal_v) { return lhs.get_allocator() != rhs.get_allocator(); } diff --git a/include/ktl/utility/aligned_malloc.h b/include/ktl/utility/aligned_malloc.h index c40b691..694f1df 100644 --- a/include/ktl/utility/aligned_malloc.h +++ b/include/ktl/utility/aligned_malloc.h @@ -45,7 +45,7 @@ namespace ktl::detail { - inline void* aligned_malloc(size_t size, size_t alignment) + inline void* aligned_malloc(size_t size, size_t alignment) noexcept { #if KTL_HAS_MALLOC_ALIGNED return malloc(size); @@ -70,7 +70,7 @@ namespace ktl::detail #endif } - inline void aligned_free(void* ptr) + inline void aligned_free(void* ptr) noexcept { #if KTL_HAS_MALLOC_ALIGNED free(ptr); @@ -88,6 +88,7 @@ namespace ktl::detail template T* aligned_new(size_t alignment, Args&&... args) + noexcept(noexcept(T(std::declval()...))) { T* p = static_cast(aligned_malloc(sizeof(T), alignment)); ::new(p) T(std::forward(args)...); @@ -96,6 +97,7 @@ namespace ktl::detail template void aligned_delete(T* p) + noexcept(noexcept(p->~T())) { p->~T(); aligned_free(p); diff --git a/include/ktl/utility/assert.h b/include/ktl/utility/assert.h index 4a58569..1462cd2 100644 --- a/include/ktl/utility/assert.h +++ b/include/ktl/utility/assert.h @@ -8,7 +8,7 @@ #include #define KTL_BREAKPOINT() std::raise(SIGTRAP) #else // Non-supported -#define KTL_BREAKPOINT() throw +#define KTL_BREAKPOINT() #endif #ifdef KTL_DEBUG_ASSERT diff --git a/include/ktl/utility/bits.h b/include/ktl/utility/bits.h index 0fd6d26..5298563 100644 --- a/include/ktl/utility/bits.h +++ b/include/ktl/utility/bits.h @@ -4,7 +4,7 @@ namespace ktl::detail { - constexpr inline uintmax_t log2(uintmax_t n) + constexpr inline uintmax_t log2(uintmax_t n) noexcept { uintmax_t r = 0; diff --git a/include/ktl/utility/meta.h b/include/ktl/utility/meta.h index f22893e..3374fe8 100644 --- a/include/ktl/utility/meta.h +++ b/include/ktl/utility/meta.h @@ -30,16 +30,6 @@ namespace ktl::detail template using get_size_type_t = typename get_size_type::type; - // has max_size() - template - struct has_max_size : std::false_type {}; - - template - struct has_max_size().max_size())>> : std::true_type {}; - - template - constexpr bool has_max_size_v = has_max_size::value; - // has construct(T*, Args&&...) template struct has_construct : std::false_type {}; @@ -60,6 +50,16 @@ namespace ktl::detail template constexpr bool has_destroy_v = has_destroy::value; + // has max_size() + template + struct has_max_size : std::false_type {}; + + template + struct has_max_size().max_size())>> : std::true_type {}; + + template + constexpr bool has_max_size_v = has_max_size::value; + // has owns(void*) template struct has_owns : std::false_type {}; @@ -70,13 +70,65 @@ namespace ktl::detail template constexpr bool has_owns_v = has_owns::value; - // has Alloc(Args...) + + + // has allocate(size_t) noexcept + template + constexpr bool has_nothrow_allocate_v = noexcept(std::declval().allocate(std::declval())); + + // has deallocate(void*, size_t) noexcept + template + constexpr bool has_nothrow_deallocate_v = noexcept(std::declval().deallocate(std::declval(), std::declval())); + + // has T& == T& noexcept + template + constexpr bool has_nothrow_equal_v = noexcept(std::declval() == std::declval()); + + // has T& == T& noexcept + template + constexpr bool has_nothrow_not_equal_v = noexcept(std::declval() == std::declval()); + + // has construct(T*, Args&&...) noexcept template - struct can_construct : std::false_type {}; + struct has_nothrow_construct : std::false_type {}; template - struct can_construct()...))>, Alloc, Args...> : std::true_type {}; + struct has_nothrow_construct>, Alloc, Args...> + : std::bool_constant().construct(std::declval()...))> {}; template - constexpr bool can_construct_v = can_construct::value; + constexpr bool has_nothrow_construct_v = has_nothrow_construct::value; + + // has destroy(T*) noexcept + template + struct has_nothrow_destroy : std::false_type {}; + + template + struct has_nothrow_destroy>> + : std::bool_constant().destroy(std::declval()))> {}; + + template + constexpr bool has_nothrow_destroy_v = has_nothrow_destroy::value; + + // has max_size() noexcept + template + struct has_nothrow_max_size : std::false_type {}; + + template + struct has_nothrow_max_size>> + : std::bool_constant().max_size())> {}; + + template + constexpr bool has_nothrow_max_size_v = has_nothrow_max_size::value; + + // has owns(void*) noexcept + template + struct has_nothrow_owns : std::false_type {}; + + template + struct has_nothrow_owns>> + : std::bool_constant().owns(std::declval()))> {}; + + template + constexpr bool has_nothrow_owns_v = has_nothrow_owns::value; } \ No newline at end of file