Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
34 changes: 20 additions & 14 deletions cth/incl/cth/ptr/not_null.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<T&>().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") {}
}
Expand All @@ -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<T>().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();
}


Expand Down Expand Up @@ -111,12 +112,14 @@ class not_null {
};


template<class T>
constexpr void swap(not_null<T>& left, not_null<T>& right) noexcept { left.swap(right); }
template<class T, auto Null, bool Adapt>
constexpr void swap(not_null<T, Null, Adapt>& left, not_null<T, Null, Adapt>& right) noexcept { left.swap(right); }

template<class T, auto Null = nullptr, bool Adapt>
[[nodiscard]] auto make_not_null(T&& t) noexcept {
return not_null<std::remove_cv_t<std::remove_reference_t<T>>, Null, Adapt>{std::forward<T>(t)};
template<auto Null = nullptr, bool Adapt = false, class T>
[[nodiscard]] constexpr auto make_not_null(T&& t) noexcept(
std::is_nothrow_constructible_v<not_null<std::remove_cvref_t<T>, Null, Adapt>, T&&>
) {
return not_null<std::remove_cvref_t<T>, Null, Adapt>{std::forward<T>(t)};
}

template<class T, class U, auto Null, bool Adapt>
Expand Down Expand Up @@ -189,15 +192,18 @@ template<class T, auto Null, bool Adapt>
struct std::hash<
cth::not_null<T, Null, Adapt>> /* NOLINT(cert-dcl58-cpp) this is a valid overload for std::hash */ {
std::size_t operator()(cth::not_null<T, Null, Adapt> const& obj) const noexcept {
using ptr_t = class cth::not_null<T, Null, Adapt>::pointer;
return std::hash<ptr_t>{}(obj.get());
using value_t = std::remove_cvref_t<decltype(obj.get())>;
return std::hash<value_t>{}(obj.get());
}
};


namespace cth {


template<class T>
using not_null_ptr = cth::not_null<T*>;

template<class T>
using unique_not_null = cth::not_null<std::unique_ptr<T>, nullptr, true>;

Expand Down
67 changes: 67 additions & 0 deletions cth/tests/src/pointers/not_null_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

#include "cth/ptr/not_null.hpp"

#include <concepts>
#include <functional>


namespace cth {
int constexpr EXAMPLE_NUMBER = 42;
Expand Down Expand Up @@ -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());
Expand All @@ -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<not_null<int*>>{}(ptr), std::hash<int*>{}(&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<decltype(ptr), not_null<int*> const>);
EXPECT_EQ(ptr.get(), &a);

auto const value = make_not_null<-1>(EXAMPLE_NUMBER);
static_assert(std::same_as<decltype(value), not_null<int, -1> const>);
EXPECT_EQ(value.get(), EXAMPLE_NUMBER);
}


CTH_TEST(not_null_ptr, const_conversion) {
static_assert(std::same_as<not_null_ptr<int const>, not_null<int const*>>);

int a = EXAMPLE_NUMBER;
not_null_ptr<int> const ptr{&a};
not_null_ptr<int const> const constPtr = ptr;

EXPECT_EQ(constPtr.get(), &a);
}


CTH_TEST(unique_not_null, comparison) {

unique_not_null a{std::make_unique<int>(EXAMPLE_NUMBER)};
Expand All @@ -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<int>(EXAMPLE_NUMBER)};

EXPECT_EQ(std::hash<unique_not_null<int>>{}(ptr), std::hash<int*>{}(ptr.get()));
}

CTH_TEST(unique_not_null, release) {
unique_not_null ptr{std::make_unique<int>(EXAMPLE_NUMBER)};
auto const* raw = ptr.get();

std::unique_ptr<int> const owner{ptr.release()};

EXPECT_EQ(owner.get(), raw);
}

}
Loading