diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index 0ec657c..1cc4877 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include #include +#include #include #include diff --git a/headers/meta.hpp/meta_utilities.hpp b/headers/meta.hpp/meta_utilities.hpp index 5d3f16c..ee58de7 100644 --- a/headers/meta.hpp/meta_utilities.hpp +++ b/headers/meta.hpp/meta_utilities.hpp @@ -31,7 +31,12 @@ namespace meta_hpp::detail }; template < typename From, typename To > - using copy_cvref_t = typename cvref_traits::template copy_to; + struct copy_cvref { + using type = typename cvref_traits::template copy_to; + }; + + template < typename From, typename To > + using copy_cvref_t = typename copy_cvref::type; } namespace meta_hpp::detail @@ -100,18 +105,29 @@ namespace meta_hpp::detail }; } +namespace meta_hpp::detail +{ + template < typename... Ts > + struct overloaded : Ts... { + using Ts::operator()...; + }; + + template < typename... Ts > + overloaded(Ts...) -> overloaded; +} + namespace meta_hpp { class value final { public: value() = default; - ~value() = default; + ~value() noexcept; - value(value&& other) = default; - value(const value& other) = default; + value(value&& other) noexcept; + value(const value& other); - value& operator=(value&& other) = default; - value& operator=(const value& other) = default; + value& operator=(value&& other) noexcept; + value& operator=(const value& other); template < detail::decay_non_uvalue_kind T > requires detail::stdex::copy_constructible> @@ -124,6 +140,7 @@ namespace meta_hpp [[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] explicit operator bool() const noexcept; + void reset() noexcept; void swap(value& other) noexcept; [[nodiscard]] any_type get_type() const noexcept; @@ -158,9 +175,12 @@ namespace meta_hpp friend std::istream& operator>>(std::istream& is, value& v); friend std::ostream& operator<<(std::ostream& os, const value& v); private: - struct traits; - std::any raw_{}; - const traits* traits_{}; + struct vtable_t; + vtable_t* vtable_{}; + private: + using buffer_t = std::aligned_storage_t; + using storage_u = std::variant; + storage_u storage_{}; }; inline void swap(value& l, value& r) noexcept { diff --git a/headers/meta.hpp/meta_utilities/value.hpp b/headers/meta.hpp/meta_utilities/value.hpp index dac1b7a..cb3f1a2 100644 --- a/headers/meta.hpp/meta_utilities/value.hpp +++ b/headers/meta.hpp/meta_utilities/value.hpp @@ -18,14 +18,18 @@ namespace meta_hpp { - struct value::traits final { + struct value::vtable_t final { const any_type type; - void* (*const data)(value&) noexcept; - const void* (*const cdata)(const value&) noexcept; + void* (*const data)(storage_u& from) noexcept; + const void* (*const cdata)(const storage_u& from) noexcept; - value (*const deref)(const value&); - value (*const index)(const value&, std::size_t); + void (*const move)(value& from, value& to) noexcept; + void (*const copy)(const value& from, value& to); + void (*const destroy)(value& self) noexcept; + + value (*const deref)(const value& from); + value (*const index)(const value& from, std::size_t); bool (*const less)(const value&, const value&); bool (*const equals)(const value&, const value&); @@ -34,167 +38,334 @@ namespace meta_hpp std::ostream& (*const ostream)(std::ostream&, const value&); template < typename T > - static const traits* get() noexcept; + static T* storage_cast(buffer_t& buffer) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast(&buffer); + } + + template < typename T > + static const T* storage_cast(const buffer_t& buffer) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast(&buffer); + } + + template < typename T > + static T* storage_cast(storage_u& storage) noexcept { + return std::visit(detail::overloaded { + [](void* ptr) { return static_cast(ptr); }, + [](buffer_t& buffer) { return storage_cast(buffer); }, + [](...) -> T* { return nullptr; }, + }, storage); + } + + template < typename T > + static const T* storage_cast(const storage_u& storage) noexcept { + return std::visit(detail::overloaded { + [](const void* ptr) { return static_cast(ptr); }, + [](const buffer_t& buffer) { return storage_cast(buffer); }, + [](...) -> const T* { return nullptr; }, + }, storage); + } + + template < typename T > + static void construct(value& dst, T&& val) { + using Tp = std::decay_t; + + constexpr bool in_buffer = + sizeof(Tp) <= sizeof(buffer_t) && + alignof(Tp) <= alignof(buffer_t) && + std::is_nothrow_move_constructible_v; + + if constexpr ( in_buffer ) { + dst.storage_.emplace(); + std::construct_at(storage_cast(dst.storage_), std::forward(val)); + } else { + dst.storage_.emplace(std::make_unique(std::forward(val)).release()); + } + + dst.vtable_ = vtable_t::get(); + } + + static void swap(value& l, value& r) noexcept { + if ( (&l == &r) || (!l && !r) ) { + return; + } + + if ( l && r ) { + if ( std::holds_alternative(l.storage_) ) { + value temp; + r.vtable_->move(r, temp); + l.vtable_->move(l, r); + temp.vtable_->move(temp, l); + } else { + value temp; + l.vtable_->move(l, temp); + r.vtable_->move(r, l); + temp.vtable_->move(temp, r); + } + } else { + if ( l ) { + l.vtable_->move(l, r); + } else { + r.vtable_->move(r, l); + } + } + } + + template < typename Tp > + static vtable_t* get() { + static vtable_t table{ + .type = resolve_type(), + + .data = [](storage_u& from) noexcept -> void* { + return storage_cast(from); + }, + + .cdata = [](const storage_u& from) noexcept -> const void* { + return storage_cast(from); + }, + + .move = [](value& from, value& to) noexcept { + std::visit(detail::overloaded { + [&to](void* ptr) { + Tp* src = static_cast(ptr); + to.storage_.emplace(src); + }, + [&to](buffer_t& buffer) { + to.storage_.emplace(); + std::construct_at(storage_cast(to.storage_), std::move(*storage_cast(buffer))); + std::destroy_at(storage_cast(buffer)); + }, + [](...){} + }, from.storage_); + + to.vtable_ = from.vtable_; + from.vtable_ = nullptr; + }, + + .copy = [](const value& from, value& to){ + std::visit(detail::overloaded { + [&to](void* ptr) { + const Tp* src = static_cast(ptr); + to.storage_.emplace(new Tp{*src}); + }, + [&to](const buffer_t& buffer) { + to.storage_.emplace(); + std::construct_at(storage_cast(to.storage_), *storage_cast(buffer)); + }, + [](...){} + }, from.storage_); + + to.vtable_ = from.vtable_; + }, + + .destroy = [](value& self) noexcept { + std::visit(detail::overloaded { + [](void* ptr) { + Tp* src = static_cast(ptr); + std::unique_ptr{src}.reset(); + }, + [](buffer_t& buffer) { + std::destroy_at(storage_cast(buffer)); + }, + [](...){} + }, self.storage_); + + self.vtable_ = nullptr; + }, + + .deref = +[]([[maybe_unused]] const value& v) -> value { + if constexpr ( detail::has_value_deref_traits ) { + return detail::value_deref_traits{}(v.cast()); + } else { + throw std::logic_error("value type doesn't have value deref traits"); + } + }, + + .index = +[]([[maybe_unused]] const value& v, [[maybe_unused]] std::size_t index) -> value { + if constexpr ( detail::has_value_index_traits ) { + return detail::value_index_traits{}(v.cast(), index); + } else { + throw std::logic_error("value type doesn't have value index traits"); + } + }, + + .less = +[]([[maybe_unused]] const value& l, [[maybe_unused]] const value& r) -> bool { + if constexpr ( detail::has_value_less_traits ) { + return detail::value_less_traits{}(l.cast(), r.cast()); + } else { + throw std::logic_error("value type doesn't have value less traits"); + } + }, + + .equals = +[]([[maybe_unused]] const value& l, [[maybe_unused]] const value& r) -> bool { + if constexpr ( detail::has_value_equals_traits ) { + return detail::value_equals_traits{}(l.cast(), r.cast()); + } else { + throw std::logic_error("value type doesn't have value equals traits"); + } + }, + + .istream = +[]([[maybe_unused]] std::istream& is, [[maybe_unused]] value& v) -> std::istream& { + if constexpr ( detail::has_value_istream_traits ) { + return detail::value_istream_traits{}(is, v.cast()); + } else { + throw std::logic_error("value type doesn't have value istream traits"); + } + }, + + .ostream = +[]([[maybe_unused]] std::ostream& os, [[maybe_unused]] const value& v) -> std::ostream& { + if constexpr ( detail::has_value_ostream_traits ) { + return detail::value_ostream_traits{}(os, v.cast()); + } else { + throw std::logic_error("value type doesn't have value ostream traits"); + } + }, + }; + + return &table; + } }; - - template < typename T > - const value::traits* value::traits::get() noexcept { - static_assert(std::is_same_v>); - - static const traits traits{ - .type = resolve_type(), - - .data = +[](value& v) noexcept -> void* { - return v.try_cast(); - }, - - .cdata = +[](const value& v) noexcept -> const void* { - return v.try_cast(); - }, - - .deref = +[]([[maybe_unused]] const value& v) -> value { - if constexpr ( detail::has_value_deref_traits ) { - return detail::value_deref_traits{}(v.cast()); - } else { - throw std::logic_error("value type doesn't have value deref traits"); - } - }, - - .index = +[]([[maybe_unused]] const value& v, [[maybe_unused]] std::size_t index) -> value { - if constexpr ( detail::has_value_index_traits ) { - return detail::value_index_traits{}(v.cast(), index); - } else { - throw std::logic_error("value type doesn't have value index traits"); - } - }, - - .less = +[]([[maybe_unused]] const value& l, [[maybe_unused]] const value& r) -> bool { - if constexpr ( detail::has_value_less_traits ) { - return detail::value_less_traits{}(l.cast(), r.cast()); - } else { - throw std::logic_error("value type doesn't have value less traits"); - } - }, - - .equals = +[]([[maybe_unused]] const value& l, [[maybe_unused]] const value& r) -> bool { - if constexpr ( detail::has_value_equals_traits ) { - return detail::value_equals_traits{}(l.cast(), r.cast()); - } else { - throw std::logic_error("value type doesn't have value equals traits"); - } - }, - - .istream = +[]([[maybe_unused]] std::istream& is, [[maybe_unused]] value& v) -> std::istream& { - if constexpr ( detail::has_value_istream_traits ) { - return detail::value_istream_traits{}(is, v.cast()); - } else { - throw std::logic_error("value type doesn't have value istream traits"); - } - }, - - .ostream = +[]([[maybe_unused]] std::ostream& os, [[maybe_unused]] const value& v) -> std::ostream& { - if constexpr ( detail::has_value_ostream_traits ) { - return detail::value_ostream_traits{}(os, v.cast()); - } else { - throw std::logic_error("value type doesn't have value ostream traits"); - } - }, - }; - - return &traits; - } } namespace meta_hpp { + inline value::~value() noexcept { + reset(); + } + + inline value::value(value&& other) noexcept { + if ( other.vtable_ != nullptr ) { + other.vtable_->move(other, *this); + } + } + + inline value::value(const value& other) { + if ( other.vtable_ != nullptr ) { + other.vtable_->copy(other, *this); + } + } + + inline value& value::operator=(value&& other) noexcept { + if ( this != &other ) { + value{std::move(other)}.swap(*this); + } + return *this; + } + + inline value& value::operator=(const value& other) { + if ( this != &other ) { + value{other}.swap(*this); + } + return *this; + } + template < detail::decay_non_uvalue_kind T > requires detail::stdex::copy_constructible> - value::value(T&& val) - : raw_{std::forward(val)} - , traits_{traits::get>()} {} + value::value(T&& val) { + vtable_t::construct(*this, std::forward(val)); + } template < detail::decay_non_uvalue_kind T > requires detail::stdex::copy_constructible> value& value::operator=(T&& val) { - value temp{std::forward(val)}; - swap(temp); + value{std::forward(val)}.swap(*this); return *this; } inline bool value::is_valid() const noexcept { - return raw_.has_value(); + return vtable_ != nullptr; } inline value::operator bool() const noexcept { return is_valid(); } + inline void value::reset() noexcept { + if ( vtable_ != nullptr ) { + vtable_->destroy(*this); + vtable_ = nullptr; + } + } + inline void value::swap(value& other) noexcept { - using std::swap; - swap(raw_, other.raw_); - swap(traits_, other.traits_); + vtable_t::swap(*this, other); } inline any_type value::get_type() const noexcept { - return traits_ != nullptr - ? traits_->type - : resolve_type(); + return vtable_ != nullptr ? vtable_->type : resolve_type(); } inline void* value::data() noexcept { - return traits_ != nullptr ? traits_->data(*this) : nullptr; + return vtable_ != nullptr ? vtable_->data(storage_) : nullptr; } inline const void* value::data() const noexcept { - return traits_ != nullptr ? traits_->cdata(*this) : nullptr; + return vtable_ != nullptr ? vtable_->cdata(storage_) : nullptr; } inline const void* value::cdata() const noexcept { - return traits_ != nullptr ? traits_->cdata(*this) : nullptr; + return vtable_ != nullptr ? vtable_->cdata(storage_) : nullptr; } inline value value::operator*() const { - return traits_ != nullptr ? traits_->deref(*this) : value{}; + return vtable_ != nullptr ? vtable_->deref(*this) : value{}; } inline value value::operator[](std::size_t index) const { - return traits_ != nullptr ? traits_->index(*this, index) : value{}; + return vtable_ != nullptr ? vtable_->index(*this, index) : value{}; } template < typename T > std::decay_t& value::cast() & { using Tp = std::decay_t; - return std::any_cast(raw_); + if ( Tp* ptr = try_cast() ) { + return *ptr; + } + throw std::logic_error("bad value cast"); } template < typename T > std::decay_t&& value::cast() && { using Tp = std::decay_t; - return std::move(std::any_cast(raw_)); + if ( Tp* ptr = try_cast() ) { + return std::move(*ptr); + } + throw std::logic_error("bad value cast"); } template < typename T > const std::decay_t& value::cast() const & { using Tp = std::decay_t; - return std::any_cast(raw_); + if ( const Tp* ptr = try_cast() ) { + return *ptr; + } + throw std::logic_error("bad value cast"); } template < typename T > const std::decay_t&& value::cast() const && { using Tp = std::decay_t; - return std::move(std::any_cast(raw_)); + if ( const Tp* ptr = try_cast() ) { + return std::move(*ptr); + } + throw std::logic_error("bad value cast"); } template < typename T > std::decay_t* value::try_cast() noexcept { using Tp = std::decay_t; - return std::any_cast(&raw_); + return get_type() == resolve_type() + ? vtable_t::storage_cast(storage_) + : nullptr; } template < typename T > const std::decay_t* value::try_cast() const noexcept { using Tp = std::decay_t; - return std::any_cast(&raw_); + return get_type() == resolve_type() + ? vtable_t::storage_cast(storage_) + : nullptr; } } @@ -232,7 +403,7 @@ namespace meta_hpp } return (l.get_type() < r.get_type()) - || (l.get_type() == r.get_type() && l.traits_->less(l, r)); + || (l.get_type() == r.get_type() && l.vtable_->less(l, r)); } } @@ -268,17 +439,17 @@ namespace meta_hpp } return l.get_type() == r.get_type() - && l.traits_->equals(l, r); + && l.vtable_->equals(l, r); } } namespace meta_hpp { inline std::istream& operator>>(std::istream& is, value& v) { - return v.traits_->istream(is, v); + return v.vtable_->istream(is, v); } inline std::ostream& operator<<(std::ostream& os, const value& v) { - return v.traits_->ostream(os, v); + return v.vtable_->ostream(os, v); } } diff --git a/untests/meta_utilities/value2_tests.cpp b/untests/meta_utilities/value2_tests.cpp new file mode 100644 index 0000000..58588bc --- /dev/null +++ b/untests/meta_utilities/value2_tests.cpp @@ -0,0 +1,411 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "../meta_untests.hpp" + +namespace +{ + struct ivec2 final { + int x{}; + int y{}; + + ivec2() = delete; + explicit ivec2(int v): x{v}, y{v} {} + ivec2(int x, int y): x{x}, y{y} {} + + ivec2(ivec2&& other) noexcept + : x{other.x} + , y{other.y} { + other.x = 0; + other.y = 0; + ++move_ctor_counter; + } + + ivec2(const ivec2& other) noexcept + : x{other.x} + , y{other.y} { + ++copy_ctor_counter; + } + + ~ivec2() noexcept { + ++dtor_counter; + } + + ivec2& operator=(ivec2&& other) = delete; + ivec2& operator=(const ivec2& other) = delete; + + static int dtor_counter; + static int move_ctor_counter; + static int copy_ctor_counter; + }; + + struct ivec2_big final { + int x{}; + int y{}; + + int dummy[42]{}; + + ivec2_big() = delete; + explicit ivec2_big(int v): x{v}, y{v} {} + ivec2_big(int x, int y): x{x}, y{y} {} + + ivec2_big(ivec2_big&& other) noexcept + : x{other.x} + , y{other.y} { + other.x = 0; + other.y = 0; + ++move_ctor_counter; + } + + ivec2_big(const ivec2_big& other) noexcept + : x{other.x} + , y{other.y} { + ++copy_ctor_counter; + } + + ~ivec2_big() noexcept { + ++dtor_counter; + } + + ivec2_big& operator=(ivec2_big&& other) = delete; + ivec2_big& operator=(const ivec2_big& other) = delete; + + static int dtor_counter; + static int move_ctor_counter; + static int copy_ctor_counter; + }; + + [[maybe_unused]] bool operator==(const ivec2& l, const ivec2& r) noexcept { + return l.x == r.x && l.y == r.y; + } + + [[maybe_unused]] bool operator==(const ivec2_big& l, const ivec2_big& r) noexcept { + return l.x == r.x && l.y == r.y; + } + + int ivec2::dtor_counter{0}; + int ivec2::move_ctor_counter{0}; + int ivec2::copy_ctor_counter{0}; + + int ivec2_big::dtor_counter{0}; + int ivec2_big::move_ctor_counter{0}; + int ivec2_big::copy_ctor_counter{0}; +} + +TEST_CASE("meta/meta_utilities/value2") { + namespace meta = meta_hpp; + + meta::class_() + .ctor_() + .ctor_() + .ctor_() + .ctor_() + .member_("x", &ivec2::x) + .member_("y", &ivec2::y); + + meta::class_() + .ctor_() + .ctor_() + .ctor_() + .ctor_() + .member_("x", &ivec2_big::x) + .member_("y", &ivec2_big::y); +} + +TEST_CASE("meta/meta_utilities/value2/counters/small") { + namespace meta = meta_hpp; + + ivec2::dtor_counter = 0; + ivec2::move_ctor_counter = 0; + ivec2::copy_ctor_counter = 0; + + SUBCASE("def ctor") { + { + meta::value v{}; + CHECK(ivec2::dtor_counter == 0); + CHECK(ivec2::move_ctor_counter == 0); + CHECK(ivec2::copy_ctor_counter == 0); + } + CHECK(ivec2::dtor_counter == 0); + CHECK(ivec2::move_ctor_counter == 0); + CHECK(ivec2::copy_ctor_counter == 0); + } + + SUBCASE("val ctor") { + { + meta::value v{ivec2{1,2}}; + CHECK(ivec2::dtor_counter == 1); + CHECK(ivec2::move_ctor_counter == 1); + CHECK(ivec2::copy_ctor_counter == 0); + } + CHECK(ivec2::dtor_counter == 2); + CHECK(ivec2::move_ctor_counter == 1); + CHECK(ivec2::copy_ctor_counter == 0); + } + + SUBCASE("move ctor") { + { + meta::value v1{ivec2{1,2}}; + meta::value v2{std::move(v1)}; + + CHECK_FALSE(v1); + CHECK(v2.cast().x == 1); + + CHECK(ivec2::dtor_counter == 2); + CHECK(ivec2::move_ctor_counter == 2); + CHECK(ivec2::copy_ctor_counter == 0); + } + CHECK(ivec2::dtor_counter == 3); + CHECK(ivec2::move_ctor_counter == 2); + CHECK(ivec2::copy_ctor_counter == 0); + } + + SUBCASE("copy ctor") { + { + meta::value v1{ivec2{1,2}}; + meta::value v2{std::as_const(v1)}; + + CHECK(v1.cast().x == 1); + CHECK(v2.cast().y == 2); + + CHECK(ivec2::dtor_counter == 1); + CHECK(ivec2::move_ctor_counter == 1); + CHECK(ivec2::copy_ctor_counter == 1); + } + CHECK(ivec2::dtor_counter == 3); + CHECK(ivec2::move_ctor_counter == 1); + CHECK(ivec2::copy_ctor_counter == 1); + } + + SUBCASE("swap") { + { + meta::value v1{ivec2{1,2}}; + meta::value v2{ivec2{3,4}}; + CHECK(ivec2::dtor_counter == 2); + CHECK(ivec2::move_ctor_counter == 2); + CHECK(ivec2::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK(v1.cast().x == 3); + CHECK(v2.cast().x == 1); + + CHECK(ivec2::dtor_counter == 5); + CHECK(ivec2::move_ctor_counter == 5); + CHECK(ivec2::copy_ctor_counter == 0); + } + CHECK(ivec2::dtor_counter == 7); + CHECK(ivec2::move_ctor_counter == 5); + CHECK(ivec2::copy_ctor_counter == 0); + } +} + +TEST_CASE("meta/meta_utilities/value2/counters/big") { + namespace meta = meta_hpp; + + ivec2_big::dtor_counter = 0; + ivec2_big::move_ctor_counter = 0; + ivec2_big::copy_ctor_counter = 0; + + SUBCASE("def ctor") { + { + meta::value v{}; + CHECK(ivec2_big::dtor_counter == 0); + CHECK(ivec2_big::move_ctor_counter == 0); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + CHECK(ivec2_big::dtor_counter == 0); + CHECK(ivec2_big::move_ctor_counter == 0); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + + SUBCASE("val ctor") { + { + meta::value v{ivec2_big{1,2}}; + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + CHECK(ivec2_big::dtor_counter == 2); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + + SUBCASE("move ctor") { + { + meta::value v1{ivec2_big{1,2}}; + meta::value v2{std::move(v1)}; + + CHECK_FALSE(v1); + CHECK(v2.cast().x == 1); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + CHECK(ivec2_big::dtor_counter == 2); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + + SUBCASE("copy ctor") { + { + meta::value v1{ivec2_big{1,2}}; + meta::value v2{std::as_const(v1)}; + + CHECK(v1.cast().x == 1); + CHECK(v2.cast().y == 2); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 1); + } + CHECK(ivec2_big::dtor_counter == 3); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 1); + } + + SUBCASE("swap") { + { + meta::value v1{ivec2_big{1,2}}; + meta::value v2{ivec2_big{3,4}}; + CHECK(ivec2_big::dtor_counter == 2); + CHECK(ivec2_big::move_ctor_counter == 2); + CHECK(ivec2_big::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK(v1.cast().x == 3); + CHECK(v2.cast().x == 1); + + CHECK(ivec2_big::dtor_counter == 2); + CHECK(ivec2_big::move_ctor_counter == 2); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + CHECK(ivec2_big::dtor_counter == 4); + CHECK(ivec2_big::move_ctor_counter == 2); + CHECK(ivec2_big::copy_ctor_counter == 0); + } +} + +TEST_CASE("meta/meta_utilities/value2/counters/swap") { + namespace meta = meta_hpp; + + ivec2::dtor_counter = 0; + ivec2::move_ctor_counter = 0; + ivec2::copy_ctor_counter = 0; + + ivec2_big::dtor_counter = 0; + ivec2_big::move_ctor_counter = 0; + ivec2_big::copy_ctor_counter = 0; + + SUBCASE("empty/small") { + { + meta::value v1{}; + meta::value v2{ivec2{1,2}}; + + CHECK(ivec2::dtor_counter == 1); + CHECK(ivec2::move_ctor_counter == 1); + CHECK(ivec2::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK(v1.cast().x == 1); + CHECK_FALSE(v2); + + CHECK(ivec2::dtor_counter == 2); + CHECK(ivec2::move_ctor_counter == 2); + CHECK(ivec2::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK_FALSE(v1); + CHECK(v2.cast().y == 2); + + CHECK(ivec2::dtor_counter == 3); + CHECK(ivec2::move_ctor_counter == 3); + CHECK(ivec2::copy_ctor_counter == 0); + } + + CHECK(ivec2::dtor_counter == 4); + CHECK(ivec2::move_ctor_counter == 3); + CHECK(ivec2::copy_ctor_counter == 0); + } + + SUBCASE("empty/big") { + { + meta::value v1{}; + meta::value v2{ivec2_big{3,4}}; + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK(v1.cast().x == 3); + CHECK_FALSE(v2); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK_FALSE(v1); + CHECK(v2.cast().y == 4); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + + CHECK(ivec2_big::dtor_counter == 2); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + + SUBCASE("small/big") { + { + meta::value v1{ivec2{1,2}}; + meta::value v2{ivec2_big{3,4}}; + + CHECK(ivec2::dtor_counter == 1); + CHECK(ivec2::move_ctor_counter == 1); + CHECK(ivec2::copy_ctor_counter == 0); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK(v1.cast().x == 3); + CHECK(v2.cast().x == 1); + + CHECK(ivec2::dtor_counter == 2); + CHECK(ivec2::move_ctor_counter == 2); + CHECK(ivec2::copy_ctor_counter == 0); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + + v1.swap(v2); + CHECK(v1.cast().y == 2); + CHECK(v2.cast().y == 4); + + CHECK(ivec2::dtor_counter == 3); + CHECK(ivec2::move_ctor_counter == 3); + CHECK(ivec2::copy_ctor_counter == 0); + + CHECK(ivec2_big::dtor_counter == 1); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } + + CHECK(ivec2::dtor_counter == 4); + CHECK(ivec2::move_ctor_counter == 3); + CHECK(ivec2::copy_ctor_counter == 0); + + CHECK(ivec2_big::dtor_counter == 2); + CHECK(ivec2_big::move_ctor_counter == 1); + CHECK(ivec2_big::copy_ctor_counter == 0); + } +} diff --git a/untests/meta_utilities/value_tests.cpp b/untests/meta_utilities/value_tests.cpp index 007afdf..591ab58 100644 --- a/untests/meta_utilities/value_tests.cpp +++ b/untests/meta_utilities/value_tests.cpp @@ -395,7 +395,7 @@ TEST_CASE("meta/meta_utilities/value") { val1.swap(val2); CHECK(val1 == ivec2{1,2}); CHECK(val2 == "world"s); - CHECK(ivec2::move_ctor_counter == 3); + CHECK(ivec2::move_ctor_counter == 2); CHECK(ivec2::copy_ctor_counter == 0); swap(val1, val2);