return uvalue::try_get_as

This commit is contained in:
BlackMATov
2022-11-06 17:56:45 +07:00
parent 178ab50a8b
commit a630d89e96
3 changed files with 293 additions and 123 deletions

View File

@@ -68,6 +68,14 @@ namespace meta_hpp
[[nodiscard]] auto get_as() const
-> std::conditional_t<detail::pointer_kind<T>, T, const T&>;
template < typename T >
[[nodiscard]] auto try_get_as() noexcept
-> std::conditional_t<detail::pointer_kind<T>, T, T*>;
template < typename T >
[[nodiscard]] auto try_get_as() const noexcept
-> std::conditional_t<detail::pointer_kind<T>, T, const T*>;
template < typename T >
[[nodiscard]] bool can_get_as() const noexcept;

View File

@@ -330,27 +330,49 @@ namespace meta_hpp
auto uvalue::get_as() -> std::conditional_t<detail::pointer_kind<T>, T, T&> {
static_assert(std::is_same_v<T, std::decay_t<T>>);
if constexpr ( detail::pointer_kind<T> ) {
if ( T ptr = try_get_as<T>(); ptr || get_type().is_nullptr() ) {
return ptr;
}
} else {
if ( T* ptr = try_get_as<T>() ) {
return *ptr;
}
}
detail::throw_exception_with("bad value cast");
}
template < typename T >
auto uvalue::get_as() const -> std::conditional_t<detail::pointer_kind<T>, T, const T&> {
static_assert(std::is_same_v<T, std::decay_t<T>>);
if constexpr ( detail::pointer_kind<T> ) {
if ( T ptr = try_get_as<T>(); ptr || get_type().is_nullptr() ) {
return ptr;
}
} else {
if ( const T* ptr = try_get_as<T>() ) {
return *ptr;
}
}
detail::throw_exception_with("bad value cast");
}
template < typename T >
// NOLINTNEXTLINE(*-function-cognitive-complexity)
auto uvalue::try_get_as() noexcept -> std::conditional_t<detail::pointer_kind<T>, T, T*> {
static_assert(std::is_same_v<T, std::decay_t<T>>);
const any_type& from_type = get_type();
const any_type& to_type = resolve_type<T>();
if ( from_type == to_type ) {
T* to_ptr = static_cast<T*>(data());
return *to_ptr;
}
const auto is_a = [](const any_type& base, const any_type& derived){
return (base == derived)
|| (base.is_class() && derived.is_class() && base.as_class().is_base_of(derived.as_class()));
};
if ( is_a(to_type, from_type) ) {
const class_type& to_class = to_type.as_class();
const class_type& from_class = from_type.as_class();
T* to_ptr = static_cast<T*>(detail::pointer_upcast(data(), from_class, to_class));
return *to_ptr;
}
if constexpr ( detail::pointer_kind<T> ) {
if ( to_type.is_pointer() && from_type.is_nullptr() ) {
return static_cast<T>(nullptr);
@@ -385,34 +407,37 @@ namespace meta_hpp
}
}
detail::throw_exception_with("bad value cast");
if constexpr ( !detail::pointer_kind<T> ) {
if ( from_type == to_type ) {
T* to_ptr = static_cast<T*>(data());
return to_ptr;
}
if ( is_a(to_type, from_type) ) {
const class_type& to_class = to_type.as_class();
const class_type& from_class = from_type.as_class();
T* to_ptr = static_cast<T*>(detail::pointer_upcast(data(), from_class, to_class));
return to_ptr;
}
}
return nullptr;
}
template < typename T >
auto uvalue::get_as() const -> std::conditional_t<detail::pointer_kind<T>, T, const T&> {
// NOLINTNEXTLINE(*-function-cognitive-complexity)
auto uvalue::try_get_as() const noexcept -> std::conditional_t<detail::pointer_kind<T>, T, const T*> {
static_assert(std::is_same_v<T, std::decay_t<T>>);
const any_type& from_type = get_type();
const any_type& to_type = resolve_type<T>();
if ( from_type == to_type ) {
const T* to_ptr = static_cast<const T*>(data());
return *to_ptr;
}
const auto is_a = [](const any_type& base, const any_type& derived){
return (base == derived)
|| (base.is_class() && derived.is_class() && base.as_class().is_base_of(derived.as_class()));
};
if ( is_a(to_type, from_type) ) {
const class_type& to_class = to_type.as_class();
const class_type& from_class = from_type.as_class();
const T* to_ptr = static_cast<const T*>(detail::pointer_upcast(data(), from_class, to_class));
return *to_ptr;
}
if constexpr ( detail::pointer_kind<T> ) {
if ( to_type.is_pointer() && from_type.is_nullptr() ) {
return static_cast<T>(nullptr);
@@ -447,49 +472,35 @@ namespace meta_hpp
}
}
detail::throw_exception_with("bad value cast");
if constexpr ( !detail::pointer_kind<T> ) {
if ( from_type == to_type ) {
const T* to_ptr = static_cast<const T*>(data());
return to_ptr;
}
if ( is_a(to_type, from_type) ) {
const class_type& to_class = to_type.as_class();
const class_type& from_class = from_type.as_class();
const T* to_ptr = static_cast<const T*>(detail::pointer_upcast(data(), from_class, to_class));
return to_ptr;
}
}
return nullptr;
}
template < typename T >
bool uvalue::can_get_as() const noexcept {
static_assert(std::is_same_v<T, std::decay_t<T>>);
const any_type& from_type = get_type();
const any_type& to_type = resolve_type<T>();
if ( from_type == to_type ) {
return true;
}
const auto is_a = [](const any_type& base, const any_type& derived){
return (base == derived)
|| (base.is_class() && derived.is_class() && base.as_class().is_base_of(derived.as_class()));
};
if ( is_a(to_type, from_type) ) {
return true;
}
if constexpr ( detail::pointer_kind<T> ) {
if ( to_type.is_pointer() && from_type.is_nullptr() ) {
if ( T ptr = try_get_as<T>(); ptr || get_type().is_nullptr() ) {
return true;
}
if ( to_type.is_pointer() && from_type.is_pointer() ) {
const pointer_type& to_type_ptr = to_type.as_pointer();
const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly);
const pointer_type& from_type_ptr = from_type.as_pointer();
const bool from_type_ptr_readonly = from_type_ptr.get_flags().has(pointer_flags::is_readonly);
const any_type& to_data_type = to_type_ptr.get_data_type();
const any_type& from_data_type = from_type_ptr.get_data_type();
if ( to_type_ptr_readonly >= from_type_ptr_readonly ) {
if ( to_data_type.is_void() || is_a(to_data_type, from_data_type) ) {
return true;
}
}
} else {
if ( const T* ptr = try_get_as<T>() ) {
return true;
}
}

View File

@@ -61,14 +61,6 @@ TEST_CASE("meta/meta_utilities/value4/get_type") {
const derived d{};
CHECK(meta::uvalue{d}.get_type() == meta::resolve_type<derived>());
}
{
derived d{};
CHECK(meta::uvalue{std::move(d)}.get_type() == meta::resolve_type<derived>());
}
{
const derived d{};
CHECK(meta::uvalue{std::move(d)}.get_type() == meta::resolve_type<derived>());
}
}
SUBCASE("from ptr") {
@@ -119,21 +111,11 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
CHECK(v.get_as<derived>().l == 168);
CHECK_THROWS(std::ignore = v.get_as<derived2>());
}
{
meta::uvalue v{derived{}};
CHECK(std::move(v).get_as<derived>().l == 168);
CHECK_THROWS(std::ignore = std::move(v).get_as<derived2>());
}
{
const meta::uvalue v{derived{}};
CHECK(v.get_as<derived>().l == 168);
CHECK_THROWS(std::ignore = v.get_as<derived2>());
}
{
const meta::uvalue v{derived{}};
CHECK(std::move(v).get_as<derived>().l == 168);
CHECK_THROWS(std::ignore = std::move(v).get_as<derived2>());
}
}
SUBCASE("derived to base") {
@@ -141,18 +123,10 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
meta::uvalue v{derived{}};
CHECK(v.get_as<base2>().k == 84);
}
{
meta::uvalue v{derived{}};
CHECK(std::move(v).get_as<base2>().k == 84);
}
{
const meta::uvalue v{derived{}};
CHECK(v.get_as<base2>().k == 84);
}
{
const meta::uvalue v{derived{}};
CHECK(std::move(v).get_as<base2>().k == 84);
}
}
SUBCASE("voidptr") {
@@ -161,16 +135,17 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
meta::uvalue v{&d};
CHECK(v.get_as<void*>() == &d);
CHECK(v.get_as<const void*>() == &d);
CHECK(std::move(v).get_as<void*>() == &d);
CHECK(std::move(v).get_as<const void*>() == &d);
}
{
const derived d{};
meta::uvalue v{&d};
CHECK_THROWS(std::ignore = v.get_as<void*>());
CHECK(v.get_as<const void*>() == &d);
CHECK_THROWS(std::ignore = std::move(v).get_as<void*>());
CHECK(std::move(v).get_as<const void*>() == &d);
}
{
meta::uvalue v{derived{}};
CHECK_THROWS(std::ignore = v.get_as<void*>());
CHECK_THROWS(std::ignore = v.get_as<const void*>());
}
}
@@ -181,10 +156,8 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
CHECK(v.get_as<const void*>() == nullptr);
CHECK(v.get_as<derived*>() == nullptr);
CHECK(v.get_as<const derived*>() == nullptr);
CHECK(std::move(v).get_as<void*>() == nullptr);
CHECK(std::move(v).get_as<const void*>() == nullptr);
CHECK(std::move(v).get_as<derived*>() == nullptr);
CHECK(std::move(v).get_as<const derived*>() == nullptr);
CHECK_THROWS(std::ignore = v.get_as<derived>());
}
{
const meta::uvalue v{nullptr};
@@ -192,10 +165,8 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
CHECK(v.get_as<const void*>() == nullptr);
CHECK(v.get_as<derived*>() == nullptr);
CHECK(v.get_as<const derived*>() == nullptr);
CHECK(std::move(v).get_as<void*>() == nullptr);
CHECK(std::move(v).get_as<const void*>() == nullptr);
CHECK(std::move(v).get_as<derived*>() == nullptr);
CHECK(std::move(v).get_as<const derived*>() == nullptr);
CHECK_THROWS(std::ignore = v.get_as<derived>());
}
}
@@ -209,15 +180,6 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
CHECK_THROWS(std::ignore = v.get_as<derived2*>());
CHECK_THROWS(std::ignore = v.get_as<const derived2*>());
}
{
derived d{};
meta::uvalue v{&d};
CHECK(std::move(v).get_as<derived*>()->l == 168);
CHECK(std::move(v).get_as<const derived*>()->l == 168);
CHECK_THROWS(std::ignore = std::move(v).get_as<derived2*>());
CHECK_THROWS(std::ignore = std::move(v).get_as<const derived2*>());
}
{
const derived d{};
meta::uvalue v{&d};
@@ -225,13 +187,6 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
CHECK_THROWS(std::ignore = v.get_as<const derived2*>());
}
{
const derived d{};
meta::uvalue v{&d};
CHECK(std::move(v).get_as<const derived*>()->l == 168);
CHECK_THROWS(std::ignore = std::move(v).get_as<const derived2*>());
}
}
SUBCASE("derived* to base*") {
@@ -251,6 +206,125 @@ TEST_CASE("meta/meta_utilities/value4/get_as") {
}
}
TEST_CASE("meta/meta_utilities/value4/try_get_as") {
namespace meta = meta_hpp;
static_assert(std::is_same_v<decltype(std::declval<meta::uvalue&>().try_get_as<derived>()), derived*>);
static_assert(std::is_same_v<decltype(std::declval<meta::uvalue&&>().try_get_as<derived>()), derived*>);
static_assert(std::is_same_v<decltype(std::declval<const meta::uvalue&>().try_get_as<derived>()), const derived*>);
static_assert(std::is_same_v<decltype(std::declval<const meta::uvalue&&>().try_get_as<derived>()), const derived*>);
static_assert(std::is_same_v<decltype(std::declval<meta::uvalue&>().try_get_as<derived*>()), derived*>);
static_assert(std::is_same_v<decltype(std::declval<meta::uvalue&&>().try_get_as<derived*>()), derived*>);
static_assert(std::is_same_v<decltype(std::declval<const meta::uvalue&>().try_get_as<derived*>()), derived*>);
static_assert(std::is_same_v<decltype(std::declval<const meta::uvalue&&>().try_get_as<derived*>()), derived*>);
static_assert(std::is_same_v<decltype(std::declval<meta::uvalue&>().try_get_as<const derived*>()), const derived*>);
static_assert(std::is_same_v<decltype(std::declval<meta::uvalue&&>().try_get_as<const derived*>()), const derived*>);
static_assert(std::is_same_v<decltype(std::declval<const meta::uvalue&>().try_get_as<const derived*>()), const derived*>);
static_assert(std::is_same_v<decltype(std::declval<const meta::uvalue&&>().try_get_as<const derived*>()), const derived*>);
SUBCASE("derived to derived") {
{
meta::uvalue v{derived{}};
CHECK(v.try_get_as<derived>()->l == 168);
CHECK_FALSE(v.try_get_as<derived2>());
}
{
const meta::uvalue v{derived{}};
CHECK(v.try_get_as<derived>()->l == 168);
CHECK_FALSE(v.try_get_as<derived2>());
}
}
SUBCASE("derived to base") {
{
meta::uvalue v{derived{}};
CHECK(v.try_get_as<base2>()->k == 84);
}
{
const meta::uvalue v{derived{}};
CHECK(v.try_get_as<base2>()->k == 84);
}
}
SUBCASE("voidptr") {
{
derived d{};
meta::uvalue v{&d};
CHECK(v.try_get_as<void*>() == &d);
CHECK(v.try_get_as<const void*>() == &d);
}
{
const derived d{};
meta::uvalue v{&d};
CHECK_FALSE(v.try_get_as<void*>());
CHECK(v.try_get_as<const void*>() == &d);
}
{
meta::uvalue v{derived{}};
CHECK_FALSE(v.try_get_as<void*>());
CHECK_FALSE(v.try_get_as<const void*>());
}
}
SUBCASE("nullptr") {
{
meta::uvalue v{nullptr};
CHECK(v.try_get_as<void*>() == nullptr);
CHECK(v.try_get_as<const void*>() == nullptr);
CHECK(v.try_get_as<derived*>() == nullptr);
CHECK(v.try_get_as<const derived*>() == nullptr);
CHECK_FALSE(v.try_get_as<derived>());
}
{
const meta::uvalue v{nullptr};
CHECK(v.try_get_as<void*>() == nullptr);
CHECK(v.try_get_as<const void*>() == nullptr);
CHECK(v.try_get_as<derived*>() == nullptr);
CHECK(v.try_get_as<const derived*>() == nullptr);
CHECK_FALSE(v.try_get_as<derived>());
}
}
SUBCASE("derived* to derived*") {
{
derived d{};
meta::uvalue v{&d};
CHECK(v.try_get_as<derived*>()->l == 168);
CHECK(v.try_get_as<const derived*>()->l == 168);
CHECK_FALSE(v.try_get_as<derived2*>());
CHECK_FALSE(v.try_get_as<const derived2*>());
}
{
const derived d{};
meta::uvalue v{&d};
CHECK(v.try_get_as<const derived*>()->l == 168);
CHECK_FALSE(v.try_get_as<const derived2*>());
}
}
SUBCASE("derived* to base*") {
{
derived d{};
meta::uvalue v{&d};
CHECK(v.try_get_as<base2*>()->k == 84);
CHECK(v.try_get_as<const base2*>()->k == 84);
}
{
const derived d{};
meta::uvalue v{&d};
CHECK_FALSE(v.try_get_as<base2*>());
CHECK(v.try_get_as<const base2*>()->k == 84);
}
}
}
TEST_CASE("meta/meta_utilities/value4/can_get_as") {
namespace meta = meta_hpp;
@@ -260,20 +334,97 @@ TEST_CASE("meta/meta_utilities/value4/can_get_as") {
CHECK(v.can_get_as<derived>());
CHECK_FALSE(v.can_get_as<derived2>());
}
{
meta::uvalue v{derived{}};
CHECK(std::move(v).can_get_as<derived>());
CHECK_FALSE(std::move(v).can_get_as<derived2>());
}
{
const meta::uvalue v{derived{}};
CHECK(v.can_get_as<derived>());
CHECK_FALSE(v.can_get_as<derived2>());
}
}
SUBCASE("derived to base") {
{
meta::uvalue v{derived{}};
CHECK(v.can_get_as<base2>());
}
{
const meta::uvalue v{derived{}};
CHECK(std::move(v).can_get_as<derived>());
CHECK_FALSE(std::move(v).can_get_as<derived2>());
CHECK(v.can_get_as<base2>());
}
}
SUBCASE("voidptr") {
{
derived d{};
meta::uvalue v{&d};
CHECK(v.can_get_as<void*>());
CHECK(v.can_get_as<const void*>());
}
{
const derived d{};
meta::uvalue v{&d};
CHECK_FALSE(v.can_get_as<void*>());
CHECK(v.can_get_as<const void*>());
}
{
meta::uvalue v{derived{}};
CHECK_FALSE(v.can_get_as<void*>());
CHECK_FALSE(v.can_get_as<const void*>());
}
}
SUBCASE("nullptr") {
{
meta::uvalue v{nullptr};
CHECK(v.can_get_as<void*>());
CHECK(v.can_get_as<const void*>());
CHECK(v.can_get_as<derived*>());
CHECK(v.can_get_as<const derived*>());
CHECK_FALSE(v.can_get_as<derived>());
}
{
const meta::uvalue v{nullptr};
CHECK(v.can_get_as<void*>());
CHECK(v.can_get_as<const void*>());
CHECK(v.can_get_as<derived*>());
CHECK(v.can_get_as<const derived*>());
CHECK_FALSE(v.can_get_as<derived>());
}
}
SUBCASE("derived* to derived*") {
{
derived d{};
meta::uvalue v{&d};
CHECK(v.can_get_as<derived*>());
CHECK(v.can_get_as<const derived*>());
CHECK_FALSE(v.can_get_as<derived2*>());
CHECK_FALSE(v.can_get_as<const derived2*>());
}
{
const derived d{};
meta::uvalue v{&d};
CHECK(v.can_get_as<const derived*>());
CHECK_FALSE(v.can_get_as<const derived2*>());
}
}
SUBCASE("derived* to base*") {
{
derived d{};
meta::uvalue v{&d};
CHECK(v.can_get_as<base2*>());
CHECK(v.can_get_as<const base2*>());
}
{
const derived d{};
meta::uvalue v{&d};
CHECK_FALSE(v.can_get_as<base2*>());
CHECK(v.can_get_as<const base2*>());
}
}
}