rewrite uvalue without variant

This commit is contained in:
BlackMATov
2023-01-20 04:41:02 +07:00
parent ad133deebc
commit 544dcd3a9f
6 changed files with 659 additions and 315 deletions

View File

@@ -25,7 +25,6 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <variant>
#include <vector> #include <vector>
#if !defined(META_HPP_NO_EXCEPTIONS) && !defined(__cpp_exceptions) #if !defined(META_HPP_NO_EXCEPTIONS) && !defined(__cpp_exceptions)
@@ -2517,12 +2516,12 @@ namespace meta_hpp
template < typename T, typename... Args, typename Tp = std::decay_t<T> > template < typename T, typename... Args, typename Tp = std::decay_t<T> >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, Args...> && std::is_constructible_v<Tp, Args...>
std::decay_t<T>& emplace(Args&&... args); Tp& emplace(Args&&... args);
template < typename T, typename U, typename... Args, typename Tp = std::decay_t<T> > template < typename T, typename U, typename... Args, typename Tp = std::decay_t<T> >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, std::initializer_list<U>&, Args...> && std::is_constructible_v<Tp, std::initializer_list<U>&, Args...>
std::decay_t<T>& emplace(std::initializer_list<U> ilist, Args&&... args); Tp& emplace(std::initializer_list<U> ilist, Args&&... args);
[[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] bool is_valid() const noexcept;
[[nodiscard]] explicit operator bool() const noexcept; [[nodiscard]] explicit operator bool() const noexcept;
@@ -2565,14 +2564,36 @@ namespace meta_hpp
-> std::conditional_t<detail::pointer_kind<T>, T, const T*>; -> std::conditional_t<detail::pointer_kind<T>, T, const T*>;
private: private:
struct vtable_t; struct vtable_t;
vtable_t* vtable_{};
private: struct alignas(std::max_align_t) internal_storage_t final {
struct buffer_t final {
// NOLINTNEXTLINE(*-avoid-c-arrays) // NOLINTNEXTLINE(*-avoid-c-arrays)
alignas(std::max_align_t) std::byte data[sizeof(void*) * 2]; std::byte data[sizeof(void*) * 2];
}; };
using storage_u = std::variant<std::monostate, void*, buffer_t>;
storage_u storage_{}; struct external_storage_t final {
// NOLINTNEXTLINE(*-avoid-c-arrays)
std::byte padding[sizeof(internal_storage_t) - sizeof(void*)];
void* ptr;
};
enum class storage_e : std::uintptr_t {
nothing,
trivial,
internal,
external,
};
struct storage_u final {
union {
internal_storage_t internal;
external_storage_t external;
};
std::uintptr_t vtag;
} storage_{};
static_assert(std::is_standard_layout_v<storage_u>);
static_assert(alignof(storage_u) == alignof(std::max_align_t));
static_assert(sizeof(internal_storage_t) == sizeof(external_storage_t));
}; };
inline void swap(uvalue& l, uvalue& r) noexcept { inline void swap(uvalue& l, uvalue& r) noexcept {
@@ -8282,91 +8303,148 @@ namespace meta_hpp
struct uvalue::vtable_t final { struct uvalue::vtable_t final {
const any_type type; const any_type type;
void* (*const data)(storage_u& from) noexcept; void (*const move)(uvalue&& self, uvalue& to) noexcept;
const void* (*const cdata)(const storage_u& from) noexcept; void (*const copy)(const uvalue& self, uvalue& to);
void (*const reset)(uvalue& self) noexcept;
void (*const move)(uvalue& from, uvalue& to) noexcept; uvalue (*const deref)(const storage_u& self);
void (*const copy)(const uvalue& from, uvalue& to); uvalue (*const index)(const storage_u& self, std::size_t i);
void (*const destroy)(uvalue& self) noexcept; uvalue (*const unmap)(const storage_u& self);
uvalue (*const deref)(const storage_u& from);
uvalue (*const index)(const storage_u& from, std::size_t);
uvalue (*const unmap)(const storage_u& from);
template < typename T > template < typename T >
static T* buffer_cast(buffer_t& buffer) noexcept { inline static constexpr bool in_internal_v =
// NOLINTNEXTLINE(*-reinterpret-cast) (sizeof(T) <= sizeof(internal_storage_t)) &&
return std::launder(reinterpret_cast<T*>(buffer.data)); (alignof(internal_storage_t) % alignof(T) == 0) &&
} std::is_nothrow_destructible_v<T> &&
std::is_nothrow_move_constructible_v<T>;
template < typename T > template < typename T >
static const T* buffer_cast(const buffer_t& buffer) noexcept { inline static constexpr bool in_trivial_internal_v =
// NOLINTNEXTLINE(*-reinterpret-cast) in_internal_v<T> &&
return std::launder(reinterpret_cast<const T*>(buffer.data)); std::is_trivially_copyable_v<T>;
static std::pair<storage_e, const vtable_t*> unpack_vtag(const uvalue& self) noexcept {
constexpr std::uintptr_t tag_mask{0b11};
const std::uintptr_t vtag{self.storage_.vtag};
return std::make_pair(
static_cast<storage_e>(vtag & tag_mask),
// NOLINTNEXTLINE(*-no-int-to-ptr, *-reinterpret-cast)
reinterpret_cast<const vtable_t*>(vtag & ~tag_mask));
} }
template < typename T > template < typename T >
static T* storage_cast(storage_u& storage) noexcept { static T* storage_cast(storage_u& storage) noexcept {
return std::visit(detail::overloaded { if constexpr ( in_internal_v<T> ) {
[](void* ptr) { return static_cast<T*>(ptr); }, // NOLINTNEXTLINE(*-union-access, *-reinterpret-cast)
[](buffer_t& buffer) { return buffer_cast<T>(buffer); }, return std::launder(reinterpret_cast<T*>(storage.internal.data));
[](...) -> T* { return nullptr; }, } else {
}, storage); // NOLINTNEXTLINE(*-union-access)
return static_cast<T*>(storage.external.ptr);
}
} }
template < typename T > template < typename T >
static const T* storage_cast(const storage_u& storage) noexcept { static const T* storage_cast(const storage_u& storage) noexcept {
return std::visit(detail::overloaded { if constexpr ( in_internal_v<T> ) {
[](const void* ptr) { return static_cast<const T*>(ptr); }, // NOLINTNEXTLINE(*-union-access, *-reinterpret-cast)
[](const buffer_t& buffer) { return buffer_cast<T>(buffer); }, return std::launder(reinterpret_cast<const T*>(storage.internal.data));
[](...) -> const T* { return nullptr; }, } else {
}, storage); // NOLINTNEXTLINE(*-union-access)
return static_cast<const T*>(storage.external.ptr);
}
} }
template < typename T, typename... Args, typename Tp = std::decay_t<T> > template < typename T, typename... Args, typename Tp = std::decay_t<T> >
static Tp& construct(uvalue& dst, Args&&... args) { static Tp& do_ctor(uvalue& dst, Args&&... args) {
assert(!dst); // NOLINT assert(!dst); // NOLINT
constexpr bool in_buffer = if constexpr ( in_internal_v<Tp> ) {
(sizeof(Tp) <= sizeof(buffer_t)) &&
(alignof(buffer_t) % alignof(Tp) == 0) &&
std::is_nothrow_move_constructible_v<Tp>;
if constexpr ( in_buffer ) {
std::construct_at( std::construct_at(
buffer_cast<Tp>(dst.storage_.emplace<buffer_t>()), storage_cast<Tp>(dst.storage_),
std::forward<Args>(args)...); std::forward<Args>(args)...);
dst.storage_.vtag = in_trivial_internal_v<Tp>
? detail::to_underlying(storage_e::trivial)
: detail::to_underlying(storage_e::internal);
} else { } else {
dst.storage_.emplace<void*>( // NOLINTNEXTLINE(*-union-access, *-owning-memory)
std::make_unique<Tp>(std::forward<Args>(args)...).release()); dst.storage_.external.ptr = ::new Tp(std::forward<Args>(args)...);
dst.storage_.vtag = detail::to_underlying(storage_e::external);
} }
dst.vtable_ = vtable_t::get<Tp>(); // NOLINTNEXTLINE(*-reinterpret-cast)
dst.storage_.vtag |= reinterpret_cast<std::uintptr_t>(vtable_t::get<Tp>());
return *storage_cast<Tp>(dst.storage_); return *storage_cast<Tp>(dst.storage_);
} }
static void swap(uvalue& l, uvalue& r) noexcept { static void do_move(uvalue&& self, uvalue& to) noexcept {
assert(!to); // NOLINT
auto&& [tag, vtable] = unpack_vtag(self);
switch ( tag ) {
case storage_e::nothing:
break;
case storage_e::trivial:
to.storage_ = self.storage_;
self.storage_.vtag = 0;
break;
case storage_e::internal:
case storage_e::external:
vtable->move(std::move(self), to);
break;
}
}
static void do_copy(const uvalue& self, uvalue& to) noexcept {
assert(!to); // NOLINT
auto&& [tag, vtable] = unpack_vtag(self);
switch ( tag ) {
case storage_e::nothing:
break;
case storage_e::trivial:
to.storage_ = self.storage_;
break;
case storage_e::internal:
case storage_e::external:
vtable->copy(self, to);
break;
}
}
static void do_reset(uvalue& self) noexcept {
auto&& [tag, vtable] = unpack_vtag(self);
switch ( tag ) {
case storage_e::nothing:
break;
case storage_e::trivial:
self.storage_.vtag = 0;
break;
case storage_e::internal:
case storage_e::external:
vtable->reset(self);
break;
}
}
static void do_swap(uvalue& l, uvalue& r) noexcept {
if ( (&l == &r) || (!l && !r) ) { if ( (&l == &r) || (!l && !r) ) {
return; return;
} }
if ( l && r ) { if ( l && r ) {
if ( std::holds_alternative<buffer_t>(l.storage_) ) { if ( unpack_vtag(l).first == storage_e::external ) {
uvalue temp; r = std::exchange(l, std::move(r));
r.vtable_->move(r, temp);
l.vtable_->move(l, r);
temp.vtable_->move(temp, l);
} else { } else {
uvalue temp; l = std::exchange(r, std::move(l));
l.vtable_->move(l, temp);
r.vtable_->move(r, l);
temp.vtable_->move(temp, r);
} }
} else { } else {
if ( l ) { if ( l ) {
l.vtable_->move(l, r); r = std::move(l);
} else { } else {
r.vtable_->move(r, l); l = std::move(r);
} }
} }
} }
@@ -8379,74 +8457,54 @@ namespace meta_hpp
static vtable_t table{ static vtable_t table{
.type = resolve_type<Tp>(), .type = resolve_type<Tp>(),
.data = [](storage_u& from) noexcept -> void* { .move = [](uvalue&& self, uvalue& to) noexcept {
return storage_cast<Tp>(from); assert(self && !to); // NOLINT
Tp* src = storage_cast<Tp>(self.storage_);
if constexpr ( in_internal_v<Tp> ) {
do_ctor<Tp>(to, std::move(*src));
do_reset(self);
} else {
// NOLINTNEXTLINE(*-union-access)
to.storage_.external.ptr = src;
std::swap(to.storage_.vtag, self.storage_.vtag);
}
}, },
.cdata = [](const storage_u& from) noexcept -> const void* { .copy = [](const uvalue& self, uvalue& to){
return storage_cast<Tp>(from); assert(self && !to); // NOLINT
const Tp* src = storage_cast<Tp>(self.storage_);
if constexpr ( in_internal_v<Tp> ) {
do_ctor<Tp>(to, *src);
} else {
// NOLINTNEXTLINE(*-union-access, *-owning-memory)
to.storage_.external.ptr = ::new Tp(*src);
to.storage_.vtag = self.storage_.vtag;
}
}, },
.move = [](uvalue& from, uvalue& to) noexcept { .reset = [](uvalue& self) noexcept {
assert(from && !to); // NOLINT
std::visit(detail::overloaded {
[&to](void* ptr) {
Tp* src = static_cast<Tp*>(ptr);
to.storage_.emplace<void*>(src);
},
[&to](buffer_t& buffer) {
Tp& src = *buffer_cast<Tp>(buffer);
std::construct_at(buffer_cast<Tp>(to.storage_.emplace<buffer_t>()), std::move(src));
std::destroy_at(&src);
},
[](...){}
}, from.storage_);
to.vtable_ = from.vtable_;
from.vtable_ = nullptr;
},
.copy = [](const uvalue& from, uvalue& to){
assert(from && !to); // NOLINT
std::visit(detail::overloaded {
[&to](void* ptr) {
const Tp& src = *static_cast<const Tp*>(ptr);
to.storage_.emplace<void*>(std::make_unique<Tp>(src).release());
},
[&to](const buffer_t& buffer) {
const Tp& src = *buffer_cast<Tp>(buffer);
std::construct_at(buffer_cast<Tp>(to.storage_.emplace<buffer_t>()), src);
},
[](...){}
}, from.storage_);
to.vtable_ = from.vtable_;
},
.destroy = [](uvalue& self) noexcept {
assert(self); // NOLINT assert(self); // NOLINT
std::visit(detail::overloaded { Tp* src = storage_cast<Tp>(self.storage_);
[](void* ptr) {
Tp* src = static_cast<Tp*>(ptr);
std::unique_ptr<Tp>{src}.reset();
},
[](buffer_t& buffer) {
Tp& src = *buffer_cast<Tp>(buffer);
std::destroy_at(&src);
},
[](...){}
}, self.storage_);
self.vtable_ = nullptr; if constexpr ( in_internal_v<Tp> ) {
std::destroy_at(src);
} else {
// NOLINTNEXTLINE(*-owning-memory)
::delete src;
}
self.storage_.vtag = 0;
}, },
.deref = [](){ .deref = [](){
if constexpr ( detail::has_deref_traits<Tp> ) { if constexpr ( detail::has_deref_traits<Tp> ) {
return +[](const storage_u& from) -> uvalue { return +[](const storage_u& self) -> uvalue {
return detail::deref_traits<Tp>{}(*storage_cast<Tp>(from)); return detail::deref_traits<Tp>{}(*storage_cast<Tp>(self));
}; };
} else { } else {
return nullptr; return nullptr;
@@ -8455,8 +8513,8 @@ namespace meta_hpp
.index = [](){ .index = [](){
if constexpr ( detail::has_index_traits<Tp> ) { if constexpr ( detail::has_index_traits<Tp> ) {
return +[](const storage_u& from, std::size_t i) -> uvalue { return +[](const storage_u& self, std::size_t i) -> uvalue {
return detail::index_traits<Tp>{}(*storage_cast<Tp>(from), i); return detail::index_traits<Tp>{}(*storage_cast<Tp>(self), i);
}; };
} else { } else {
return nullptr; return nullptr;
@@ -8465,8 +8523,8 @@ namespace meta_hpp
.unmap = [](){ .unmap = [](){
if constexpr ( detail::has_unmap_traits<Tp> ) { if constexpr ( detail::has_unmap_traits<Tp> ) {
return +[](const storage_u& from) -> uvalue { return +[](const storage_u& self) -> uvalue {
return detail::unmap_traits<Tp>{}(*storage_cast<Tp>(from)); return detail::unmap_traits<Tp>{}(*storage_cast<Tp>(self));
}; };
} else { } else {
return nullptr; return nullptr;
@@ -8486,27 +8544,25 @@ namespace meta_hpp
} }
inline uvalue::uvalue(uvalue&& other) noexcept { inline uvalue::uvalue(uvalue&& other) noexcept {
if ( other.vtable_ != nullptr ) { vtable_t::do_move(std::move(other), *this);
other.vtable_->move(other, *this);
}
} }
inline uvalue::uvalue(const uvalue& other) { inline uvalue::uvalue(const uvalue& other) {
if ( other.vtable_ != nullptr ) { vtable_t::do_copy(other, *this);
other.vtable_->copy(other, *this);
}
} }
inline uvalue& uvalue::operator=(uvalue&& other) noexcept { inline uvalue& uvalue::operator=(uvalue&& other) noexcept {
if ( this != &other ) { if ( this != &other ) {
uvalue{std::move(other)}.swap(*this); vtable_t::do_reset(*this);
vtable_t::do_move(std::move(other), *this);
} }
return *this; return *this;
} }
inline uvalue& uvalue::operator=(const uvalue& other) { inline uvalue& uvalue::operator=(const uvalue& other) {
if ( this != &other ) { if ( this != &other ) {
uvalue{other}.swap(*this); vtable_t::do_reset(*this);
vtable_t::do_copy(other, *this);
} }
return *this; return *this;
} }
@@ -8517,7 +8573,7 @@ namespace meta_hpp
&& (std::is_copy_constructible_v<Tp>) && (std::is_copy_constructible_v<Tp>)
// NOLINTNEXTLINE(*-forwarding-reference-overload) // NOLINTNEXTLINE(*-forwarding-reference-overload)
uvalue::uvalue(T&& val) { uvalue::uvalue(T&& val) {
vtable_t::construct<T>(*this, std::forward<T>(val)); vtable_t::do_ctor<T>(*this, std::forward<T>(val));
} }
template < typename T, typename Tp > template < typename T, typename Tp >
@@ -8525,7 +8581,8 @@ namespace meta_hpp
&& (!detail::is_in_place_type_v<Tp>) && (!detail::is_in_place_type_v<Tp>)
&& (std::is_copy_constructible_v<Tp>) && (std::is_copy_constructible_v<Tp>)
uvalue& uvalue::operator=(T&& val) { uvalue& uvalue::operator=(T&& val) {
uvalue{std::forward<T>(val)}.swap(*this); vtable_t::do_reset(*this);
vtable_t::do_ctor<T>(*this, std::forward<T>(val));
return *this; return *this;
} }
@@ -8533,34 +8590,34 @@ namespace meta_hpp
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, Args...> && std::is_constructible_v<Tp, Args...>
uvalue::uvalue(std::in_place_type_t<T>, Args&&... args) { uvalue::uvalue(std::in_place_type_t<T>, Args&&... args) {
vtable_t::construct<T>(*this, std::forward<Args>(args)...); vtable_t::do_ctor<T>(*this, std::forward<Args>(args)...);
} }
template < typename T, typename U, typename... Args, typename Tp > template < typename T, typename U, typename... Args, typename Tp >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, std::initializer_list<U>&, Args...> && std::is_constructible_v<Tp, std::initializer_list<U>&, Args...>
uvalue::uvalue(std::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args) { uvalue::uvalue(std::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args) {
vtable_t::construct<T>(*this, ilist, std::forward<Args>(args)...); vtable_t::do_ctor<T>(*this, ilist, std::forward<Args>(args)...);
} }
template < typename T, typename... Args, typename Tp > template < typename T, typename... Args, typename Tp >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, Args...> && std::is_constructible_v<Tp, Args...>
std::decay_t<T>& uvalue::emplace(Args&&... args) { Tp& uvalue::emplace(Args&&... args) {
reset(); vtable_t::do_reset(*this);
return vtable_t::construct<T>(*this, std::forward<Args>(args)...); return vtable_t::do_ctor<T>(*this, std::forward<Args>(args)...);
} }
template < typename T, typename U, typename... Args, typename Tp > template < typename T, typename U, typename... Args, typename Tp >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, std::initializer_list<U>&, Args...> && std::is_constructible_v<Tp, std::initializer_list<U>&, Args...>
std::decay_t<T>& uvalue::emplace(std::initializer_list<U> ilist, Args&&... args) { Tp& uvalue::emplace(std::initializer_list<U> ilist, Args&&... args) {
reset(); vtable_t::do_reset(*this);
return vtable_t::construct<T>(*this, ilist, std::forward<Args>(args)...); return vtable_t::do_ctor<T>(*this, ilist, std::forward<Args>(args)...);
} }
inline bool uvalue::is_valid() const noexcept { inline bool uvalue::is_valid() const noexcept {
return vtable_ != nullptr; return storage_.vtag != 0;
} }
inline uvalue::operator bool() const noexcept { inline uvalue::operator bool() const noexcept {
@@ -8568,54 +8625,104 @@ namespace meta_hpp
} }
inline void uvalue::reset() { inline void uvalue::reset() {
if ( vtable_ != nullptr ) { vtable_t::do_reset(*this);
vtable_->destroy(*this);
}
} }
inline void uvalue::swap(uvalue& other) noexcept { inline void uvalue::swap(uvalue& other) noexcept {
vtable_t::swap(*this, other); vtable_t::do_swap(*this, other);
} }
inline const any_type& uvalue::get_type() const noexcept { inline const any_type& uvalue::get_type() const noexcept {
static any_type void_type = resolve_type<void>(); static any_type void_type = resolve_type<void>();
return vtable_ != nullptr ? vtable_->type : void_type; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag == storage_e::nothing ? void_type : vtable->type;
} }
inline void* uvalue::get_data() noexcept { inline void* uvalue::get_data() noexcept {
return vtable_ != nullptr ? vtable_->data(storage_) : nullptr; switch ( vtable_t::unpack_vtag(*this).first ) {
case storage_e::nothing:
return nullptr;
case storage_e::trivial:
case storage_e::internal:
// NOLINTNEXTLINE(*-union-access)
return static_cast<void*>(storage_.internal.data);
case storage_e::external:
// NOLINTNEXTLINE(*-union-access)
return storage_.external.ptr;
}
assert(false); // NOLINT
return nullptr;
} }
inline const void* uvalue::get_data() const noexcept { inline const void* uvalue::get_data() const noexcept {
return vtable_ != nullptr ? vtable_->cdata(storage_) : nullptr; switch ( vtable_t::unpack_vtag(*this).first ) {
case storage_e::nothing:
return nullptr;
case storage_e::trivial:
case storage_e::internal:
// NOLINTNEXTLINE(*-union-access)
return static_cast<const void*>(storage_.internal.data);
case storage_e::external:
// NOLINTNEXTLINE(*-union-access)
return storage_.external.ptr;
}
assert(false); // NOLINT
return nullptr;
} }
inline const void* uvalue::get_cdata() const noexcept { inline const void* uvalue::get_cdata() const noexcept {
return vtable_ != nullptr ? vtable_->cdata(storage_) : nullptr; switch ( vtable_t::unpack_vtag(*this).first ) {
case storage_e::nothing:
return nullptr;
case storage_e::trivial:
case storage_e::internal:
// NOLINTNEXTLINE(*-union-access)
return static_cast<const void*>(storage_.internal.data);
case storage_e::external:
// NOLINTNEXTLINE(*-union-access)
return storage_.external.ptr;
}
assert(false); // NOLINT
return nullptr;
} }
inline uvalue uvalue::operator*() const { inline uvalue uvalue::operator*() const {
return has_deref_op() ? vtable_->deref(storage_) : uvalue{}; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->deref != nullptr
? vtable->deref(storage_)
: uvalue{};
} }
inline bool uvalue::has_deref_op() const noexcept { inline bool uvalue::has_deref_op() const noexcept {
return vtable_ != nullptr && vtable_->deref != nullptr; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->deref != nullptr;
} }
inline uvalue uvalue::operator[](std::size_t index) const { inline uvalue uvalue::operator[](std::size_t index) const {
return has_index_op() ? vtable_->index(storage_, index) : uvalue{}; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->index != nullptr
? vtable->index(storage_, index)
: uvalue{};
} }
inline bool uvalue::has_index_op() const noexcept { inline bool uvalue::has_index_op() const noexcept {
return vtable_ != nullptr && vtable_->index != nullptr; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->index != nullptr;
} }
inline uvalue uvalue::unmap() const { inline uvalue uvalue::unmap() const {
return has_unmap_op() ? vtable_->unmap(storage_) : uvalue{}; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->unmap != nullptr
? vtable->unmap(storage_)
: uvalue{};
} }
inline bool uvalue::has_unmap_op() const noexcept { inline bool uvalue::has_unmap_op() const noexcept {
return vtable_ != nullptr && vtable_->unmap != nullptr; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->unmap != nullptr;
} }
template < typename T > template < typename T >

View File

@@ -116,7 +116,7 @@ TEST_CASE("meta/meta_utilities/value5/inplace") {
} }
SUBCASE("args") { SUBCASE("args") {
meta::uvalue v = meta::make_uvalue<std::vector<clazz_throw_dtor>>(2, 42); meta::uvalue v = meta::make_uvalue<std::vector<clazz_throw_dtor>>(2u, 42);
CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>()); CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>());
CHECK(v[0].get_type() == meta::resolve_type<clazz_throw_dtor>()); CHECK(v[0].get_type() == meta::resolve_type<clazz_throw_dtor>());
@@ -128,7 +128,7 @@ TEST_CASE("meta/meta_utilities/value5/inplace") {
SUBCASE("args/counters") { SUBCASE("args/counters") {
{ {
meta::uvalue v = meta::make_uvalue<std::vector<clazz_throw_dtor>>(2, 42); meta::uvalue v = meta::make_uvalue<std::vector<clazz_throw_dtor>>(2u, 42);
CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>()); CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>());
} }
CHECK(clazz_throw_dtor::destructor_counter == 3); CHECK(clazz_throw_dtor::destructor_counter == 3);
@@ -196,7 +196,7 @@ TEST_CASE("meta/meta_utilities/value5/emplace") {
SUBCASE("args") { SUBCASE("args") {
meta::uvalue v = meta::make_uvalue<clazz_throw_dtor>(21); meta::uvalue v = meta::make_uvalue<clazz_throw_dtor>(21);
v.emplace<std::vector<clazz_throw_dtor>>(2, 42); v.emplace<std::vector<clazz_throw_dtor>>(2u, 42);
CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>()); CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>());
CHECK(v[0].get_type() == meta::resolve_type<clazz_throw_dtor>()); CHECK(v[0].get_type() == meta::resolve_type<clazz_throw_dtor>());
@@ -209,7 +209,7 @@ TEST_CASE("meta/meta_utilities/value5/emplace") {
SUBCASE("args/counters") { SUBCASE("args/counters") {
{ {
meta::uvalue v = meta::make_uvalue<clazz_throw_dtor>(21); meta::uvalue v = meta::make_uvalue<clazz_throw_dtor>(21);
v.emplace<std::vector<clazz_throw_dtor>>(2, 42); v.emplace<std::vector<clazz_throw_dtor>>(2u, 42);
CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>()); CHECK(v.get_type() == meta::resolve_type<std::vector<clazz_throw_dtor>>());
} }
CHECK(clazz_throw_dtor::destructor_counter == 4); CHECK(clazz_throw_dtor::destructor_counter == 4);

View File

@@ -42,8 +42,8 @@ namespace
ivec2& operator=(ivec2&& other) = delete; ivec2& operator=(ivec2&& other) = delete;
ivec2& operator=(const ivec2& other) = delete; ivec2& operator=(const ivec2& other) = delete;
public: public:
static int move_constructor_counter; inline static int move_constructor_counter{};
static int copy_constructor_counter; inline static int copy_constructor_counter{};
}; };
struct ivec3 { struct ivec3 {
@@ -57,8 +57,43 @@ namespace
ivec3(int nx, int ny, int nz): x{nx}, y{ny}, z{nz} {} ivec3(int nx, int ny, int nz): x{nx}, y{ny}, z{nz} {}
}; };
int ivec2::move_constructor_counter{0}; struct ivec2_big {
int ivec2::copy_constructor_counter{0}; int x{};
int y{};
int dummy[42]{};
ivec2_big() = delete;
explicit ivec2_big(int nv): x{nv}, y{nv} {}
ivec2_big(int nx, int ny): x{nx}, y{ny} {}
ivec2_big(ivec2_big&& other) noexcept
: x{other.x}
, y{other.y} {
other.x = 0;
other.y = 0;
++move_constructor_counter;
}
ivec2_big(const ivec2_big& other) noexcept
: x{other.x}
, y{other.y} {
++copy_constructor_counter;
}
ivec2_big& add(const ivec2_big& other) {
x += other.x;
y += other.y;
return *this;
}
ivec2_big& operator=(ivec2_big&& other) = delete;
ivec2_big& operator=(const ivec2_big& other) = delete;
public:
inline static int move_constructor_counter{};
inline static int copy_constructor_counter{};
};
ivec2 iadd2(ivec2 l, ivec2 r) { ivec2 iadd2(ivec2 l, ivec2 r) {
return {l.x + r.x, l.y + r.y}; return {l.x + r.x, l.y + r.y};
@@ -67,6 +102,10 @@ namespace
bool operator==(const ivec2& l, const ivec2& r) noexcept { bool operator==(const ivec2& l, const ivec2& r) noexcept {
return l.x == r.x && l.y == r.y; return l.x == r.x && l.y == r.y;
} }
bool operator==(const ivec2_big& l, const ivec2_big& r) noexcept {
return l.x == r.x && l.y == r.y;
}
} }
TEST_CASE("meta/meta_utilities/value/_") { TEST_CASE("meta/meta_utilities/value/_") {
@@ -96,6 +135,8 @@ TEST_CASE("meta/meta_utilities/value") {
ivec2::move_constructor_counter = 0; ivec2::move_constructor_counter = 0;
ivec2::copy_constructor_counter = 0; ivec2::copy_constructor_counter = 0;
ivec2_big::move_constructor_counter = 0;
ivec2_big::copy_constructor_counter = 0;
SUBCASE("cast types") { SUBCASE("cast types") {
static_assert(std::is_same_v< static_assert(std::is_same_v<
@@ -339,7 +380,7 @@ TEST_CASE("meta/meta_utilities/value") {
val_dst = std::move(val_src2); val_dst = std::move(val_src2);
CHECK(val_dst.get_as<ivec2>() == ivec2{1,2}); CHECK(val_dst.get_as<ivec2>() == ivec2{1,2});
CHECK(ivec2::move_constructor_counter == 3); CHECK(ivec2::move_constructor_counter == 2);
CHECK(ivec2::copy_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 0);
} }
@@ -358,14 +399,14 @@ TEST_CASE("meta/meta_utilities/value") {
val_dst = val_src2; val_dst = val_src2;
CHECK(val_dst.get_as<ivec2>() == ivec2{1,2}); CHECK(val_dst.get_as<ivec2>() == ivec2{1,2});
CHECK(ivec2::move_constructor_counter == 2); CHECK(ivec2::move_constructor_counter == 1);
CHECK(ivec2::copy_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 1);
CHECK(val_src2.get_as<ivec2>() == ivec2{1,2}); CHECK(val_src2.get_as<ivec2>() == ivec2{1,2});
CHECK(val_src2.get_data() != val_dst.get_data()); CHECK(val_src2.get_data() != val_dst.get_data());
} }
SUBCASE("swap") { SUBCASE("swap/0") {
meta::uvalue val1{"world"s}; meta::uvalue val1{"world"s};
meta::uvalue val2{ivec2{1,2}}; meta::uvalue val2{ivec2{1,2}};
CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::move_constructor_counter == 1);
@@ -382,6 +423,95 @@ TEST_CASE("meta/meta_utilities/value") {
CHECK(val2.get_as<ivec2>() == ivec2{1,2}); CHECK(val2.get_as<ivec2>() == ivec2{1,2});
} }
SUBCASE("swap/1") {
meta::uvalue val1{42};
meta::uvalue val2{};
swap(val1, val2);
CHECK_FALSE(val1);
CHECK(val2.get_as<int>() == 42);
swap(val1, val2);
CHECK(val1.get_as<int>() == 42);
CHECK_FALSE(val2);
}
SUBCASE("swap/2") {
meta::uvalue val1{ivec2{1,2}};
meta::uvalue val2{};
CHECK(ivec2::move_constructor_counter == 1);
CHECK(ivec2::copy_constructor_counter == 0);
swap(val1, val2);
CHECK_FALSE(val1);
CHECK(val2.get_as<ivec2>() == ivec2{1,2});
CHECK(ivec2::move_constructor_counter == 2);
CHECK(ivec2::copy_constructor_counter == 0);
swap(val1, val2);
CHECK(val1.get_as<ivec2>() == ivec2{1,2});
CHECK_FALSE(val2);
CHECK(ivec2::move_constructor_counter == 3);
CHECK(ivec2::copy_constructor_counter == 0);
}
SUBCASE("swap/3") {
meta::uvalue val1{ivec2_big{1,2}};
meta::uvalue val2{};
CHECK(ivec2_big::move_constructor_counter == 1);
CHECK(ivec2_big::copy_constructor_counter == 0);
swap(val1, val2);
CHECK_FALSE(val1);
CHECK(val2.get_as<ivec2_big>() == ivec2_big{1,2});
CHECK(ivec2_big::move_constructor_counter == 1);
CHECK(ivec2_big::copy_constructor_counter == 0);
swap(val1, val2);
CHECK(val1.get_as<ivec2_big>() == ivec2_big{1,2});
CHECK_FALSE(val2);
CHECK(ivec2_big::move_constructor_counter == 1);
CHECK(ivec2_big::copy_constructor_counter == 0);
}
SUBCASE("after_move") {
{
meta::uvalue val1{42};
meta::uvalue val2{std::move(val1)};
CHECK_FALSE(val1);
CHECK(val2.get_as<int>() == 42);
val1 = std::move(val2);
CHECK_FALSE(val2);
CHECK(val1.get_as<int>() == 42);
}
{
meta::uvalue val1{ivec2{1,2}};
meta::uvalue val2{std::move(val1)};
CHECK_FALSE(val1);
CHECK(val2);
CHECK(val2.get_as<ivec2>() == ivec2{1,2});
val1 = std::move(val2);
CHECK_FALSE(val2);
CHECK(val1.get_as<ivec2>() == ivec2{1,2});
}
{
meta::uvalue val1{ivec2_big{1,2}};
meta::uvalue val2{std::move(val1)};
CHECK_FALSE(val1);
CHECK(val2);
CHECK(val2.get_as<ivec2_big>() == ivec2_big{1,2});
val1 = std::move(val2);
CHECK_FALSE(val2);
CHECK(val1.get_as<ivec2_big>() == ivec2_big{1,2});
}
}
SUBCASE("unmap") { SUBCASE("unmap") {
{ {
const meta::uvalue u{42}; const meta::uvalue u{42};

View File

@@ -36,7 +36,6 @@
#include <tuple> #include <tuple>
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#include <variant>
#include <vector> #include <vector>
#if !defined(META_HPP_NO_EXCEPTIONS) #if !defined(META_HPP_NO_EXCEPTIONS)

View File

@@ -63,12 +63,12 @@ namespace meta_hpp
template < typename T, typename... Args, typename Tp = std::decay_t<T> > template < typename T, typename... Args, typename Tp = std::decay_t<T> >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, Args...> && std::is_constructible_v<Tp, Args...>
std::decay_t<T>& emplace(Args&&... args); Tp& emplace(Args&&... args);
template < typename T, typename U, typename... Args, typename Tp = std::decay_t<T> > template < typename T, typename U, typename... Args, typename Tp = std::decay_t<T> >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, std::initializer_list<U>&, Args...> && std::is_constructible_v<Tp, std::initializer_list<U>&, Args...>
std::decay_t<T>& emplace(std::initializer_list<U> ilist, Args&&... args); Tp& emplace(std::initializer_list<U> ilist, Args&&... args);
[[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] bool is_valid() const noexcept;
[[nodiscard]] explicit operator bool() const noexcept; [[nodiscard]] explicit operator bool() const noexcept;
@@ -111,14 +111,36 @@ namespace meta_hpp
-> std::conditional_t<detail::pointer_kind<T>, T, const T*>; -> std::conditional_t<detail::pointer_kind<T>, T, const T*>;
private: private:
struct vtable_t; struct vtable_t;
vtable_t* vtable_{};
private: struct alignas(std::max_align_t) internal_storage_t final {
struct buffer_t final {
// NOLINTNEXTLINE(*-avoid-c-arrays) // NOLINTNEXTLINE(*-avoid-c-arrays)
alignas(std::max_align_t) std::byte data[sizeof(void*) * 2]; std::byte data[sizeof(void*) * 2];
}; };
using storage_u = std::variant<std::monostate, void*, buffer_t>;
storage_u storage_{}; struct external_storage_t final {
// NOLINTNEXTLINE(*-avoid-c-arrays)
std::byte padding[sizeof(internal_storage_t) - sizeof(void*)];
void* ptr;
};
enum class storage_e : std::uintptr_t {
nothing,
trivial,
internal,
external,
};
struct storage_u final {
union {
internal_storage_t internal;
external_storage_t external;
};
std::uintptr_t vtag;
} storage_{};
static_assert(std::is_standard_layout_v<storage_u>);
static_assert(alignof(storage_u) == alignof(std::max_align_t));
static_assert(sizeof(internal_storage_t) == sizeof(external_storage_t));
}; };
inline void swap(uvalue& l, uvalue& r) noexcept { inline void swap(uvalue& l, uvalue& r) noexcept {

View File

@@ -21,91 +21,148 @@ namespace meta_hpp
struct uvalue::vtable_t final { struct uvalue::vtable_t final {
const any_type type; const any_type type;
void* (*const data)(storage_u& from) noexcept; void (*const move)(uvalue&& self, uvalue& to) noexcept;
const void* (*const cdata)(const storage_u& from) noexcept; void (*const copy)(const uvalue& self, uvalue& to);
void (*const reset)(uvalue& self) noexcept;
void (*const move)(uvalue& from, uvalue& to) noexcept; uvalue (*const deref)(const storage_u& self);
void (*const copy)(const uvalue& from, uvalue& to); uvalue (*const index)(const storage_u& self, std::size_t i);
void (*const destroy)(uvalue& self) noexcept; uvalue (*const unmap)(const storage_u& self);
uvalue (*const deref)(const storage_u& from);
uvalue (*const index)(const storage_u& from, std::size_t);
uvalue (*const unmap)(const storage_u& from);
template < typename T > template < typename T >
static T* buffer_cast(buffer_t& buffer) noexcept { inline static constexpr bool in_internal_v =
// NOLINTNEXTLINE(*-reinterpret-cast) (sizeof(T) <= sizeof(internal_storage_t)) &&
return std::launder(reinterpret_cast<T*>(buffer.data)); (alignof(internal_storage_t) % alignof(T) == 0) &&
} std::is_nothrow_destructible_v<T> &&
std::is_nothrow_move_constructible_v<T>;
template < typename T > template < typename T >
static const T* buffer_cast(const buffer_t& buffer) noexcept { inline static constexpr bool in_trivial_internal_v =
// NOLINTNEXTLINE(*-reinterpret-cast) in_internal_v<T> &&
return std::launder(reinterpret_cast<const T*>(buffer.data)); std::is_trivially_copyable_v<T>;
static std::pair<storage_e, const vtable_t*> unpack_vtag(const uvalue& self) noexcept {
constexpr std::uintptr_t tag_mask{0b11};
const std::uintptr_t vtag{self.storage_.vtag};
return std::make_pair(
static_cast<storage_e>(vtag & tag_mask),
// NOLINTNEXTLINE(*-no-int-to-ptr, *-reinterpret-cast)
reinterpret_cast<const vtable_t*>(vtag & ~tag_mask));
} }
template < typename T > template < typename T >
static T* storage_cast(storage_u& storage) noexcept { static T* storage_cast(storage_u& storage) noexcept {
return std::visit(detail::overloaded { if constexpr ( in_internal_v<T> ) {
[](void* ptr) { return static_cast<T*>(ptr); }, // NOLINTNEXTLINE(*-union-access, *-reinterpret-cast)
[](buffer_t& buffer) { return buffer_cast<T>(buffer); }, return std::launder(reinterpret_cast<T*>(storage.internal.data));
[](...) -> T* { return nullptr; }, } else {
}, storage); // NOLINTNEXTLINE(*-union-access)
return static_cast<T*>(storage.external.ptr);
}
} }
template < typename T > template < typename T >
static const T* storage_cast(const storage_u& storage) noexcept { static const T* storage_cast(const storage_u& storage) noexcept {
return std::visit(detail::overloaded { if constexpr ( in_internal_v<T> ) {
[](const void* ptr) { return static_cast<const T*>(ptr); }, // NOLINTNEXTLINE(*-union-access, *-reinterpret-cast)
[](const buffer_t& buffer) { return buffer_cast<T>(buffer); }, return std::launder(reinterpret_cast<const T*>(storage.internal.data));
[](...) -> const T* { return nullptr; }, } else {
}, storage); // NOLINTNEXTLINE(*-union-access)
return static_cast<const T*>(storage.external.ptr);
}
} }
template < typename T, typename... Args, typename Tp = std::decay_t<T> > template < typename T, typename... Args, typename Tp = std::decay_t<T> >
static Tp& construct(uvalue& dst, Args&&... args) { static Tp& do_ctor(uvalue& dst, Args&&... args) {
assert(!dst); // NOLINT assert(!dst); // NOLINT
constexpr bool in_buffer = if constexpr ( in_internal_v<Tp> ) {
(sizeof(Tp) <= sizeof(buffer_t)) &&
(alignof(buffer_t) % alignof(Tp) == 0) &&
std::is_nothrow_move_constructible_v<Tp>;
if constexpr ( in_buffer ) {
std::construct_at( std::construct_at(
buffer_cast<Tp>(dst.storage_.emplace<buffer_t>()), storage_cast<Tp>(dst.storage_),
std::forward<Args>(args)...); std::forward<Args>(args)...);
dst.storage_.vtag = in_trivial_internal_v<Tp>
? detail::to_underlying(storage_e::trivial)
: detail::to_underlying(storage_e::internal);
} else { } else {
dst.storage_.emplace<void*>( // NOLINTNEXTLINE(*-union-access, *-owning-memory)
std::make_unique<Tp>(std::forward<Args>(args)...).release()); dst.storage_.external.ptr = ::new Tp(std::forward<Args>(args)...);
dst.storage_.vtag = detail::to_underlying(storage_e::external);
} }
dst.vtable_ = vtable_t::get<Tp>(); // NOLINTNEXTLINE(*-reinterpret-cast)
dst.storage_.vtag |= reinterpret_cast<std::uintptr_t>(vtable_t::get<Tp>());
return *storage_cast<Tp>(dst.storage_); return *storage_cast<Tp>(dst.storage_);
} }
static void swap(uvalue& l, uvalue& r) noexcept { static void do_move(uvalue&& self, uvalue& to) noexcept {
assert(!to); // NOLINT
auto&& [tag, vtable] = unpack_vtag(self);
switch ( tag ) {
case storage_e::nothing:
break;
case storage_e::trivial:
to.storage_ = self.storage_;
self.storage_.vtag = 0;
break;
case storage_e::internal:
case storage_e::external:
vtable->move(std::move(self), to);
break;
}
}
static void do_copy(const uvalue& self, uvalue& to) noexcept {
assert(!to); // NOLINT
auto&& [tag, vtable] = unpack_vtag(self);
switch ( tag ) {
case storage_e::nothing:
break;
case storage_e::trivial:
to.storage_ = self.storage_;
break;
case storage_e::internal:
case storage_e::external:
vtable->copy(self, to);
break;
}
}
static void do_reset(uvalue& self) noexcept {
auto&& [tag, vtable] = unpack_vtag(self);
switch ( tag ) {
case storage_e::nothing:
break;
case storage_e::trivial:
self.storage_.vtag = 0;
break;
case storage_e::internal:
case storage_e::external:
vtable->reset(self);
break;
}
}
static void do_swap(uvalue& l, uvalue& r) noexcept {
if ( (&l == &r) || (!l && !r) ) { if ( (&l == &r) || (!l && !r) ) {
return; return;
} }
if ( l && r ) { if ( l && r ) {
if ( std::holds_alternative<buffer_t>(l.storage_) ) { if ( unpack_vtag(l).first == storage_e::external ) {
uvalue temp; r = std::exchange(l, std::move(r));
r.vtable_->move(r, temp);
l.vtable_->move(l, r);
temp.vtable_->move(temp, l);
} else { } else {
uvalue temp; l = std::exchange(r, std::move(l));
l.vtable_->move(l, temp);
r.vtable_->move(r, l);
temp.vtable_->move(temp, r);
} }
} else { } else {
if ( l ) { if ( l ) {
l.vtable_->move(l, r); r = std::move(l);
} else { } else {
r.vtable_->move(r, l); l = std::move(r);
} }
} }
} }
@@ -118,74 +175,54 @@ namespace meta_hpp
static vtable_t table{ static vtable_t table{
.type = resolve_type<Tp>(), .type = resolve_type<Tp>(),
.data = [](storage_u& from) noexcept -> void* { .move = [](uvalue&& self, uvalue& to) noexcept {
return storage_cast<Tp>(from); assert(self && !to); // NOLINT
Tp* src = storage_cast<Tp>(self.storage_);
if constexpr ( in_internal_v<Tp> ) {
do_ctor<Tp>(to, std::move(*src));
do_reset(self);
} else {
// NOLINTNEXTLINE(*-union-access)
to.storage_.external.ptr = src;
std::swap(to.storage_.vtag, self.storage_.vtag);
}
}, },
.cdata = [](const storage_u& from) noexcept -> const void* { .copy = [](const uvalue& self, uvalue& to){
return storage_cast<Tp>(from); assert(self && !to); // NOLINT
const Tp* src = storage_cast<Tp>(self.storage_);
if constexpr ( in_internal_v<Tp> ) {
do_ctor<Tp>(to, *src);
} else {
// NOLINTNEXTLINE(*-union-access, *-owning-memory)
to.storage_.external.ptr = ::new Tp(*src);
to.storage_.vtag = self.storage_.vtag;
}
}, },
.move = [](uvalue& from, uvalue& to) noexcept { .reset = [](uvalue& self) noexcept {
assert(from && !to); // NOLINT
std::visit(detail::overloaded {
[&to](void* ptr) {
Tp* src = static_cast<Tp*>(ptr);
to.storage_.emplace<void*>(src);
},
[&to](buffer_t& buffer) {
Tp& src = *buffer_cast<Tp>(buffer);
std::construct_at(buffer_cast<Tp>(to.storage_.emplace<buffer_t>()), std::move(src));
std::destroy_at(&src);
},
[](...){}
}, from.storage_);
to.vtable_ = from.vtable_;
from.vtable_ = nullptr;
},
.copy = [](const uvalue& from, uvalue& to){
assert(from && !to); // NOLINT
std::visit(detail::overloaded {
[&to](void* ptr) {
const Tp& src = *static_cast<const Tp*>(ptr);
to.storage_.emplace<void*>(std::make_unique<Tp>(src).release());
},
[&to](const buffer_t& buffer) {
const Tp& src = *buffer_cast<Tp>(buffer);
std::construct_at(buffer_cast<Tp>(to.storage_.emplace<buffer_t>()), src);
},
[](...){}
}, from.storage_);
to.vtable_ = from.vtable_;
},
.destroy = [](uvalue& self) noexcept {
assert(self); // NOLINT assert(self); // NOLINT
std::visit(detail::overloaded { Tp* src = storage_cast<Tp>(self.storage_);
[](void* ptr) {
Tp* src = static_cast<Tp*>(ptr);
std::unique_ptr<Tp>{src}.reset();
},
[](buffer_t& buffer) {
Tp& src = *buffer_cast<Tp>(buffer);
std::destroy_at(&src);
},
[](...){}
}, self.storage_);
self.vtable_ = nullptr; if constexpr ( in_internal_v<Tp> ) {
std::destroy_at(src);
} else {
// NOLINTNEXTLINE(*-owning-memory)
::delete src;
}
self.storage_.vtag = 0;
}, },
.deref = [](){ .deref = [](){
if constexpr ( detail::has_deref_traits<Tp> ) { if constexpr ( detail::has_deref_traits<Tp> ) {
return +[](const storage_u& from) -> uvalue { return +[](const storage_u& self) -> uvalue {
return detail::deref_traits<Tp>{}(*storage_cast<Tp>(from)); return detail::deref_traits<Tp>{}(*storage_cast<Tp>(self));
}; };
} else { } else {
return nullptr; return nullptr;
@@ -194,8 +231,8 @@ namespace meta_hpp
.index = [](){ .index = [](){
if constexpr ( detail::has_index_traits<Tp> ) { if constexpr ( detail::has_index_traits<Tp> ) {
return +[](const storage_u& from, std::size_t i) -> uvalue { return +[](const storage_u& self, std::size_t i) -> uvalue {
return detail::index_traits<Tp>{}(*storage_cast<Tp>(from), i); return detail::index_traits<Tp>{}(*storage_cast<Tp>(self), i);
}; };
} else { } else {
return nullptr; return nullptr;
@@ -204,8 +241,8 @@ namespace meta_hpp
.unmap = [](){ .unmap = [](){
if constexpr ( detail::has_unmap_traits<Tp> ) { if constexpr ( detail::has_unmap_traits<Tp> ) {
return +[](const storage_u& from) -> uvalue { return +[](const storage_u& self) -> uvalue {
return detail::unmap_traits<Tp>{}(*storage_cast<Tp>(from)); return detail::unmap_traits<Tp>{}(*storage_cast<Tp>(self));
}; };
} else { } else {
return nullptr; return nullptr;
@@ -225,27 +262,25 @@ namespace meta_hpp
} }
inline uvalue::uvalue(uvalue&& other) noexcept { inline uvalue::uvalue(uvalue&& other) noexcept {
if ( other.vtable_ != nullptr ) { vtable_t::do_move(std::move(other), *this);
other.vtable_->move(other, *this);
}
} }
inline uvalue::uvalue(const uvalue& other) { inline uvalue::uvalue(const uvalue& other) {
if ( other.vtable_ != nullptr ) { vtable_t::do_copy(other, *this);
other.vtable_->copy(other, *this);
}
} }
inline uvalue& uvalue::operator=(uvalue&& other) noexcept { inline uvalue& uvalue::operator=(uvalue&& other) noexcept {
if ( this != &other ) { if ( this != &other ) {
uvalue{std::move(other)}.swap(*this); vtable_t::do_reset(*this);
vtable_t::do_move(std::move(other), *this);
} }
return *this; return *this;
} }
inline uvalue& uvalue::operator=(const uvalue& other) { inline uvalue& uvalue::operator=(const uvalue& other) {
if ( this != &other ) { if ( this != &other ) {
uvalue{other}.swap(*this); vtable_t::do_reset(*this);
vtable_t::do_copy(other, *this);
} }
return *this; return *this;
} }
@@ -256,7 +291,7 @@ namespace meta_hpp
&& (std::is_copy_constructible_v<Tp>) && (std::is_copy_constructible_v<Tp>)
// NOLINTNEXTLINE(*-forwarding-reference-overload) // NOLINTNEXTLINE(*-forwarding-reference-overload)
uvalue::uvalue(T&& val) { uvalue::uvalue(T&& val) {
vtable_t::construct<T>(*this, std::forward<T>(val)); vtable_t::do_ctor<T>(*this, std::forward<T>(val));
} }
template < typename T, typename Tp > template < typename T, typename Tp >
@@ -264,7 +299,8 @@ namespace meta_hpp
&& (!detail::is_in_place_type_v<Tp>) && (!detail::is_in_place_type_v<Tp>)
&& (std::is_copy_constructible_v<Tp>) && (std::is_copy_constructible_v<Tp>)
uvalue& uvalue::operator=(T&& val) { uvalue& uvalue::operator=(T&& val) {
uvalue{std::forward<T>(val)}.swap(*this); vtable_t::do_reset(*this);
vtable_t::do_ctor<T>(*this, std::forward<T>(val));
return *this; return *this;
} }
@@ -272,34 +308,34 @@ namespace meta_hpp
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, Args...> && std::is_constructible_v<Tp, Args...>
uvalue::uvalue(std::in_place_type_t<T>, Args&&... args) { uvalue::uvalue(std::in_place_type_t<T>, Args&&... args) {
vtable_t::construct<T>(*this, std::forward<Args>(args)...); vtable_t::do_ctor<T>(*this, std::forward<Args>(args)...);
} }
template < typename T, typename U, typename... Args, typename Tp > template < typename T, typename U, typename... Args, typename Tp >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, std::initializer_list<U>&, Args...> && std::is_constructible_v<Tp, std::initializer_list<U>&, Args...>
uvalue::uvalue(std::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args) { uvalue::uvalue(std::in_place_type_t<T>, std::initializer_list<U> ilist, Args&&... args) {
vtable_t::construct<T>(*this, ilist, std::forward<Args>(args)...); vtable_t::do_ctor<T>(*this, ilist, std::forward<Args>(args)...);
} }
template < typename T, typename... Args, typename Tp > template < typename T, typename... Args, typename Tp >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, Args...> && std::is_constructible_v<Tp, Args...>
std::decay_t<T>& uvalue::emplace(Args&&... args) { Tp& uvalue::emplace(Args&&... args) {
reset(); vtable_t::do_reset(*this);
return vtable_t::construct<T>(*this, std::forward<Args>(args)...); return vtable_t::do_ctor<T>(*this, std::forward<Args>(args)...);
} }
template < typename T, typename U, typename... Args, typename Tp > template < typename T, typename U, typename... Args, typename Tp >
requires std::is_copy_constructible_v<Tp> requires std::is_copy_constructible_v<Tp>
&& std::is_constructible_v<Tp, std::initializer_list<U>&, Args...> && std::is_constructible_v<Tp, std::initializer_list<U>&, Args...>
std::decay_t<T>& uvalue::emplace(std::initializer_list<U> ilist, Args&&... args) { Tp& uvalue::emplace(std::initializer_list<U> ilist, Args&&... args) {
reset(); vtable_t::do_reset(*this);
return vtable_t::construct<T>(*this, ilist, std::forward<Args>(args)...); return vtable_t::do_ctor<T>(*this, ilist, std::forward<Args>(args)...);
} }
inline bool uvalue::is_valid() const noexcept { inline bool uvalue::is_valid() const noexcept {
return vtable_ != nullptr; return storage_.vtag != 0;
} }
inline uvalue::operator bool() const noexcept { inline uvalue::operator bool() const noexcept {
@@ -307,54 +343,104 @@ namespace meta_hpp
} }
inline void uvalue::reset() { inline void uvalue::reset() {
if ( vtable_ != nullptr ) { vtable_t::do_reset(*this);
vtable_->destroy(*this);
}
} }
inline void uvalue::swap(uvalue& other) noexcept { inline void uvalue::swap(uvalue& other) noexcept {
vtable_t::swap(*this, other); vtable_t::do_swap(*this, other);
} }
inline const any_type& uvalue::get_type() const noexcept { inline const any_type& uvalue::get_type() const noexcept {
static any_type void_type = resolve_type<void>(); static any_type void_type = resolve_type<void>();
return vtable_ != nullptr ? vtable_->type : void_type; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag == storage_e::nothing ? void_type : vtable->type;
} }
inline void* uvalue::get_data() noexcept { inline void* uvalue::get_data() noexcept {
return vtable_ != nullptr ? vtable_->data(storage_) : nullptr; switch ( vtable_t::unpack_vtag(*this).first ) {
case storage_e::nothing:
return nullptr;
case storage_e::trivial:
case storage_e::internal:
// NOLINTNEXTLINE(*-union-access)
return static_cast<void*>(storage_.internal.data);
case storage_e::external:
// NOLINTNEXTLINE(*-union-access)
return storage_.external.ptr;
}
assert(false); // NOLINT
return nullptr;
} }
inline const void* uvalue::get_data() const noexcept { inline const void* uvalue::get_data() const noexcept {
return vtable_ != nullptr ? vtable_->cdata(storage_) : nullptr; switch ( vtable_t::unpack_vtag(*this).first ) {
case storage_e::nothing:
return nullptr;
case storage_e::trivial:
case storage_e::internal:
// NOLINTNEXTLINE(*-union-access)
return static_cast<const void*>(storage_.internal.data);
case storage_e::external:
// NOLINTNEXTLINE(*-union-access)
return storage_.external.ptr;
}
assert(false); // NOLINT
return nullptr;
} }
inline const void* uvalue::get_cdata() const noexcept { inline const void* uvalue::get_cdata() const noexcept {
return vtable_ != nullptr ? vtable_->cdata(storage_) : nullptr; switch ( vtable_t::unpack_vtag(*this).first ) {
case storage_e::nothing:
return nullptr;
case storage_e::trivial:
case storage_e::internal:
// NOLINTNEXTLINE(*-union-access)
return static_cast<const void*>(storage_.internal.data);
case storage_e::external:
// NOLINTNEXTLINE(*-union-access)
return storage_.external.ptr;
}
assert(false); // NOLINT
return nullptr;
} }
inline uvalue uvalue::operator*() const { inline uvalue uvalue::operator*() const {
return has_deref_op() ? vtable_->deref(storage_) : uvalue{}; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->deref != nullptr
? vtable->deref(storage_)
: uvalue{};
} }
inline bool uvalue::has_deref_op() const noexcept { inline bool uvalue::has_deref_op() const noexcept {
return vtable_ != nullptr && vtable_->deref != nullptr; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->deref != nullptr;
} }
inline uvalue uvalue::operator[](std::size_t index) const { inline uvalue uvalue::operator[](std::size_t index) const {
return has_index_op() ? vtable_->index(storage_, index) : uvalue{}; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->index != nullptr
? vtable->index(storage_, index)
: uvalue{};
} }
inline bool uvalue::has_index_op() const noexcept { inline bool uvalue::has_index_op() const noexcept {
return vtable_ != nullptr && vtable_->index != nullptr; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->index != nullptr;
} }
inline uvalue uvalue::unmap() const { inline uvalue uvalue::unmap() const {
return has_unmap_op() ? vtable_->unmap(storage_) : uvalue{}; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->unmap != nullptr
? vtable->unmap(storage_)
: uvalue{};
} }
inline bool uvalue::has_unmap_op() const noexcept { inline bool uvalue::has_unmap_op() const noexcept {
return vtable_ != nullptr && vtable_->unmap != nullptr; auto&& [tag, vtable] = vtable_t::unpack_vtag(*this);
return tag != storage_e::nothing && vtable->unmap != nullptr;
} }
template < typename T > template < typename T >