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