From 953d7215110e4d6a912d8c5bc9cc83572fbb362d Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Tue, 3 Jan 2023 07:17:02 +0700 Subject: [PATCH] 'in place' example and new memory buffer helper class --- README.md | 1 + headers/meta.hpp/meta_base.hpp | 3 + headers/meta.hpp/meta_base/fixed_function.hpp | 2 +- headers/meta.hpp/meta_base/memory_buffer.hpp | 92 +++++++ headers/meta.hpp/meta_binds.hpp | 4 +- manuals/meta_examples/inplace_example.cpp | 63 +++++ singles/headers/meta.hpp/meta_all.hpp | 257 ++++++++++++------ untests/meta_base/memory_buffer_tests.cpp | 171 ++++++++++++ 8 files changed, 514 insertions(+), 79 deletions(-) create mode 100644 headers/meta.hpp/meta_base/memory_buffer.hpp create mode 100644 manuals/meta_examples/inplace_example.cpp create mode 100644 untests/meta_base/memory_buffer_tests.cpp diff --git a/README.md b/README.md index bc9a06c..9bf11bd 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ target_link_libraries(your_project_target PUBLIC meta.hpp::meta.hpp) - [Class](./manuals/meta_examples/class_example.cpp) - [Enum](./manuals/meta_examples/enum_example.cpp) - [Function](./manuals/meta_examples/function_example.cpp) +- [InPlace](./manuals/meta_examples/inplace_example.cpp) - [Member](./manuals/meta_examples/member_example.cpp) - [Method](./manuals/meta_examples/method_example.cpp) - [Scopes](./manuals/meta_examples/scopes_example.cpp) diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index ed6d76b..3fab6e5 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -13,6 +13,7 @@ #include "meta_base/fixed_function.hpp" #include "meta_base/hash_combiner.hpp" #include "meta_base/is_in_place_type.hpp" +#include "meta_base/memory_buffer.hpp" #include "meta_base/noncopyable.hpp" #include "meta_base/overloaded.hpp" #include "meta_base/select_overload.hpp" @@ -23,6 +24,8 @@ namespace meta_hpp { + using detail::memory_buffer; + using detail::select_const; using detail::select_non_const; using detail::select_overload; diff --git a/headers/meta.hpp/meta_base/fixed_function.hpp b/headers/meta.hpp/meta_base/fixed_function.hpp index 0e72313..2464da2 100644 --- a/headers/meta.hpp/meta_base/fixed_function.hpp +++ b/headers/meta.hpp/meta_base/fixed_function.hpp @@ -84,7 +84,7 @@ namespace meta_hpp::detail }; template < typename Function > - inline void swap(fixed_function& l, fixed_function& r) noexcept { + void swap(fixed_function& l, fixed_function& r) noexcept { l.swap(r); } } diff --git a/headers/meta.hpp/meta_base/memory_buffer.hpp b/headers/meta.hpp/meta_base/memory_buffer.hpp new file mode 100644 index 0000000..b645375 --- /dev/null +++ b/headers/meta.hpp/meta_base/memory_buffer.hpp @@ -0,0 +1,92 @@ +/******************************************************************************* + * 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-2022, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "base.hpp" + +namespace meta_hpp::detail +{ + class memory_buffer final { + public: + memory_buffer() = default; + + memory_buffer(const memory_buffer&) = delete; + memory_buffer& operator=(const memory_buffer&) = delete; + + memory_buffer(memory_buffer&& other) noexcept + : memory_{other.memory_} + , size_{other.size_} + , align_{other.align_} { + other.memory_ = nullptr; + other.size_ = 0; + other.align_ = std::align_val_t{}; + } + + memory_buffer& operator=(memory_buffer&& other) noexcept { + if ( this != &other ) { + memory_buffer{std::move(other)}.swap(*this); + } + return *this; + } + + explicit memory_buffer(std::size_t size, std::align_val_t align) + : memory_{::operator new(size, align)} + , size_{size} + , align_{align} {} + + ~memory_buffer() noexcept { + reset(); + } + + [[nodiscard]] bool is_valid() const noexcept { + return memory_ != nullptr; + } + + [[nodiscard]] explicit operator bool() const noexcept { + return is_valid(); + } + + void reset() noexcept { + if ( memory_ != nullptr ) { + ::operator delete(memory_, align_); + memory_ = nullptr; + size_ = 0; + align_ = std::align_val_t{}; + } + } + + void swap(memory_buffer& other) noexcept { + std::swap(memory_, other.memory_); + std::swap(size_, other.size_); + std::swap(align_, other.align_); + } + + [[nodiscard]] void* get_memory() noexcept { + return memory_; + } + + [[nodiscard]] const void* get_memory() const noexcept { + return memory_; + } + + [[nodiscard]] std::size_t get_size() const noexcept { + return size_; + } + + [[nodiscard]] std::align_val_t get_align() const noexcept { + return align_; + } + private: + void* memory_{}; + std::size_t size_{}; + std::align_val_t align_{}; + }; + + inline void swap(memory_buffer& l, memory_buffer& r) noexcept { + l.swap(r); + } +} diff --git a/headers/meta.hpp/meta_binds.hpp b/headers/meta.hpp/meta_binds.hpp index 4cc0b87..66d3e0d 100644 --- a/headers/meta.hpp/meta_binds.hpp +++ b/headers/meta.hpp/meta_binds.hpp @@ -17,12 +17,12 @@ namespace meta_hpp::detail template < typename Class, typename... Args > concept class_bind_constructor_kind = class_kind && - requires(Args&&... args) { { Class{std::forward(args)...} }; }; + std::is_constructible_v; template < typename Class > concept class_bind_destructor_kind = class_kind && - requires(Class&& inst) { { inst.~Class() }; }; + std::is_destructible_v; template < typename Class, typename Base > concept class_bind_base_kind = diff --git a/manuals/meta_examples/inplace_example.cpp b/manuals/meta_examples/inplace_example.cpp new file mode 100644 index 0000000..01400cd --- /dev/null +++ b/manuals/meta_examples/inplace_example.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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-2022, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "../meta_manuals.hpp" + +namespace +{ + struct ivec2 { + int x; + int y; + + ivec2(int xy) : x{xy}, y{xy} {} + ivec2(int nx, int ny) : x{nx}, y{ny} {} + + int length2() const noexcept { + return x * x + y * y; + } + }; +} + +TEST_CASE("meta/meta_examples/inplace") { + namespace meta = meta_hpp; + + // 'ivec2' class type registration + meta::class_() + .constructor_() + .constructor_() + .member_("x", &ivec2::x) + .member_("y", &ivec2::y) + .method_("length2", &ivec2::length2); + + // creates local scope for finding types by name + const meta::scope math_scope = meta::local_scope_("math") + .typedef_("ivec2"); + + // finds required types/members/methods + const meta::class_type ivec2_type = math_scope.get_typedef("ivec2").as_class(); + const meta::member ivec2_x = ivec2_type.get_member("x"); + const meta::member ivec2_y = ivec2_type.get_member("y"); + const meta::method ivec2_length2 = ivec2_type.get_method("length2"); + + // allocates dynamic memory buffer with required size and alignment + meta::memory_buffer ivec2_buffer{ + ivec2_type.get_size(), + std::align_val_t{ivec2_type.get_align()}}; + + // creates 'ivec2' on the memory buffer ('placement new' under the hood) + const meta::uvalue ivec2_ptr = ivec2_type.create_at(ivec2_buffer.get_memory(), 2, 3); + + // 'create_at' returns a pointer to the constructed object + CHECK(ivec2_ptr.get_type() == meta::resolve_type()); + + // interacts with the created object as usual + CHECK(ivec2_x.get(ivec2_ptr) == 2); + CHECK(ivec2_y.get(ivec2_ptr) == 3); + CHECK(ivec2_length2(ivec2_ptr) == 13); + + // you must manually call the object's destructor + ivec2_type.destroy_at(ivec2_buffer.get_memory()); +} diff --git a/singles/headers/meta.hpp/meta_all.hpp b/singles/headers/meta.hpp/meta_all.hpp index 35a5381..fad178c 100644 --- a/singles/headers/meta.hpp/meta_all.hpp +++ b/singles/headers/meta.hpp/meta_all.hpp @@ -354,7 +354,7 @@ namespace meta_hpp::detail }; template < typename Function > - inline void swap(fixed_function& l, fixed_function& r) noexcept { + void swap(fixed_function& l, fixed_function& r) noexcept { l.swap(r); } } @@ -515,6 +515,89 @@ namespace meta_hpp::detail inline constexpr bool is_in_place_type_v = is_in_place_type::value; } +namespace meta_hpp::detail +{ + class memory_buffer final { + public: + memory_buffer() = default; + + memory_buffer(const memory_buffer&) = delete; + memory_buffer& operator=(const memory_buffer&) = delete; + + memory_buffer(memory_buffer&& other) noexcept + : memory_{other.memory_} + , size_{other.size_} + , align_{other.align_} { + other.memory_ = nullptr; + other.size_ = 0; + other.align_ = std::align_val_t{}; + } + + memory_buffer& operator=(memory_buffer&& other) noexcept { + if ( this != &other ) { + memory_buffer{std::move(other)}.swap(*this); + } + return *this; + } + + explicit memory_buffer(std::size_t size, std::align_val_t align) + : memory_{::operator new(size, align)} + , size_{size} + , align_{align} {} + + ~memory_buffer() noexcept { + reset(); + } + + [[nodiscard]] bool is_valid() const noexcept { + return memory_ != nullptr; + } + + [[nodiscard]] explicit operator bool() const noexcept { + return is_valid(); + } + + void reset() noexcept { + if ( memory_ != nullptr ) { + ::operator delete(memory_, align_); + memory_ = nullptr; + size_ = 0; + align_ = std::align_val_t{}; + } + } + + void swap(memory_buffer& other) noexcept { + std::swap(memory_, other.memory_); + std::swap(size_, other.size_); + std::swap(align_, other.align_); + } + + [[nodiscard]] void* get_memory() noexcept { + return memory_; + } + + [[nodiscard]] const void* get_memory() const noexcept { + return memory_; + } + + [[nodiscard]] std::size_t get_size() const noexcept { + return size_; + } + + [[nodiscard]] std::align_val_t get_align() const noexcept { + return align_; + } + private: + void* memory_{}; + std::size_t size_{}; + std::align_val_t align_{}; + }; + + inline void swap(memory_buffer& l, memory_buffer& r) noexcept { + l.swap(r); + } +} + namespace meta_hpp::detail { class noncopyable { @@ -728,6 +811,8 @@ namespace meta_hpp::detail namespace meta_hpp { + using detail::memory_buffer; + using detail::select_const; using detail::select_non_const; using detail::select_overload; @@ -1560,12 +1645,12 @@ namespace meta_hpp template < typename... Args > [[nodiscard]] uvalue create(Args&&... args) const; - template < typename... Args > - [[nodiscard]] uvalue operator()(Args&&... args) const; + uvalue create_at(void* mem, Args&&... args) const; template < typename Arg > - bool destroy(Arg&& ptr) const; + bool destroy(Arg&& arg) const; + bool destroy_at(void* mem) const; template < detail::class_kind Derived > [[nodiscard]] bool is_base_of() const noexcept; @@ -2464,10 +2549,10 @@ namespace meta_hpp [[nodiscard]] const constructor_type& get_type() const noexcept; template < typename... Args > - uvalue invoke(Args&&... args) const; + [[nodiscard]] uvalue create(Args&&... args) const; template < typename... Args > - uvalue operator()(Args&&... args) const; + uvalue create_at(void* mem, Args&&... args) const; template < typename... Args > [[nodiscard]] bool is_invocable_with() const noexcept; @@ -2497,16 +2582,9 @@ namespace meta_hpp [[nodiscard]] const destructor_type& get_type() const noexcept; template < typename Arg > - void invoke(Arg&& ptr) const; + bool destroy(Arg&& arg) const; - template < typename Arg > - void operator()(Arg&& ptr) const; - - template < typename Arg > - [[nodiscard]] bool is_invocable_with() const noexcept; - - template < typename Arg > - [[nodiscard]] bool is_invocable_with(Arg&& ptr) const noexcept; + void destroy_at(void* mem) const; private: detail::destructor_state_ptr state_; friend auto detail::state_access(const destructor&); @@ -2761,13 +2839,15 @@ namespace meta_hpp::detail }; struct constructor_state final { - using invoke_impl = fixed_function)>; + using create_impl = fixed_function)>; + using create_at_impl = fixed_function)>; using is_invocable_with_impl = fixed_function)>; constructor_index index; metadata_map metadata; - invoke_impl invoke; + create_impl create; + create_at_impl create_at; is_invocable_with_impl is_invocable_with; argument_list arguments; @@ -2777,14 +2857,14 @@ namespace meta_hpp::detail }; struct destructor_state final { - using invoke_impl = fixed_function; - using is_invocable_with_impl = fixed_function; + using destroy_impl = fixed_function; + using destroy_at_impl = fixed_function; destructor_index index; metadata_map metadata; - invoke_impl invoke; - is_invocable_with_impl is_invocable_with; + destroy_impl destroy; + destroy_at_impl destroy_at; template < class_kind Class > [[nodiscard]] static destructor_state_ptr make(metadata_map metadata); @@ -3161,12 +3241,12 @@ namespace meta_hpp::detail template < typename Class, typename... Args > concept class_bind_constructor_kind = class_kind && - requires(Args&&... args) { { Class{std::forward(args)...} }; }; + std::is_constructible_v; template < typename Class > concept class_bind_destructor_kind = class_kind && - requires(Class&& inst) { { inst.~Class() }; }; + std::is_destructible_v; template < typename Class, typename Base > concept class_bind_base_kind = @@ -5157,7 +5237,7 @@ namespace meta_hpp::detail namespace meta_hpp::detail { template < constructor_policy_kind Policy, class_kind Class, typename... Args > - uvalue raw_constructor_invoke(std::span args) { + uvalue raw_constructor_create(std::span args) { using ct = constructor_traits; using class_type = typename ct::class_type; using argument_types = typename ct::argument_types; @@ -5200,6 +5280,26 @@ namespace meta_hpp::detail }(std::make_index_sequence()); } + template < class_kind Class, typename... Args > + uvalue raw_constructor_create_at(void* mem, std::span args) { + using ct = constructor_traits; + using class_type = typename ct::class_type; + using argument_types = typename ct::argument_types; + + if ( args.size() != ct::arity ) { + throw_exception_with("an attempt to call a constructor with an incorrect arity"); + } + + return [mem, args](std::index_sequence) -> uvalue { + if ( !(... && args[Is].can_cast_to>()) ) { + throw_exception_with("an attempt to call a constructor with incorrect argument types"); + } + return uvalue{std::construct_at( + static_cast(mem), + args[Is].cast>()...)}; + }(std::make_index_sequence()); + } + template < class_kind Class, typename... Args > bool raw_constructor_is_invocable_with(std::span args) { using ct = constructor_traits; @@ -5218,8 +5318,13 @@ namespace meta_hpp::detail namespace meta_hpp::detail { template < constructor_policy_kind Policy, class_kind Class, typename... Args > - constructor_state::invoke_impl make_constructor_invoke() { - return &raw_constructor_invoke; + constructor_state::create_impl make_constructor_create() { + return &raw_constructor_create; + } + + template < class_kind Class, typename... Args > + constructor_state::create_at_impl make_constructor_create_at() { + return &raw_constructor_create_at; } template < class_kind Class, typename... Args > @@ -5254,7 +5359,8 @@ namespace meta_hpp::detail return std::make_shared(constructor_state{ .index{constructor_index::make()}, .metadata{std::move(metadata)}, - .invoke{make_constructor_invoke()}, + .create{make_constructor_create()}, + .create_at{make_constructor_create_at()}, .is_invocable_with{make_constructor_is_invocable_with()}, .arguments{make_constructor_arguments()}, }); @@ -5292,19 +5398,25 @@ namespace meta_hpp } template < typename... Args > - uvalue constructor::invoke(Args&&... args) const { + uvalue constructor::create(Args&&... args) const { if constexpr ( sizeof...(Args) > 0 ) { using namespace detail; const std::array vargs{uarg{std::forward(args)}...}; - return state_->invoke(vargs); + return state_->create(vargs); } else { - return state_->invoke({}); + return state_->create({}); } } template < typename... Args > - uvalue constructor::operator()(Args&&... args) const { - return invoke(std::forward(args)...); + uvalue constructor::create_at(void* mem, Args&&... args) const { + if constexpr ( sizeof...(Args) > 0 ) { + using namespace detail; + const std::array vargs{uarg{std::forward(args)}...}; + return state_->create_at(mem, vargs); + } else { + return state_->create_at(mem, {}); + } } template < typename... Args > @@ -5383,39 +5495,37 @@ namespace meta_hpp namespace meta_hpp::detail { template < class_kind Class > - void raw_destructor_invoke(const uarg& ptr) { + bool raw_destructor_destroy(const uarg& arg) { using dt = destructor_traits; using class_type = typename dt::class_type; - if ( !ptr.can_cast_to() ) { - throw_exception_with("an attempt to call a destructor with an incorrect argument type"); + if ( !arg.can_cast_to() ) { + return false; } - class_type* raw_ptr = ptr.cast(); - if ( raw_ptr ) { - std::unique_ptr{raw_ptr}.reset(); - } + std::unique_ptr{arg.cast()}.reset(); + return true; } template < class_kind Class > - bool raw_destructor_is_invocable_with(const uarg_base& ptr) { + void raw_destructor_destroy_at(void* mem) { using dt = destructor_traits; using class_type = typename dt::class_type; - return ptr.can_cast_to(); + std::destroy_at(static_cast(mem)); } } namespace meta_hpp::detail { template < class_kind Class > - destructor_state::invoke_impl make_destructor_invoke() { - return &raw_destructor_invoke; + destructor_state::destroy_impl make_destructor_destroy() { + return &raw_destructor_destroy; } template < class_kind Class > - destructor_state::is_invocable_with_impl make_destructor_is_invocable_with() { - return &raw_destructor_is_invocable_with; + destructor_state::destroy_at_impl make_destructor_destroy_at() { + return &raw_destructor_destroy_at; } } @@ -5426,8 +5536,8 @@ namespace meta_hpp::detail return std::make_shared(destructor_state{ .index{destructor_index::make()}, .metadata{std::move(metadata)}, - .invoke{make_destructor_invoke()}, - .is_invocable_with{make_destructor_is_invocable_with()}, + .destroy{make_destructor_destroy()}, + .destroy_at{make_destructor_destroy_at()}, }); } } @@ -5463,29 +5573,14 @@ namespace meta_hpp } template < typename Arg > - void destructor::invoke(Arg&& ptr) const { + bool destructor::destroy(Arg&& arg) const { using namespace detail; - const uarg varg{std::forward(ptr)}; - state_->invoke(varg); + const uarg varg{std::forward(arg)}; + return state_->destroy(varg); } - template < typename Arg > - void destructor::operator()(Arg&& ptr) const { - invoke(std::forward(ptr)); - } - - template < typename Arg > - bool destructor::is_invocable_with() const noexcept { - using namespace detail; - const uarg_base varg{type_list{}}; - return state_->is_invocable_with(varg); - } - - template < typename Arg > - bool destructor::is_invocable_with(Arg&& ptr) const noexcept { - using namespace detail; - const uarg_base varg{std::forward(ptr)}; - return state_->is_invocable_with(varg); + inline void destructor::destroy_at(void* mem) const { + state_->destroy_at(mem); } } @@ -6961,24 +7056,34 @@ namespace meta_hpp uvalue class_type::create(Args&&... args) const { for ( auto&& [_, ctor] : data_->constructors ) { if ( ctor.is_invocable_with(std::forward(args)...) ) { - return ctor.invoke(std::forward(args)...); + return ctor.create(std::forward(args)...); } } return uvalue{}; } template < typename... Args > - uvalue class_type::operator()(Args&&... args) const { - return create(std::forward(args)...); + uvalue class_type::create_at(void* mem, Args&&... args) const { + for ( auto&& [_, ctor] : data_->constructors ) { + if ( ctor.is_invocable_with(std::forward(args)...) ) { + return ctor.create_at(mem, std::forward(args)...); + } + } + return uvalue{}; } template < typename Arg > - bool class_type::destroy(Arg&& ptr) const { - for ( auto&& [_, dtor] : data_->destructors ) { - if ( dtor.is_invocable_with(std::forward(ptr)) ) { - dtor.invoke(std::forward(ptr)); - return true; - } + bool class_type::destroy(Arg&& arg) const { + if ( const destructor dtor = get_destructor() ) { + return dtor.destroy(std::forward(arg)); + } + return false; + } + + inline bool class_type::destroy_at(void* mem) const { + if ( const destructor dtor = get_destructor() ) { + dtor.destroy_at(mem); + return true; } return false; } diff --git a/untests/meta_base/memory_buffer_tests.cpp b/untests/meta_base/memory_buffer_tests.cpp new file mode 100644 index 0000000..4d08f65 --- /dev/null +++ b/untests/meta_base/memory_buffer_tests.cpp @@ -0,0 +1,171 @@ +/******************************************************************************* + * 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-2022, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "../meta_untests.hpp" + +namespace +{ +} + +TEST_CASE("meta/meta_base/memory_buffer") { + namespace meta = meta_hpp; + using meta::detail::memory_buffer; + + SUBCASE("ctor/0") { + memory_buffer buf; + + CHECK(!buf); + CHECK(!buf.is_valid()); + + CHECK(buf.get_size() == 0); + CHECK(buf.get_align() == std::align_val_t{}); + + CHECK(buf.get_memory() == nullptr); + CHECK(std::as_const(buf).get_memory() == nullptr); + } + + SUBCASE("ctor/1") { + memory_buffer buf1{10, std::align_val_t{32}}; + const void* buf1_memory{buf1.get_memory()}; + + memory_buffer buf2{std::move(buf1)}; + + { + CHECK(!buf1); + CHECK(!buf1.is_valid()); + + CHECK(buf1.get_size() == 0); + CHECK(buf1.get_align() == std::align_val_t{}); + + CHECK(buf1.get_memory() == nullptr); + CHECK(std::as_const(buf1).get_memory() == nullptr); + } + + { + CHECK(buf2); + CHECK(buf2.is_valid()); + + CHECK(buf2.get_size() == 10); + CHECK(buf2.get_align() == std::align_val_t{32}); + + CHECK(buf2.get_memory() == buf1_memory); + CHECK(std::as_const(buf2).get_memory() == buf1_memory); + } + } + + SUBCASE("ctor/2") { + memory_buffer buf{10, std::align_val_t{32}}; + + CHECK(buf); + CHECK(buf.is_valid()); + + CHECK(buf.get_size() == 10); + CHECK(buf.get_align() == std::align_val_t{32}); + + CHECK(buf.get_memory()); + CHECK(std::as_const(buf).get_memory()); + + { + void* aligned_ptr{buf.get_memory()}; + std::size_t aligned_size{buf.get_size()}; + CHECK(std::align( + meta::detail::to_underlying(buf.get_align()), buf.get_size(), + aligned_ptr, aligned_size) == buf.get_memory()); + } + } + + SUBCASE("operator=/0") { + memory_buffer buf1{10, std::align_val_t{32}}; + const void* buf1_memory{buf1.get_memory()}; + + memory_buffer buf2; + buf2 = std::move(buf1); + + { + CHECK(!buf1); + CHECK(!buf1.is_valid()); + + CHECK(buf1.get_size() == 0); + CHECK(buf1.get_align() == std::align_val_t{}); + + CHECK(buf1.get_memory() == nullptr); + CHECK(std::as_const(buf1).get_memory() == nullptr); + } + + { + CHECK(buf2); + CHECK(buf2.is_valid()); + + CHECK(buf2.get_size() == 10); + CHECK(buf2.get_align() == std::align_val_t{32}); + + CHECK(buf2.get_memory() == buf1_memory); + CHECK(std::as_const(buf2).get_memory() == buf1_memory); + } + } + + SUBCASE("operator=/1") { + memory_buffer buf1{10, std::align_val_t{32}}; + const void* buf1_memory{buf1.get_memory()}; + + memory_buffer buf2{20, std::align_val_t{16}}; + buf2 = std::move(buf1); + + { + CHECK(!buf1); + CHECK(!buf1.is_valid()); + + CHECK(buf1.get_size() == 0); + CHECK(buf1.get_align() == std::align_val_t{}); + + CHECK(buf1.get_memory() == nullptr); + CHECK(std::as_const(buf1).get_memory() == nullptr); + } + + { + CHECK(buf2); + CHECK(buf2.is_valid()); + + CHECK(buf2.get_size() == 10); + CHECK(buf2.get_align() == std::align_val_t{32}); + + CHECK(buf2.get_memory() == buf1_memory); + CHECK(std::as_const(buf2).get_memory() == buf1_memory); + } + } + + SUBCASE("reset") { + memory_buffer buf{10, std::align_val_t{32}}; + buf.reset(); + + CHECK(!buf); + CHECK(!buf.is_valid()); + + CHECK(buf.get_size() == 0); + CHECK(buf.get_align() == std::align_val_t{}); + + CHECK(buf.get_memory() == nullptr); + CHECK(std::as_const(buf).get_memory() == nullptr); + } + + SUBCASE("swap") { + memory_buffer buf1{10, std::align_val_t{32}}; + memory_buffer buf2{15, std::align_val_t{16}}; + + const void* buf1_memory{buf1.get_memory()}; + const void* buf2_memory{buf2.get_memory()}; + + meta::detail::swap(buf1, buf2); + + CHECK(buf1.get_size() == 15); + CHECK(buf1.get_align() == std::align_val_t{16}); + CHECK(buf1.get_memory() == buf2_memory); + + CHECK(buf2.get_size() == 10); + CHECK(buf2.get_align() == std::align_val_t{32}); + CHECK(buf2.get_memory() == buf1_memory); + } +}