safe deref/index uvalue's operators

This commit is contained in:
BlackMATov
2023-01-18 02:58:06 +07:00
parent 0326ac3188
commit 702349dca9
7 changed files with 217 additions and 71 deletions

View File

@@ -12,6 +12,7 @@
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <cstdlib> #include <cstdlib>
#include <deque>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <map> #include <map>
@@ -2536,7 +2537,10 @@ namespace meta_hpp
[[nodiscard]] const void* get_cdata() const noexcept; [[nodiscard]] const void* get_cdata() const noexcept;
[[nodiscard]] uvalue operator*() const; [[nodiscard]] uvalue operator*() const;
[[nodiscard]] bool has_deref_op() const noexcept;
[[nodiscard]] uvalue operator[](std::size_t index) const; [[nodiscard]] uvalue operator[](std::size_t index) const;
[[nodiscard]] bool has_index_op() const noexcept;
template < typename T > template < typename T >
[[nodiscard]] T get_as() &&; [[nodiscard]] T get_as() &&;
@@ -8134,7 +8138,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<T*> { struct deref_traits<T*> {
uvalue operator()(T* v) const { uvalue operator()(T* v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
@@ -8142,7 +8146,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<const T*> { struct deref_traits<const T*> {
uvalue operator()(const T* v) const { uvalue operator()(const T* v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
@@ -8150,7 +8154,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<std::shared_ptr<T>> { struct deref_traits<std::shared_ptr<T>> {
uvalue operator()(const std::shared_ptr<T>& v) const { uvalue operator()(const std::shared_ptr<T>& v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
@@ -8158,7 +8162,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<std::unique_ptr<T>> { struct deref_traits<std::unique_ptr<T>> {
uvalue operator()(const std::unique_ptr<T>& v) const { uvalue operator()(const std::unique_ptr<T>& v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
} }
@@ -8181,7 +8185,7 @@ namespace meta_hpp::detail
struct index_traits<T*> { struct index_traits<T*> {
uvalue operator()(T* v, std::size_t i) const { uvalue operator()(T* v, std::size_t i) const {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
return uvalue{v[i]}; return v != nullptr ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -8190,7 +8194,7 @@ namespace meta_hpp::detail
struct index_traits<const T*> { struct index_traits<const T*> {
uvalue operator()(const T* v, std::size_t i) const { uvalue operator()(const T* v, std::size_t i) const {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
return uvalue{v[i]}; return v != nullptr ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -8198,7 +8202,15 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::array<T, Size>> { struct index_traits<std::array<T, Size>> {
uvalue operator()(const std::array<T, Size>& v, std::size_t i) const { uvalue operator()(const std::array<T, Size>& v, std::size_t i) const {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
}
};
template < typename T, typename Allocator >
requires std::is_copy_constructible_v<T>
struct index_traits<std::deque<T, Allocator>> {
uvalue operator()(const std::deque<T, Allocator>& v, std::size_t i) {
return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -8206,7 +8218,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::span<T, Extent>> { struct index_traits<std::span<T, Extent>> {
uvalue operator()(const std::span<T, Extent>& v, std::size_t i) const { uvalue operator()(const std::span<T, Extent>& v, std::size_t i) const {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -8214,7 +8226,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::basic_string<T, Traits, Allocator>> { struct index_traits<std::basic_string<T, Traits, Allocator>> {
uvalue operator()(const std::basic_string<T, Traits, Allocator>& v, std::size_t i) const { uvalue operator()(const std::basic_string<T, Traits, Allocator>& v, std::size_t i) const {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -8222,7 +8234,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::vector<T, Allocator>> { struct index_traits<std::vector<T, Allocator>> {
uvalue operator()(const std::vector<T, Allocator>& v, std::size_t i) { uvalue operator()(const std::vector<T, Allocator>& v, std::size_t i) {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
} }
@@ -8392,21 +8404,26 @@ namespace meta_hpp
self.vtable_ = nullptr; self.vtable_ = nullptr;
}, },
.deref = +[]([[maybe_unused]] const storage_u& from) -> uvalue {
if constexpr ( detail::has_deref_traits<Tp> ) {
return detail::deref_traits<Tp>{}(*storage_cast<Tp>(from));
} else {
META_HPP_THROW_AS(exception, "value type doesn't have value deref traits");
}
},
.index = +[]([[maybe_unused]] const storage_u& from, [[maybe_unused]] std::size_t i) -> uvalue { .deref = [](){
if constexpr ( detail::has_index_traits<Tp> ) { if constexpr ( detail::has_deref_traits<Tp> ) {
return detail::index_traits<Tp>{}(*storage_cast<Tp>(from), i); return +[](const storage_u& from) -> uvalue {
return detail::deref_traits<Tp>{}(*storage_cast<Tp>(from));
};
} else { } else {
META_HPP_THROW_AS(exception, "value type doesn't have value index traits"); return nullptr;
} }
}, }(),
.index = [](){
if constexpr ( detail::has_index_traits<Tp> ) {
return +[](const storage_u& from, std::size_t i) -> uvalue {
return detail::index_traits<Tp>{}(*storage_cast<Tp>(from), i);
};
} else {
return nullptr;
}
}(),
}; };
return &table; return &table;
@@ -8530,11 +8547,19 @@ namespace meta_hpp
} }
inline uvalue uvalue::operator*() const { inline uvalue uvalue::operator*() const {
return vtable_ != nullptr ? vtable_->deref(storage_) : uvalue{}; return has_deref_op() ? vtable_->deref(storage_) : uvalue{};
}
inline bool uvalue::has_deref_op() const noexcept {
return vtable_ != nullptr && vtable_->deref != nullptr;
} }
inline uvalue uvalue::operator[](std::size_t index) const { inline uvalue uvalue::operator[](std::size_t index) const {
return vtable_ != nullptr ? vtable_->index(storage_, index) : uvalue{}; return has_index_op() ? vtable_->index(storage_, index) : uvalue{};
}
inline bool uvalue::has_index_op() const noexcept {
return vtable_ != nullptr && vtable_->index != nullptr;
} }
template < typename T > template < typename T >

View File

@@ -385,35 +385,59 @@ TEST_CASE("meta/meta_utilities/value") {
SUBCASE("deref") { SUBCASE("deref") {
{ {
int i{42}; int i{42};
const meta::uvalue v{*meta::uvalue{&i}}; meta::uvalue u{&i};
CHECK(u.has_deref_op());
const meta::uvalue v{*u};
CHECK(v.get_type() == meta::resolve_type<int>()); CHECK(v.get_type() == meta::resolve_type<int>());
CHECK(v.get_data() != &i); CHECK(v.get_data() != &i);
} }
{ {
const char i{42}; const char i{42};
const meta::uvalue v{*meta::uvalue{&i}}; meta::uvalue u{&i};
CHECK(u.has_deref_op());
const meta::uvalue v{*u};
CHECK(v.get_type() == meta::resolve_type<char>()); CHECK(v.get_type() == meta::resolve_type<char>());
CHECK(v.get_data() != &i); CHECK(v.get_data() != &i);
} }
{ {
const int i{42}; const int i{42};
const int* const pi = &i; const int* const pi = &i;
const meta::uvalue v{*meta::uvalue{&pi}}; meta::uvalue u{&pi};
CHECK(u.has_deref_op());
const meta::uvalue v{*u};
CHECK(v.get_type() == meta::resolve_type<const int*>() ); CHECK(v.get_type() == meta::resolve_type<const int*>() );
CHECK(v.get_as<const int*>() == pi); CHECK(v.get_as<const int*>() == pi);
} }
{ {
int i{42}; int i{42};
[[maybe_unused]] void* p1 = &i; void* p1 = &i;
[[maybe_unused]] const void* p2 = &i; const void* p2 = &i;
[[maybe_unused]] void* const& p3 = &i; void* const& p3 = &i;
[[maybe_unused]] const void* const& p4 = &i; const void* const& p4 = &i;
CHECK_THROWS(std::ignore = *meta::uvalue(p1)); CHECK_FALSE(meta::uvalue(p1).has_deref_op());
CHECK_THROWS(std::ignore = *meta::uvalue(p2)); CHECK_FALSE(meta::uvalue(p2).has_deref_op());
CHECK_THROWS(std::ignore = *meta::uvalue(p3)); CHECK_FALSE(meta::uvalue(p3).has_deref_op());
CHECK_THROWS(std::ignore = *meta::uvalue(p4)); CHECK_FALSE(meta::uvalue(p4).has_deref_op());
CHECK_FALSE(*meta::uvalue(p1));
CHECK_FALSE(*meta::uvalue(p2));
CHECK_FALSE(*meta::uvalue(p3));
CHECK_FALSE(*meta::uvalue(p4));
}
{
int* p1 = nullptr;
const int* p2 = nullptr;
CHECK(meta::uvalue{p1}.has_deref_op());
CHECK(meta::uvalue{p2}.has_deref_op());
CHECK_FALSE(*meta::uvalue{p1});
CHECK_FALSE(*meta::uvalue{p2});
} }
{ {
ivec2 v{1,2}; ivec2 v{1,2};
@@ -443,55 +467,127 @@ TEST_CASE("meta/meta_utilities/value") {
TEST_CASE("meta/meta_utilities/value/arrays") { TEST_CASE("meta/meta_utilities/value/arrays") {
namespace meta = meta_hpp; namespace meta = meta_hpp;
SUBCASE("int") {
meta::uvalue v{42};
CHECK(v.get_type() == meta::resolve_type<int>());
CHECK_FALSE(v.has_index_op());
CHECK_FALSE(v[0]);
}
SUBCASE("void*") {
int i{42};
void* p{&i};
meta::uvalue v{p};
CHECK(v.get_type() == meta::resolve_type<void*>());
CHECK_FALSE(v.has_index_op());
CHECK_FALSE(v[0]);
}
SUBCASE("const void*") {
int i{42};
const void* p{&i};
meta::uvalue v{p};
CHECK(v.get_type() == meta::resolve_type<const void*>());
CHECK_FALSE(v.has_index_op());
CHECK_FALSE(v[0]);
}
SUBCASE("int[3]") { SUBCASE("int[3]") {
int arr[3]{1,2,3}; {
meta::uvalue v{arr}; int arr[3]{1,2,3};
CHECK(v.get_type() == meta::resolve_type<int*>()); meta::uvalue v{arr};
CHECK(v[0].get_as<int>() == 1); CHECK(v.get_type() == meta::resolve_type<int*>());
CHECK(v[1].get_as<int>() == 2); CHECK(v.has_index_op());
CHECK(v[2].get_as<int>() == 3);
CHECK(v[0].get_as<int>() == 1);
CHECK(v[1].get_as<int>() == 2);
CHECK(v[2].get_as<int>() == 3);
}
{
int* arr = nullptr;
meta::uvalue v{arr};
CHECK(v.get_type() == meta::resolve_type<int*>());
CHECK(v.has_index_op());
CHECK_FALSE(v[0]);
}
} }
SUBCASE("const int[3]") { SUBCASE("const int[3]") {
const int arr[3]{1,2,3}; {
meta::uvalue v{arr}; const int arr[3]{1,2,3};
CHECK(v.get_type() == meta::resolve_type<const int*>()); meta::uvalue v{arr};
CHECK(v[0].get_as<int>() == 1); CHECK(v.get_type() == meta::resolve_type<const int*>());
CHECK(v[1].get_as<int>() == 2); CHECK(v.has_index_op());
CHECK(v[2].get_as<int>() == 3);
CHECK(v[0].get_as<int>() == 1);
CHECK(v[1].get_as<int>() == 2);
CHECK(v[2].get_as<int>() == 3);
}
{
const int* arr = nullptr;
meta::uvalue v{arr};
CHECK(v.get_type() == meta::resolve_type<const int*>());
CHECK(v.has_index_op());
CHECK_FALSE(v[0]);
}
} }
SUBCASE("std::array") { SUBCASE("std::array") {
meta::uvalue v{std::array{1,2,3}}; meta::uvalue v{std::array{1,2,3}};
CHECK(v.get_type() == meta::resolve_type<std::array<int, 3>>()); CHECK(v.get_type() == meta::resolve_type<std::array<int, 3>>());
CHECK(v.has_index_op());
CHECK(v[0].get_as<int>() == 1); CHECK(v[0].get_as<int>() == 1);
CHECK(v[1].get_as<int>() == 2); CHECK(v[1].get_as<int>() == 2);
CHECK(v[2].get_as<int>() == 3); CHECK(v[2].get_as<int>() == 3);
CHECK_FALSE(v[3]);
}
SUBCASE("std::deque") {
const meta::uvalue v{std::deque{1,2,3}};
CHECK(v.get_type() == meta::resolve_type<std::deque<int>>());
CHECK(v.has_index_op());
CHECK(v[0].get_as<int>() == 1);
CHECK(v[1].get_as<int>() == 2);
CHECK(v[2].get_as<int>() == 3);
CHECK_FALSE(v[3]);
} }
SUBCASE("std::string") { SUBCASE("std::string") {
meta::uvalue v{std::string{"hi!"}}; meta::uvalue v{std::string{"hi!"}};
CHECK(v.get_type() == meta::resolve_type<std::string>()); CHECK(v.get_type() == meta::resolve_type<std::string>());
CHECK(v.has_index_op());
CHECK(v[0].get_as<char>() == 'h'); CHECK(v[0].get_as<char>() == 'h');
CHECK(v[1].get_as<char>() == 'i'); CHECK(v[1].get_as<char>() == 'i');
CHECK(v[2].get_as<char>() == '!'); CHECK(v[2].get_as<char>() == '!');
CHECK_FALSE(v[3]);
} }
SUBCASE("std::span") { SUBCASE("std::span") {
std::vector arr{1,2,3}; std::vector arr{1,2,3};
meta::uvalue v{std::span{arr}}; meta::uvalue v{std::span{arr}};
CHECK(v.get_type() == meta::resolve_type<std::span<int>>()); CHECK(v.get_type() == meta::resolve_type<std::span<int>>());
CHECK(v.has_index_op());
CHECK(v[0].get_as<int>() == 1); CHECK(v[0].get_as<int>() == 1);
CHECK(v[1].get_as<int>() == 2); CHECK(v[1].get_as<int>() == 2);
CHECK(v[2].get_as<int>() == 3); CHECK(v[2].get_as<int>() == 3);
CHECK_FALSE(v[3]);
} }
SUBCASE("std::vector") { SUBCASE("std::vector") {
const meta::uvalue v{std::vector{1,2,3}}; const meta::uvalue v{std::vector{1,2,3}};
CHECK(v.get_type() == meta::resolve_type<std::vector<int>>()); CHECK(v.get_type() == meta::resolve_type<std::vector<int>>());
CHECK(v.has_index_op());
CHECK(v[0].get_as<int>() == 1); CHECK(v[0].get_as<int>() == 1);
CHECK(v[1].get_as<int>() == 2); CHECK(v[1].get_as<int>() == 2);
CHECK(v[2].get_as<int>() == 3); CHECK(v[2].get_as<int>() == 3);
CHECK_FALSE(v[3]);
} }
} }

View File

@@ -23,6 +23,7 @@
#include <array> #include <array>
#include <atomic> #include <atomic>
#include <concepts> #include <concepts>
#include <deque>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <map> #include <map>

View File

@@ -26,7 +26,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<T*> { struct deref_traits<T*> {
uvalue operator()(T* v) const { uvalue operator()(T* v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
@@ -34,7 +34,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<const T*> { struct deref_traits<const T*> {
uvalue operator()(const T* v) const { uvalue operator()(const T* v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
@@ -42,7 +42,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<std::shared_ptr<T>> { struct deref_traits<std::shared_ptr<T>> {
uvalue operator()(const std::shared_ptr<T>& v) const { uvalue operator()(const std::shared_ptr<T>& v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
@@ -50,7 +50,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct deref_traits<std::unique_ptr<T>> { struct deref_traits<std::unique_ptr<T>> {
uvalue operator()(const std::unique_ptr<T>& v) const { uvalue operator()(const std::unique_ptr<T>& v) const {
return uvalue{*v}; return v != nullptr ? uvalue{*v} : uvalue{};
} }
}; };
} }

View File

@@ -27,7 +27,7 @@ namespace meta_hpp::detail
struct index_traits<T*> { struct index_traits<T*> {
uvalue operator()(T* v, std::size_t i) const { uvalue operator()(T* v, std::size_t i) const {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
return uvalue{v[i]}; return v != nullptr ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -36,7 +36,7 @@ namespace meta_hpp::detail
struct index_traits<const T*> { struct index_traits<const T*> {
uvalue operator()(const T* v, std::size_t i) const { uvalue operator()(const T* v, std::size_t i) const {
// NOLINTNEXTLINE(*-pointer-arithmetic) // NOLINTNEXTLINE(*-pointer-arithmetic)
return uvalue{v[i]}; return v != nullptr ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -44,7 +44,15 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::array<T, Size>> { struct index_traits<std::array<T, Size>> {
uvalue operator()(const std::array<T, Size>& v, std::size_t i) const { uvalue operator()(const std::array<T, Size>& v, std::size_t i) const {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
}
};
template < typename T, typename Allocator >
requires std::is_copy_constructible_v<T>
struct index_traits<std::deque<T, Allocator>> {
uvalue operator()(const std::deque<T, Allocator>& v, std::size_t i) {
return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -52,7 +60,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::span<T, Extent>> { struct index_traits<std::span<T, Extent>> {
uvalue operator()(const std::span<T, Extent>& v, std::size_t i) const { uvalue operator()(const std::span<T, Extent>& v, std::size_t i) const {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -60,7 +68,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::basic_string<T, Traits, Allocator>> { struct index_traits<std::basic_string<T, Traits, Allocator>> {
uvalue operator()(const std::basic_string<T, Traits, Allocator>& v, std::size_t i) const { uvalue operator()(const std::basic_string<T, Traits, Allocator>& v, std::size_t i) const {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
@@ -68,7 +76,7 @@ namespace meta_hpp::detail
requires std::is_copy_constructible_v<T> requires std::is_copy_constructible_v<T>
struct index_traits<std::vector<T, Allocator>> { struct index_traits<std::vector<T, Allocator>> {
uvalue operator()(const std::vector<T, Allocator>& v, std::size_t i) { uvalue operator()(const std::vector<T, Allocator>& v, std::size_t i) {
return uvalue{v[i]}; return i < v.size() ? uvalue{v[i]} : uvalue{};
} }
}; };
} }

View File

@@ -83,7 +83,10 @@ namespace meta_hpp
[[nodiscard]] const void* get_cdata() const noexcept; [[nodiscard]] const void* get_cdata() const noexcept;
[[nodiscard]] uvalue operator*() const; [[nodiscard]] uvalue operator*() const;
[[nodiscard]] bool has_deref_op() const noexcept;
[[nodiscard]] uvalue operator[](std::size_t index) const; [[nodiscard]] uvalue operator[](std::size_t index) const;
[[nodiscard]] bool has_index_op() const noexcept;
template < typename T > template < typename T >
[[nodiscard]] T get_as() &&; [[nodiscard]] T get_as() &&;

View File

@@ -180,21 +180,26 @@ namespace meta_hpp
self.vtable_ = nullptr; self.vtable_ = nullptr;
}, },
.deref = +[]([[maybe_unused]] const storage_u& from) -> uvalue {
if constexpr ( detail::has_deref_traits<Tp> ) {
return detail::deref_traits<Tp>{}(*storage_cast<Tp>(from));
} else {
META_HPP_THROW_AS(exception, "value type doesn't have value deref traits");
}
},
.index = +[]([[maybe_unused]] const storage_u& from, [[maybe_unused]] std::size_t i) -> uvalue { .deref = [](){
if constexpr ( detail::has_index_traits<Tp> ) { if constexpr ( detail::has_deref_traits<Tp> ) {
return detail::index_traits<Tp>{}(*storage_cast<Tp>(from), i); return +[](const storage_u& from) -> uvalue {
return detail::deref_traits<Tp>{}(*storage_cast<Tp>(from));
};
} else { } else {
META_HPP_THROW_AS(exception, "value type doesn't have value index traits"); return nullptr;
} }
}, }(),
.index = [](){
if constexpr ( detail::has_index_traits<Tp> ) {
return +[](const storage_u& from, std::size_t i) -> uvalue {
return detail::index_traits<Tp>{}(*storage_cast<Tp>(from), i);
};
} else {
return nullptr;
}
}(),
}; };
return &table; return &table;
@@ -318,11 +323,19 @@ namespace meta_hpp
} }
inline uvalue uvalue::operator*() const { inline uvalue uvalue::operator*() const {
return vtable_ != nullptr ? vtable_->deref(storage_) : uvalue{}; return has_deref_op() ? vtable_->deref(storage_) : uvalue{};
}
inline bool uvalue::has_deref_op() const noexcept {
return vtable_ != nullptr && vtable_->deref != nullptr;
} }
inline uvalue uvalue::operator[](std::size_t index) const { inline uvalue uvalue::operator[](std::size_t index) const {
return vtable_ != nullptr ? vtable_->index(storage_, index) : uvalue{}; return has_index_op() ? vtable_->index(storage_, index) : uvalue{};
}
inline bool uvalue::has_index_op() const noexcept {
return vtable_ != nullptr && vtable_->index != nullptr;
} }
template < typename T > template < typename T >