diff --git a/src/small_unique_ptr.hpp b/src/small_unique_ptr.hpp index cb6eb9f..28292eb 100644 --- a/src/small_unique_ptr.hpp +++ b/src/small_unique_ptr.hpp @@ -1,10 +1,12 @@ /* Copyright (c) 2024 Krisztián Rugási. Subject to the MIT License. */ -#ifndef SMALL_UNIQUE_PTR_SMALL_UNIQUE_PTR_HPP -#define SMALL_UNIQUE_PTR_SMALL_UNIQUE_PTR_HPP +#ifndef SMALL_UNIQUE_PTR_HPP +#define SMALL_UNIQUE_PTR_HPP #include #include +#include +#include #include #include #include @@ -288,6 +290,18 @@ class small_unique_ptr : private detail::small_unique_ptr_base } } + [[nodiscard]] + constexpr static bool is_always_heap_allocated() noexcept + { + return detail::always_heap_allocated_v; + } + + [[nodiscard]] + constexpr bool is_stack_allocated() const noexcept + { + return small_unique_ptr_base::is_stack_allocated(); + } + [[nodiscard]] constexpr pointer get() const noexcept { @@ -336,6 +350,12 @@ class small_unique_ptr : private detail::small_unique_ptr_base return this->data_ <=> rhs.data_; } + template + friend std::basic_ostream& operator<<(std::basic_ostream& os, const small_unique_ptr& p) + { + return os << p.get(); + } + private: template constexpr std::ptrdiff_t offsetof_base() const noexcept @@ -362,6 +382,19 @@ constexpr void swap(small_unique_ptr& lhs, small_unique_ptr& rhs) noexcept lhs.swap(rhs); } +namespace std +{ + template + struct hash> + { + std::size_t operator()(const small_unique_ptr& p) const noexcept + { + return std::hash::pointer>{}(p.get()); + } + }; + +} // namespace std + template [[nodiscard]] constexpr small_unique_ptr make_unique_small(Args&&... args) noexcept(!detail::always_heap_allocated_v && std::is_nothrow_constructible_v) @@ -385,4 +418,4 @@ noexcept(!detail::always_heap_allocated_v && std::is_nothrow_constructible_v< return ptr; } -#endif // !SMALL_UNIQUE_PTR_SMALL_UNIQUE_PTR_HPP \ No newline at end of file +#endif // !SMALL_UNIQUE_PTR_HPP \ No newline at end of file diff --git a/test/small_unique_ptr.cpp b/test/small_unique_ptr.cpp index 939674b..100004d 100644 --- a/test/small_unique_ptr.cpp +++ b/test/small_unique_ptr.cpp @@ -71,6 +71,36 @@ TEST_CASE("construction", "[small_unique_ptr]") REQUIRE_NOTHROW(make_unique_small()); } +TEST_CASE("is_always_heap_allocated", "[small_unique_ptr]") +{ + STATIC_REQUIRE(!small_unique_ptr::is_always_heap_allocated()); + STATIC_REQUIRE(small_unique_ptr::is_always_heap_allocated()); + + STATIC_REQUIRE(!small_unique_ptr::is_always_heap_allocated()); + STATIC_REQUIRE(small_unique_ptr::is_always_heap_allocated()); +} + +TEST_CASE("is_stack_allocated", "[small_unique_ptr]") +{ + STATIC_REQUIRE( !std::invoke([]{ return make_unique_small().is_stack_allocated(); }) ); + STATIC_REQUIRE( !std::invoke([]{ return make_unique_small().is_stack_allocated(); }) ); + + STATIC_REQUIRE( !std::invoke([]{ return make_unique_small().is_stack_allocated(); }) ); + STATIC_REQUIRE( !std::invoke([]{ return make_unique_small().is_stack_allocated(); }) ); + + small_unique_ptr p1 = make_unique_small(); + small_unique_ptr p2 = make_unique_small(); + + REQUIRE(p1.is_stack_allocated()); + REQUIRE(!p2.is_stack_allocated()); + + small_unique_ptr p3 = make_unique_small(); + small_unique_ptr p4 = make_unique_small(); + + REQUIRE(p3.is_stack_allocated()); + REQUIRE(!p4.is_stack_allocated()); +} + TEST_CASE("comparisons", "[small_unique_ptr]") { STATIC_REQUIRE(small_unique_ptr(nullptr) == nullptr); @@ -423,7 +453,6 @@ TEST_CASE("poly_alignment", "[small_unique_ptr]") TEST_CASE("const_unique_ptr", "[small_unique_ptr]") { const small_unique_ptr p = make_unique_small(3); - *p = 2; REQUIRE(*p == 2);