diff --git a/README.md b/README.md index 48510ac..73fb0fd 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ header only core - `hash_aggregate`: hashes aggregates member wise ### pointers -- `not_null`, `move_ptr` +- `not_null`, `not_null_ptr`, `unique_not_null`, `move_ptr` ### io (`cth::io`) - colored console output: `col_stream` diff --git a/cth/incl/cth/ptr/not_null.hpp b/cth/incl/cth/ptr/not_null.hpp index 50c3eb7..684e518 100644 --- a/cth/incl/cth/ptr/not_null.hpp +++ b/cth/incl/cth/ptr/not_null.hpp @@ -40,6 +40,11 @@ class not_null { static constexpr bool NOEXCEPT_CHECK_VAL = COMPILATION_MODE != CompilationMode::DEBUG; static constexpr bool RELEASE_VAL_NOEXCEPT = NOEXCEPT_ASSIGN && NOEXCEPT_MOVE_CTOR; + static consteval bool release_noexcept() { + if constexpr(ADAPT_RELEASE) return noexcept(std::declval().release()); + else return RELEASE_VAL_NOEXCEPT; + } + static constexpr void checkVal(T const& obj) noexcept(NOEXCEPT_CHECK_VAL) { CTH_CRITICAL(obj == Null, "obj must not be Null") {} } @@ -58,13 +63,9 @@ class not_null { _obj = Null; return obj; } - [[nodiscard]] constexpr auto release() noexcept( - (!ADAPT_RELEASE && RELEASE_VAL_NOEXCEPT) || (ADAPT_RELEASE && noexcept(std::declval().release())) - ) { - if constexpr(!ADAPT_RELEASE) - return release_val(); - - return _obj.release(); + [[nodiscard]] constexpr auto release() noexcept(release_noexcept()) { + if constexpr(ADAPT_RELEASE) return _obj.release(); + else return release_val(); } @@ -111,12 +112,14 @@ class not_null { }; -template -constexpr void swap(not_null& left, not_null& right) noexcept { left.swap(right); } +template +constexpr void swap(not_null& left, not_null& right) noexcept { left.swap(right); } -template -[[nodiscard]] auto make_not_null(T&& t) noexcept { - return not_null>, Null, Adapt>{std::forward(t)}; +template +[[nodiscard]] constexpr auto make_not_null(T&& t) noexcept( + std::is_nothrow_constructible_v, Null, Adapt>, T&&> +) { + return not_null, Null, Adapt>{std::forward(t)}; } template @@ -189,8 +192,8 @@ template struct std::hash< cth::not_null> /* NOLINT(cert-dcl58-cpp) this is a valid overload for std::hash */ { std::size_t operator()(cth::not_null const& obj) const noexcept { - using ptr_t = class cth::not_null::pointer; - return std::hash{}(obj.get()); + using value_t = std::remove_cvref_t; + return std::hash{}(obj.get()); } }; @@ -198,6 +201,9 @@ struct std::hash< namespace cth { +template +using not_null_ptr = cth::not_null; + template using unique_not_null = cth::not_null, nullptr, true>; diff --git a/cth/tests/src/pointers/not_null_test.cpp b/cth/tests/src/pointers/not_null_test.cpp index 2fd432d..c983ae9 100644 --- a/cth/tests/src/pointers/not_null_test.cpp +++ b/cth/tests/src/pointers/not_null_test.cpp @@ -4,6 +4,9 @@ #include "cth/ptr/not_null.hpp" +#include +#include + namespace cth { int constexpr EXAMPLE_NUMBER = 42; @@ -65,6 +68,16 @@ CTH_TEST(not_null, swap) { ASSERT_EQ(ptr2.get(), &data1); } +CTH_TEST(not_null, swap_with_custom_null) { + auto first = make_not_null<-1>(1); + auto second = make_not_null<-1>(2); + + swap(first, second); + + ASSERT_EQ(first.get(), 2); + ASSERT_EQ(second.get(), 1); +} + CTH_TEST(not_null, get) { not_null const ptr(data.get()); ASSERT_EQ(ptr.get(), data.get()); @@ -83,6 +96,44 @@ CTH_TEST(not_null, operator_deref) { } +CTH_TEST(not_null, hash) { + int a = EXAMPLE_NUMBER; + not_null const ptr{&a}; + + EXPECT_EQ(std::hash>{}(ptr), std::hash{}(&a)); +} + +CTH_TEST(not_null, release) { + int a = EXAMPLE_NUMBER; + not_null ptr{&a}; + + EXPECT_EQ(ptr.release(), &a); +} + +CTH_TEST(not_null, make_not_null) { + int a = EXAMPLE_NUMBER; + + auto const ptr = make_not_null(&a); + static_assert(std::same_as const>); + EXPECT_EQ(ptr.get(), &a); + + auto const value = make_not_null<-1>(EXAMPLE_NUMBER); + static_assert(std::same_as const>); + EXPECT_EQ(value.get(), EXAMPLE_NUMBER); +} + + +CTH_TEST(not_null_ptr, const_conversion) { + static_assert(std::same_as, not_null>); + + int a = EXAMPLE_NUMBER; + not_null_ptr const ptr{&a}; + not_null_ptr const constPtr = ptr; + + EXPECT_EQ(constPtr.get(), &a); +} + + CTH_TEST(unique_not_null, comparison) { unique_not_null a{std::make_unique(EXAMPLE_NUMBER)}; @@ -109,4 +160,20 @@ CTH_TEST(unique_not_null, comparison) { EXPECT_FALSE(px >= py); } + +CTH_TEST(unique_not_null, hash) { + unique_not_null const ptr{std::make_unique(EXAMPLE_NUMBER)}; + + EXPECT_EQ(std::hash>{}(ptr), std::hash{}(ptr.get())); +} + +CTH_TEST(unique_not_null, release) { + unique_not_null ptr{std::make_unique(EXAMPLE_NUMBER)}; + auto const* raw = ptr.get(); + + std::unique_ptr const owner{ptr.release()}; + + EXPECT_EQ(owner.get(), raw); +} + }