aboutsummaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/kstd/CMakeLists.txt1
-rw-r--r--libs/kstd/include/kstd/bits/flat_map.hpp2
-rw-r--r--libs/kstd/include/kstd/bits/observer_ptr.hpp163
-rw-r--r--libs/kstd/include/kstd/bits/unique_ptr.hpp25
-rw-r--r--libs/kstd/include/kstd/memory9
-rw-r--r--libs/kstd/include/kstd/string13
-rw-r--r--libs/kstd/tests/include/kstd/tests/test_types.hpp71
-rw-r--r--libs/kstd/tests/src/observer_ptr.cpp359
-rw-r--r--libs/kstd/tests/src/vector.cpp119
9 files changed, 684 insertions, 78 deletions
diff --git a/libs/kstd/CMakeLists.txt b/libs/kstd/CMakeLists.txt
index ec0f441..240118e 100644
--- a/libs/kstd/CMakeLists.txt
+++ b/libs/kstd/CMakeLists.txt
@@ -44,6 +44,7 @@ else()
add_executable("kstd_tests"
"tests/src/flat_map.cpp"
"tests/src/vector.cpp"
+ "tests/src/observer_ptr.cpp"
"tests/src/os_panic.cpp"
"tests/src/string.cpp"
)
diff --git a/libs/kstd/include/kstd/bits/flat_map.hpp b/libs/kstd/include/kstd/bits/flat_map.hpp
index 9455549..fe46203 100644
--- a/libs/kstd/include/kstd/bits/flat_map.hpp
+++ b/libs/kstd/include/kstd/bits/flat_map.hpp
@@ -45,7 +45,7 @@ namespace kstd::bits
template<std::size_t Index>
requires(Index >= 0 && Index <= 1)
- constexpr auto get() const noexcept -> decltype(auto)
+ [[nodiscard]] constexpr auto get() const noexcept -> decltype(auto)
{
if constexpr (Index == 0)
{
diff --git a/libs/kstd/include/kstd/bits/observer_ptr.hpp b/libs/kstd/include/kstd/bits/observer_ptr.hpp
new file mode 100644
index 0000000..1c5da15
--- /dev/null
+++ b/libs/kstd/include/kstd/bits/observer_ptr.hpp
@@ -0,0 +1,163 @@
+#ifndef KSTD_OBSERVER_PTR_HPP
+#define KSTD_OBSERVER_PTR_HPP
+
+// IWYU pragma: private, include <kstd/memory>
+
+#include "kstd/os/error.hpp"
+
+#include <compare>
+#include <concepts>
+#include <cstddef>
+#include <type_traits>
+#include <utility>
+
+namespace kstd
+{
+
+ template<typename ElementType>
+ struct observer_ptr
+ {
+ //! The type of the element being pointed to.
+ using element_type = ElementType;
+
+ //! Construct an empty observer pointer.
+ constexpr observer_ptr() noexcept = default;
+
+ //! Construct an empty observer pointer from a null pointer.
+ constexpr observer_ptr(std::nullptr_t) noexcept {}
+
+ //! Construct an observer pointer from a raw pointer.
+ constexpr explicit observer_ptr(element_type * pointer)
+ : m_ptr{pointer}
+ {}
+
+ //! Construct an observer pointer from another observer pointer.
+ template<typename OtherElementType>
+ requires std::convertible_to<OtherElementType *, ElementType *>
+ constexpr observer_ptr(observer_ptr<OtherElementType> other) noexcept
+ : m_ptr{other.get()}
+ {}
+
+ //! Copy construct an observer pointer.
+ constexpr observer_ptr(observer_ptr const & other) noexcept = default;
+
+ //! Move construct an observer pointer.
+ constexpr observer_ptr(observer_ptr && other) noexcept = default;
+
+ //! Copy assign an observer pointer.
+ constexpr auto operator=(observer_ptr const & other) noexcept -> observer_ptr & = default;
+
+ //! Move assign an observer pointer.
+ constexpr auto operator=(observer_ptr && other) noexcept -> observer_ptr & = default;
+
+ //! Stop watching the the watched object.
+ //!
+ //! @return The currently watched object, or nullptr if no object is being watched.
+ [[nodiscard]] constexpr auto release() noexcept -> element_type *
+ {
+ return std::exchange(m_ptr, nullptr);
+ }
+
+ //! Reset the observer pointer.
+ //!
+ //! @param pointer The new object to watch.
+ constexpr auto reset(element_type * pointer = nullptr) noexcept -> void
+ {
+ m_ptr = pointer;
+ }
+
+ //! Swap the observer pointer with another observer pointer.
+ //!
+ //! @param other The other observer pointer to swap with.
+ constexpr auto swap(observer_ptr & other) noexcept -> void
+ {
+ std::swap(m_ptr, other.m_ptr);
+ }
+
+ //! Get the currently watched object.
+ //!
+ //! @return The currently watched object, or nullptr if no object is being watched.
+ [[nodiscard]] constexpr auto get() const noexcept -> element_type *
+ {
+ return m_ptr;
+ }
+
+ //! Check if the observer pointer is watching an object.
+ //!
+ //! @return True if the observer pointer is watching an object, false otherwise.
+ [[nodiscard]] constexpr explicit operator bool() const noexcept
+ {
+ return m_ptr != nullptr;
+ }
+
+ //! Get the currently watched object.
+ //!
+ //! @return A reference to the currently watched object.
+ [[nodiscard]] constexpr auto operator*() const -> std::add_lvalue_reference_t<element_type>
+ {
+ throw_on_null();
+ return *m_ptr;
+ }
+
+ //! Get the currently watched object.
+ //!
+ //! @return A pointer to the currently watched object.
+ [[nodiscard]] constexpr auto operator->() const -> element_type *
+ {
+ throw_on_null();
+ return m_ptr;
+ }
+
+ //! Convert the observer pointer to a raw pointer.
+ //!
+ //! @return A pointer to the currently watched object.
+ constexpr explicit operator element_type *() const noexcept
+ {
+ return m_ptr;
+ }
+
+ //! Compare the observer pointer with another observer pointer.
+ //!>
+ //! @param other The other observer pointer to compare with.
+ //! @return The result of the comparison.
+ constexpr auto operator<=>(observer_ptr const & other) const noexcept -> std::strong_ordering = default;
+
+ private:
+ //! Throw an exception if the observer pointer is null.
+ //!
+ //! @throws std::runtime_error if the observer pointer is null.
+ constexpr auto throw_on_null() const -> void
+ {
+ if (m_ptr == nullptr)
+ {
+ os::panic("[kstd:observer_ptr] Dereferencing a null observer pointer");
+ }
+ }
+
+ //! The raw pointer to the watched object.
+ ElementType * m_ptr{};
+ };
+
+ //! Swap two observer pointers.
+ //!
+ //! @param lhs The first observer pointer to swap.
+ //! @param rhs The second observer pointer to swap.
+ template<typename ElementType>
+ constexpr auto swap(observer_ptr<ElementType> & lhs, observer_ptr<ElementType> & rhs) noexcept -> void
+ {
+ lhs.swap(rhs);
+ }
+
+ //! Create an observer pointer from a raw pointer.
+ //!
+ //! @param pointer The raw pointer to create an observer pointer from.
+ //! @return An observer pointer to the given raw pointer.
+ template<typename ElementType>
+ constexpr auto make_observer(ElementType * pointer) noexcept -> observer_ptr<ElementType>
+ {
+ return observer_ptr<ElementType>{pointer};
+ }
+
+} // namespace kstd
+
+#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/bits/unique_ptr.hpp b/libs/kstd/include/kstd/bits/unique_ptr.hpp
index e0870b1..3d803b4 100644
--- a/libs/kstd/include/kstd/bits/unique_ptr.hpp
+++ b/libs/kstd/include/kstd/bits/unique_ptr.hpp
@@ -16,6 +16,9 @@ namespace kstd
template<typename T>
struct unique_ptr
{
+ template<typename U>
+ friend struct unique_ptr;
+
/**
* @brief Constructor.
*
@@ -40,6 +43,12 @@ namespace kstd
*/
unique_ptr(unique_ptr const &) = delete;
+ template<typename U>
+ requires(std::is_convertible_v<U *, T *>)
+ unique_ptr(unique_ptr<U> && other) noexcept
+ : pointer{std::exchange(other.pointer, nullptr)}
+ {}
+
/**
* @brief Deleted copy assignment operator to enforce unique ownership.
*/
@@ -51,10 +60,8 @@ namespace kstd
* @param other Unique pointer to move from.
*/
unique_ptr(unique_ptr && other) noexcept
- : pointer(other.pointer)
- {
- other.pointer = nullptr;
- }
+ : pointer{std::exchange(other.pointer, nullptr)}
+ {}
/**
* @brief Move assignment operator. Transfers ownership from other to *this as if by calling reset(r.release()).
@@ -67,8 +74,7 @@ namespace kstd
if (this != &other)
{
delete pointer;
- pointer = other.pointer;
- other.pointer = nullptr;
+ pointer = std::exchange(other.pointer, nullptr);
}
return *this;
}
@@ -123,9 +129,7 @@ namespace kstd
*/
auto release() -> T *
{
- T * temp = pointer;
- pointer = nullptr;
- return temp;
+ return std::exchange(pointer, nullptr);
}
/**
@@ -139,8 +143,7 @@ namespace kstd
*/
auto reset(T * ptr = nullptr) -> void
{
- delete pointer;
- pointer = ptr;
+ delete std::exchange(pointer, ptr);
}
/**
diff --git a/libs/kstd/include/kstd/memory b/libs/kstd/include/kstd/memory
index cab2fba..493f49a 100644
--- a/libs/kstd/include/kstd/memory
+++ b/libs/kstd/include/kstd/memory
@@ -1,7 +1,8 @@
-#ifndef KSTD_SHARED_POINTER_HPP
-#define KSTD_SHARED_POINTER_HPP
+#ifndef KSTD_MEMORY_HPP
+#define KSTD_MEMORY_HPP
-#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export
-#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export
+#include "kstd/bits/observer_ptr.hpp" // IWYU pragma: export
+#include "kstd/bits/shared_ptr.hpp" // IWYU pragma: export
+#include "kstd/bits/unique_ptr.hpp" // IWYU pragma: export
#endif \ No newline at end of file
diff --git a/libs/kstd/include/kstd/string b/libs/kstd/include/kstd/string
index 075422e..4ce19ce 100644
--- a/libs/kstd/include/kstd/string
+++ b/libs/kstd/include/kstd/string
@@ -1,6 +1,10 @@
#ifndef KSTD_STRING_HPP
#define KSTD_STRING_HPP
+#include "kstd/bits/format/context.hpp"
+#include "kstd/bits/format/formatter.hpp"
+#include "kstd/bits/format/formatter/string_view.hpp"
+
#include <kstd/cstring>
#include <kstd/os/error.hpp>
#include <kstd/vector>
@@ -343,6 +347,15 @@ namespace kstd
return !(lhs == rhs);
}
+ template<>
+ struct formatter<string> : formatter<std::string_view>
+ {
+ auto format(string const & str, format_context & context) const -> void
+ {
+ formatter<std::string_view>::format(str.view(), context);
+ }
+ };
+
} // namespace kstd
#endif \ No newline at end of file
diff --git a/libs/kstd/tests/include/kstd/tests/test_types.hpp b/libs/kstd/tests/include/kstd/tests/test_types.hpp
index 6a06311..9207ee9 100644
--- a/libs/kstd/tests/include/kstd/tests/test_types.hpp
+++ b/libs/kstd/tests/include/kstd/tests/test_types.hpp
@@ -11,44 +11,43 @@ namespace kstd::tests
//! A type tracking copy and move operations
//!
//! This type is designed to test move and copy semantics of standard library containers implemented in kstd.
- struct move_tracker
+ struct special_member_tracker
{
//! A value indicating that the object was moved from.
constexpr auto static moved_from_v = -1;
- //! A simple value to be able to track the move-from state.
- int value{};
- //! A flag to track if an instance of this type was either copy constructed or copy assigned.
- bool was_copied{false};
- //! A flag to track if an instance of this type was either move constructed or move assigned.
- bool was_moved{false};
+ constexpr special_member_tracker()
+ : default_constructed_count{1}
+ {}
//! Construct a new move tracker with the given value, if any.
- constexpr move_tracker(int v = 0)
+ constexpr special_member_tracker(int v = 0)
: value{v}
+ , value_constructed_count{1}
{}
//! Construct a new move tracker by copying an existing one.
- constexpr move_tracker(move_tracker const & other)
+ constexpr special_member_tracker(special_member_tracker const & other)
: value{other.value}
- , was_copied{true}
+ , copy_constructed_count{1}
{}
//! Construct a new move tracker by moving from an existing one.
- constexpr move_tracker(move_tracker && other) noexcept
+ constexpr special_member_tracker(special_member_tracker && other) noexcept
: value{other.value}
- , was_moved{true}
+ , move_constructed_count{1}
{
other.value = moved_from_v;
}
//! Copy assign a new move tracker from an existing one.
- constexpr auto operator=(move_tracker const & other) -> move_tracker &
+ constexpr auto operator=(special_member_tracker const & other) -> special_member_tracker &
{
if (this != &other)
{
value = other.value;
- was_copied = true;
+ ++copy_assigned_count;
+ ++other.copied_from_count;
}
return *this;
}
@@ -56,16 +55,56 @@ namespace kstd::tests
//! Move assign a new move tracker from an existing one.
//!
//! This function ensures that the moved-from state is marked.
- constexpr auto operator=(move_tracker && other) noexcept -> move_tracker &
+ constexpr auto operator=(special_member_tracker && other) noexcept -> special_member_tracker &
{
if (this != &other)
{
value = other.value;
- was_moved = true;
+ ++move_assigned_count;
other.value = moved_from_v;
+ ++other.moved_from_count;
}
return *this;
}
+
+ ~special_member_tracker()
+ {
+ ++destroyed_count;
+ }
+
+ auto reset_counts() -> void
+ {
+ default_constructed_count = 0;
+ copy_constructed_count = 0;
+ move_constructed_count = 0;
+ value_constructed_count = 0;
+ copy_assigned_count = 0;
+ move_assigned_count = 0;
+ destroyed_count = 0;
+ copied_from_count = 0;
+ moved_from_count = 0;
+ }
+
+ //! A simple value to be able to track the move-from state.
+ int value{};
+ //! A counter to track how many times an instance of this type was default constructed.
+ std::size_t default_constructed_count{0};
+ //! A counter to track how many times an instance of this type was copy constructed.
+ std::size_t copy_constructed_count{0};
+ //! A counter to track how many times an instance of this type was move constructed.
+ std::size_t move_constructed_count{0};
+ //! A counter to track how many times an instance of this type was value constructed.
+ std::size_t value_constructed_count{0};
+ //! A counter to track how many times an instance of this type was copy assigned.
+ std::size_t copy_assigned_count{0};
+ //! A counter to track how many times an instance of this type was move assigned.
+ std::size_t move_assigned_count{0};
+ //! A counter to track how many times an instance of this type was destroyed.
+ std::size_t destroyed_count{0};
+ //! A counter to track how many times an instance of this type was copied from another instance.
+ mutable std::size_t copied_from_count{0};
+ //! A counter to track how many times an instance of this type was moved from another instance.
+ std::size_t moved_from_count{0};
};
//! A type that is not default constructible.
diff --git a/libs/kstd/tests/src/observer_ptr.cpp b/libs/kstd/tests/src/observer_ptr.cpp
new file mode 100644
index 0000000..006ebde
--- /dev/null
+++ b/libs/kstd/tests/src/observer_ptr.cpp
@@ -0,0 +1,359 @@
+#include <kstd/memory>
+#include <kstd/tests/os_panic.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+
+#include <compare>
+#include <type_traits>
+#include <utility>
+
+namespace
+{
+ struct Base
+ {
+ };
+
+ struct Derived : Base
+ {
+ };
+
+ struct Element
+ {
+ int value{};
+
+ constexpr auto operator<=>(Element const &) const noexcept = default;
+ };
+} // namespace
+
+SCENARIO("Observer Pointer initialization and construction", "[observer_ptr]")
+{
+ GIVEN("An empty context")
+ {
+ WHEN("constructing by default")
+ {
+ auto ptr = kstd::observer_ptr<int>{};
+
+ THEN("the observer pointer is null")
+ {
+ REQUIRE_FALSE(ptr);
+ }
+ }
+
+ WHEN("constructing from a nullptr")
+ {
+ auto ptr = kstd::observer_ptr<int>{nullptr};
+
+ THEN("the observer pointer is null")
+ {
+ REQUIRE_FALSE(ptr);
+ }
+ }
+
+ WHEN("constructing from a raw pointer")
+ {
+ auto value = 1;
+ auto ptr = kstd::observer_ptr{&value};
+
+ THEN("the observer pointer is not null")
+ {
+ REQUIRE(ptr);
+ }
+
+ THEN("the observer pointer points to the correct object")
+ {
+ REQUIRE(&*ptr == &value);
+ }
+ }
+
+ WHEN("copy constructing from an existing observer pointer")
+ {
+ auto value = 1;
+ auto ptr = kstd::observer_ptr{&value};
+ auto copy = ptr;
+
+ THEN("the new observer pointer points to the same object as the other observer pointer")
+ {
+ REQUIRE(&*copy == &value);
+ }
+ }
+
+ WHEN("copy constructing from an existing observer pointer with a compatible type")
+ {
+ auto value = Derived{};
+ auto ptr = kstd::observer_ptr<Derived>(&value);
+ kstd::observer_ptr<Base> copy = ptr;
+
+ THEN("the new observer pointer points to the same object as the other observer pointer")
+ {
+ REQUIRE(&*copy == &value);
+ }
+ }
+
+ WHEN("copy assigning from an existing observer pointer")
+ {
+ auto value = 1;
+ auto ptr = kstd::observer_ptr{&value};
+ auto copy = ptr;
+
+ THEN("the new observer pointer points to the same object as the other observer pointer")
+ {
+ REQUIRE(&*copy == &value);
+ }
+ }
+
+ WHEN("move constructing from an existing observer pointer")
+ {
+ auto value = 1;
+ auto ptr = kstd::observer_ptr{&value};
+ auto copy = std::move(ptr);
+
+ THEN("the new observer pointer points to the same object as the other observer pointer")
+ {
+ REQUIRE(&*copy == &value);
+ }
+ }
+
+ WHEN("move assigning from an existing observer pointer")
+ {
+ auto value = 1;
+ auto ptr = kstd::observer_ptr{&value};
+ auto copy = std::move(ptr);
+
+ THEN("the new observer pointer points to the same object as the other observer pointer")
+ {
+ REQUIRE(&*copy == &value);
+ }
+ }
+
+ WHEN("constructing an observer pointer using make_observer")
+ {
+ auto value = 1;
+ auto ptr = kstd::make_observer(&value);
+
+ THEN("the observer pointer points to the correct object")
+ {
+ REQUIRE(&*ptr == &value);
+ }
+
+ THEN("the observe pointer has the correct element type")
+ {
+ STATIC_REQUIRE(std::is_same_v<decltype(ptr), kstd::observer_ptr<int>>);
+ }
+ }
+ }
+}
+
+SCENARIO("Observer pointer modifiers", "[observer_ptr]")
+{
+ GIVEN("A non-null observer pointer")
+ {
+ auto value = 1;
+ auto ptr = kstd::observer_ptr{&value};
+
+ WHEN("releasing the observer pointer")
+ {
+ auto raw_ptr = ptr.release();
+
+ THEN("the observer pointer is null")
+ {
+ REQUIRE_FALSE(ptr);
+ }
+
+ THEN("the returned pointer points to the correct object")
+ {
+ REQUIRE(raw_ptr == &value);
+ }
+ }
+
+ WHEN("resetting the observer pointer to nullptr")
+ {
+ ptr.reset();
+
+ THEN("the observer pointer is null")
+ {
+ REQUIRE_FALSE(ptr);
+ }
+ }
+
+ WHEN("resetting the observer pointer to a new object")
+ {
+ auto other_value = 2;
+ ptr.reset(&other_value);
+
+ THEN("the observer pointer points to the new object")
+ {
+ REQUIRE(&*ptr == &other_value);
+ }
+ }
+
+ WHEN("swapping it with another observer pointer")
+ {
+ auto other_value = 2;
+ auto other_ptr = kstd::observer_ptr{&other_value};
+ ptr.swap(other_ptr);
+
+ THEN("the observer pointer points to the other object")
+ {
+ REQUIRE(&*ptr == &other_value);
+ }
+
+ THEN("the other observer pointer points to the original object")
+ {
+ REQUIRE(&*other_ptr == &value);
+ }
+ }
+
+ WHEN("using namespace-level swap to swap it with another observer pointer")
+ {
+ using std::swap;
+ auto other_value = 2;
+ auto other_ptr = kstd::observer_ptr{&other_value};
+ swap(ptr, other_ptr);
+
+ THEN("the observer pointer points to the other object")
+ {
+ REQUIRE(&*ptr == &other_value);
+ }
+
+ THEN("the other observer pointer points to the original object")
+ {
+ REQUIRE(&*other_ptr == &value);
+ }
+ }
+ }
+}
+
+SCENARIO("Observer pointer observers", "[observer_ptr]")
+{
+ GIVEN("A non-null observer pointer")
+ {
+ auto value = Element{1};
+ auto ptr = kstd::observer_ptr{&value};
+
+ WHEN("getting the raw pointer")
+ {
+ auto raw_ptr = ptr.get();
+
+ THEN("the raw pointer points to the correct object")
+ {
+ REQUIRE(raw_ptr == &value);
+ }
+ }
+
+ WHEN("dereferencing the observer pointer")
+ {
+ auto dereferenced = *ptr;
+
+ THEN("the dereferenced value is the correct value")
+ {
+ REQUIRE(dereferenced == value);
+ }
+ }
+
+ WHEN("writing through the observer pointer with the arrow operator")
+ {
+ ptr->value = 2;
+
+ THEN("the value is updated")
+ {
+ REQUIRE(value.value == 2);
+ }
+ }
+
+ WHEN("converting the observer pointer to a raw pointer")
+ {
+ auto raw_ptr = static_cast<Element *>(ptr);
+
+ THEN("the raw pointer points to the correct object")
+ {
+ REQUIRE(raw_ptr == &value);
+ }
+ }
+
+ WHEN("checking the observer pointer as a boolean")
+ {
+ THEN("it returns true")
+ {
+ REQUIRE(static_cast<bool>(ptr));
+ }
+ }
+ }
+
+ GIVEN("A null observer pointer")
+ {
+ auto ptr = kstd::observer_ptr<Element>{};
+
+ WHEN("checking the observer pointer as a boolean")
+ {
+ THEN("it returns false")
+ {
+ REQUIRE_FALSE(static_cast<bool>(ptr));
+ }
+ }
+
+ WHEN("dereferencing the observer pointer")
+ {
+ THEN("the observer pointer panics")
+ {
+ REQUIRE_THROWS_AS(*ptr, kstd::tests::os_panic);
+ }
+ }
+
+ WHEN("writing through the observer pointer with the arrow operator")
+ {
+ THEN("the observer pointer panics")
+ {
+ REQUIRE_THROWS_AS(ptr->value = 2, kstd::tests::os_panic);
+ }
+ }
+ }
+}
+
+SCENARIO("Observer pointer comparisons", "[observer_ptr]")
+{
+ GIVEN("Observer pointers to elements of an array")
+ {
+ int arr[] = {1, 2};
+ auto ptr1 = kstd::observer_ptr{&arr[0]};
+ auto ptr2 = kstd::observer_ptr{&arr[1]};
+
+ WHEN("comparing the same observer pointer")
+ {
+ THEN("they are equal")
+ {
+ REQUIRE(ptr1 == ptr1);
+ REQUIRE((ptr1 <=> ptr1) == std::strong_ordering::equal);
+ }
+ }
+
+ WHEN("comparing different observer pointers")
+ {
+ THEN("they are ordered correctly")
+ {
+ REQUIRE(ptr1 != ptr2);
+ REQUIRE(ptr1 < ptr2);
+ REQUIRE(ptr1 <= ptr2);
+ REQUIRE(ptr2 > ptr1);
+ REQUIRE(ptr2 >= ptr1);
+ REQUIRE((ptr1 <=> ptr2) == std::strong_ordering::less);
+ REQUIRE((ptr2 <=> ptr1) == std::strong_ordering::greater);
+ }
+ }
+ }
+
+ GIVEN("A null observer pointer")
+ {
+ auto ptr = kstd::observer_ptr<int>{};
+
+ WHEN("comparing with another null observer pointer")
+ {
+ auto other_ptr = kstd::observer_ptr<int>{};
+
+ THEN("they are equal")
+ {
+ REQUIRE(ptr == other_ptr);
+ REQUIRE((ptr <=> other_ptr) == std::strong_ordering::equal);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/libs/kstd/tests/src/vector.cpp b/libs/kstd/tests/src/vector.cpp
index b7971f4..81bf32f 100644
--- a/libs/kstd/tests/src/vector.cpp
+++ b/libs/kstd/tests/src/vector.cpp
@@ -919,8 +919,8 @@ SCENARIO("Vector modifier move semantics", "[vector]")
{
GIVEN("An empty vector and a move tracker element")
{
- auto v = kstd::vector<kstd::tests::move_tracker>{};
- auto tracker = kstd::tests::move_tracker{42};
+ auto v = kstd::vector<kstd::tests::special_member_tracker>{};
+ auto tracker = kstd::tests::special_member_tracker{42};
WHEN("push_back is called with the move tracker")
{
@@ -929,8 +929,8 @@ SCENARIO("Vector modifier move semantics", "[vector]")
THEN("the element is added and the size and capacity increase")
{
REQUIRE(v.size() == 1);
- REQUIRE(v.back().was_moved);
- REQUIRE_FALSE(v.back().was_copied);
+ REQUIRE(v.back().move_constructed_count == 1);
+ REQUIRE(v.back().copy_constructed_count == 0);
REQUIRE(v.back().value == 42);
}
@@ -943,7 +943,7 @@ SCENARIO("Vector modifier move semantics", "[vector]")
GIVEN("An empty vector")
{
- auto v = kstd::vector<kstd::tests::move_tracker>{};
+ auto v = kstd::vector<kstd::tests::special_member_tracker>{};
WHEN("emplace_back is called with constructor arguments")
{
@@ -952,8 +952,8 @@ SCENARIO("Vector modifier move semantics", "[vector]")
THEN("the element is constructed directly, without moves or copies")
{
REQUIRE(v.size() == 1);
- REQUIRE_FALSE(v.back().was_moved);
- REQUIRE_FALSE(v.back().was_copied);
+ REQUIRE(v.back().move_constructed_count == 0);
+ REQUIRE(v.back().copy_constructed_count == 0);
REQUIRE(v.back().value == 42);
}
}
@@ -961,7 +961,7 @@ SCENARIO("Vector modifier move semantics", "[vector]")
GIVEN("A populated vector of move trackers")
{
- auto v = kstd::vector<kstd::tests::move_tracker>{};
+ auto v = kstd::vector<kstd::tests::special_member_tracker>{};
v.reserve(10);
v.emplace_back(10);
v.emplace_back(20);
@@ -969,41 +969,46 @@ SCENARIO("Vector modifier move semantics", "[vector]")
WHEN("inserting an element in the middle with sufficient capacity")
{
- auto const tracker = kstd::tests::move_tracker{15};
+ auto const tracker = kstd::tests::special_member_tracker{15};
v.insert(v.cbegin() + 1, tracker);
THEN("the shifted elements are move-assigned and the new element is copy-assigned")
{
REQUIRE(v.size() == 4);
REQUIRE(v[0].value == 10);
- REQUIRE_FALSE(v[0].was_moved);
+ REQUIRE(v[0].move_assigned_count == 0);
+ REQUIRE(v[0].copy_assigned_count == 0);
REQUIRE(v[1].value == 15);
- REQUIRE(v[1].was_copied);
- REQUIRE_FALSE(v[1].was_moved);
+ REQUIRE(v[1].move_assigned_count == 0);
+ REQUIRE(v[1].copy_assigned_count == 1);
+ REQUIRE(tracker.copied_from_count == 1);
REQUIRE(v[2].value == 20);
- REQUIRE(v[2].was_moved);
+ REQUIRE(v[2].move_assigned_count == 1);
+ REQUIRE(v[2].copy_assigned_count == 0);
REQUIRE(v[3].value == 30);
- REQUIRE(v[3].was_moved);
+ REQUIRE(v[3].move_constructed_count == 1);
}
}
WHEN("inserting an rvalue element in the middle with sufficient capacity")
{
- auto tracker = kstd::tests::move_tracker{15};
+ auto tracker = kstd::tests::special_member_tracker{15};
v.insert(v.cbegin() + 1, std::move(tracker));
THEN("the shifted elements are move-assigned and the new element is move-assigned")
{
REQUIRE(v.size() == 4);
REQUIRE(v[0].value == 10);
- REQUIRE_FALSE(v[0].was_moved);
+ REQUIRE(v[0].move_assigned_count == 0);
+ REQUIRE(v[0].copy_assigned_count == 0);
REQUIRE(v[1].value == 15);
- REQUIRE_FALSE(v[1].was_copied);
- REQUIRE(v[1].was_moved);
+ REQUIRE(v[1].move_assigned_count == 1);
+ REQUIRE(v[1].copy_assigned_count == 0);
+ REQUIRE(tracker.moved_from_count == 1);
REQUIRE(v[2].value == 20);
- REQUIRE(v[2].was_moved);
+ REQUIRE(v[2].move_assigned_count == 1);
REQUIRE(v[3].value == 30);
- REQUIRE(v[3].was_moved);
+ REQUIRE(v[3].move_constructed_count == 1);
}
}
@@ -1011,8 +1016,7 @@ SCENARIO("Vector modifier move semantics", "[vector]")
{
for (auto & elem : v)
{
- elem.was_copied = false;
- elem.was_moved = false;
+ elem.reset_counts();
}
auto it = v.erase(v.cbegin() + 1);
@@ -1022,12 +1026,15 @@ SCENARIO("Vector modifier move semantics", "[vector]")
REQUIRE(v.size() == 2);
REQUIRE(v[0].value == 10);
- REQUIRE_FALSE(v[0].was_moved);
- REQUIRE_FALSE(v[0].was_copied);
+ REQUIRE(v[0].move_assigned_count == 0);
+ REQUIRE(v[0].copy_assigned_count == 0);
REQUIRE(v[1].value == 30);
- REQUIRE(v[1].was_moved);
- REQUIRE_FALSE(v[1].was_copied);
+ REQUIRE(v[1].move_assigned_count == 1);
+ REQUIRE(v[1].copy_assigned_count == 0);
+
+ REQUIRE(v.data()[2].destroyed_count == 1);
+ REQUIRE(v.data()[2].moved_from_count == 1);
REQUIRE(it == v.begin() + 1);
}
@@ -1037,8 +1044,7 @@ SCENARIO("Vector modifier move semantics", "[vector]")
{
for (auto & elem : v)
{
- elem.was_copied = false;
- elem.was_moved = false;
+ elem.reset_counts();
}
auto it = v.erase(v.cend() - 1);
@@ -1048,12 +1054,20 @@ SCENARIO("Vector modifier move semantics", "[vector]")
REQUIRE(v.size() == 2);
REQUIRE(v[0].value == 10);
- REQUIRE_FALSE(v[0].was_moved);
- REQUIRE_FALSE(v[0].was_copied);
+ REQUIRE(v[0].move_constructed_count == 0);
+ REQUIRE(v[0].copy_constructed_count == 0);
+ REQUIRE(v[0].move_assigned_count == 0);
+ REQUIRE(v[0].copy_assigned_count == 0);
REQUIRE(v[1].value == 20);
- REQUIRE_FALSE(v[1].was_moved);
- REQUIRE_FALSE(v[1].was_copied);
+ REQUIRE(v[1].move_constructed_count == 0);
+ REQUIRE(v[1].copy_constructed_count == 0);
+ REQUIRE(v[1].move_assigned_count == 0);
+ REQUIRE(v[1].copy_assigned_count == 0);
+
+ REQUIRE(v.data()[2].destroyed_count == 1);
+ REQUIRE(v.data()[2].moved_from_count == 0);
+ REQUIRE(v.data()[2].copied_from_count == 0);
REQUIRE(it == v.end());
}
@@ -1066,8 +1080,7 @@ SCENARIO("Vector modifier move semantics", "[vector]")
for (auto & elem : v)
{
- elem.was_copied = false;
- elem.was_moved = false;
+ elem.reset_counts();
}
auto it = v.erase(v.cbegin() + 1, v.cbegin() + 3);
@@ -1077,16 +1090,30 @@ SCENARIO("Vector modifier move semantics", "[vector]")
REQUIRE(v.size() == 3);
REQUIRE(v[0].value == 10);
- REQUIRE_FALSE(v[0].was_moved);
- REQUIRE_FALSE(v[0].was_copied);
+ REQUIRE(v[0].move_constructed_count == 0);
+ REQUIRE(v[0].copy_constructed_count == 0);
+ REQUIRE(v[0].move_assigned_count == 0);
+ REQUIRE(v[0].copy_assigned_count == 0);
REQUIRE(v[1].value == 40);
- REQUIRE(v[1].was_moved);
- REQUIRE_FALSE(v[1].was_copied);
+ REQUIRE(v[1].move_constructed_count == 0);
+ REQUIRE(v[1].copy_constructed_count == 0);
+ REQUIRE(v[1].move_assigned_count == 1);
+ REQUIRE(v[1].copy_assigned_count == 0);
REQUIRE(v[2].value == 50);
- REQUIRE(v[2].was_moved);
- REQUIRE_FALSE(v[2].was_copied);
+ REQUIRE(v[2].move_constructed_count == 0);
+ REQUIRE(v[2].copy_constructed_count == 0);
+ REQUIRE(v[2].move_assigned_count == 1);
+ REQUIRE(v[2].copy_assigned_count == 0);
+
+ REQUIRE(v.data()[3].destroyed_count == 1);
+ REQUIRE(v.data()[3].moved_from_count == 1);
+ REQUIRE(v.data()[3].copied_from_count == 0);
+
+ REQUIRE(v.data()[4].destroyed_count == 1);
+ REQUIRE(v.data()[4].moved_from_count == 1);
+ REQUIRE(v.data()[4].copied_from_count == 0);
REQUIRE(it == v.begin() + 1);
}
@@ -1417,8 +1444,8 @@ SCENARIO("Vector const accessors and copy insertion", "[vector]")
GIVEN("An empty vector and a const lvalue tracker")
{
- auto v = kstd::vector<kstd::tests::move_tracker>{};
- auto const tracker = kstd::tests::move_tracker{42};
+ auto v = kstd::vector<kstd::tests::special_member_tracker>{};
+ auto const tracker = kstd::tests::special_me