mirror of
https://github.com/BlackMATov/meta.hpp.git
synced 2025-12-15 03:45:30 +07:00
rewrite uvalue without variant
This commit is contained in:
@@ -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 >
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 >
|
||||||
|
|||||||
Reference in New Issue
Block a user