diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/kstd/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/flat_map.hpp | 2 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/observer_ptr.hpp | 163 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/bits/unique_ptr.hpp | 25 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/memory | 9 | ||||
| -rw-r--r-- | libs/kstd/include/kstd/string | 13 | ||||
| -rw-r--r-- | libs/kstd/tests/include/kstd/tests/test_types.hpp | 71 | ||||
| -rw-r--r-- | libs/kstd/tests/src/observer_ptr.cpp | 359 | ||||
| -rw-r--r-- | libs/kstd/tests/src/vector.cpp | 119 |
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 |
