new uvalue api

This commit is contained in:
BlackMATov
2022-11-06 01:31:58 +07:00
parent 014315699f
commit f9504306c0
13 changed files with 763 additions and 275 deletions

View File

@@ -35,6 +35,7 @@
#include <vector>
#include "meta_base/bitflags.hpp"
#include "meta_base/cv_traits.hpp"
#include "meta_base/cvref_traits.hpp"
#include "meta_base/fixed_function.hpp"
#include "meta_base/hash_combiner.hpp"

View File

@@ -0,0 +1,46 @@
/*******************************************************************************
* 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-2022, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include <type_traits>
namespace meta_hpp::detail
{
template < typename From >
struct cv_traits {
static constexpr bool is_const = std::is_const_v<std::remove_reference_t<From>>;
static constexpr bool is_volatile = std::is_volatile_v<std::remove_reference_t<From>>;
template < bool yesno, template < typename > typename Q, typename V >
using apply_t_if = std::conditional_t<yesno, Q<V>, V>;
template < typename To >
using add_to =
apply_t_if<is_const, std::add_const_t,
apply_t_if<is_volatile, std::add_volatile_t,
To>>;
template < typename To >
using copy_to = add_to<std::remove_cv_t<To>>;
};
template < typename From, typename To >
struct add_cv {
using type = typename cv_traits<From>::template add_to<To>;
};
template < typename From, typename To >
struct copy_cv {
using type = typename cv_traits<From>::template copy_to<To>;
};
template < typename From, typename To >
using add_cv_t = typename add_cv<From, To>::type;
template < typename From, typename To >
using copy_cv_t = typename copy_cv<From, To>::type;
}

View File

@@ -21,12 +21,20 @@ namespace meta_hpp::detail
using apply_t_if = std::conditional_t<yesno, Q<V>, V>;
template < typename To >
using copy_to =
using add_to =
apply_t_if<is_lvalue, std::add_lvalue_reference_t,
apply_t_if<is_rvalue, std::add_rvalue_reference_t,
apply_t_if<is_const, std::add_const_t,
apply_t_if<is_volatile, std::add_volatile_t,
std::remove_cvref_t<To>>>>>;
To>>>>;
template < typename To >
using copy_to = add_to<std::remove_cvref_t<To>>;
};
template < typename From, typename To >
struct add_cvref {
using type = typename cvref_traits<From>::template add_to<To>;
};
template < typename From, typename To >
@@ -34,6 +42,9 @@ namespace meta_hpp::detail
using type = typename cvref_traits<From>::template copy_to<To>;
};
template < typename From, typename To >
using add_cvref_t = typename add_cvref<From, To>::type;
template < typename From, typename To >
using copy_cvref_t = typename copy_cvref<From, To>::type;
}

View File

@@ -61,22 +61,40 @@ namespace meta_hpp
[[nodiscard]] uvalue operator[](std::size_t index) const;
template < typename T >
[[nodiscard]] std::decay_t<T>& cast() &;
[[nodiscard]] T get_as() &;
template < typename T >
[[nodiscard]] T get_as() &&;
template < typename T >
[[nodiscard]] T get_as() const &;
template < typename T >
[[nodiscard]] T get_as() const &&;
template < typename T >
[[nodiscard]] std::decay_t<T>&& cast() &&;
[[nodiscard]] T& get_as_ref() &;
template < typename T >
[[nodiscard]] T&& get_as_ref() &&;
template < typename T >
[[nodiscard]] const T& get_as_ref() const &;
template < typename T >
[[nodiscard]] const T&& get_as_ref() const &&;
template < typename T >
[[nodiscard]] const std::decay_t<T>& cast() const &;
[[nodiscard]] bool can_get_as() &;
template < typename T >
[[nodiscard]] bool can_get_as() &&;
template < typename T >
[[nodiscard]] bool can_get_as() const &;
template < typename T >
[[nodiscard]] bool can_get_as() const &&;
template < typename T >
[[nodiscard]] const std::decay_t<T>&& cast() const &&;
[[nodiscard]] bool can_get_as_ref() &;
template < typename T >
[[nodiscard]] std::decay_t<T>* try_cast() noexcept;
[[nodiscard]] bool can_get_as_ref() &&;
template < typename T >
[[nodiscard]] const std::decay_t<T>* try_cast() const noexcept;
[[nodiscard]] bool can_get_as_ref() const &;
template < typename T >
[[nodiscard]] bool can_get_as_ref() const &&;
friend bool operator<(const uvalue& l, const uvalue& r);
friend bool operator==(const uvalue& l, const uvalue& r);
@@ -98,3 +116,18 @@ namespace meta_hpp
l.swap(r);
}
}
namespace meta_hpp
{
template < typename T, detail::decay_value_kind V >
[[nodiscard]] auto get_as(V&& value) -> T;
template < typename T, detail::decay_value_kind V >
[[nodiscard]] auto get_as_ref(V&& value) -> detail::add_cvref_t<V&&, T>;
template < typename T, detail::decay_value_kind V >
[[nodiscard]] bool can_get_as(V&& value) noexcept;
template < typename T, detail::decay_value_kind V >
[[nodiscard]] bool can_get_as_ref(V&& value) noexcept;
}

View File

@@ -186,7 +186,7 @@ namespace meta_hpp
.deref = +[]([[maybe_unused]] const uvalue& v) -> uvalue {
if constexpr ( detail::has_deref_traits<Tp> ) {
return detail::deref_traits<Tp>{}(v.cast<Tp>());
return detail::deref_traits<Tp>{}(v.get_as_ref<Tp>());
} else {
detail::throw_exception_with("value type doesn't have value deref traits");
}
@@ -194,7 +194,7 @@ namespace meta_hpp
.index = +[]([[maybe_unused]] const uvalue& v, [[maybe_unused]] std::size_t i) -> uvalue {
if constexpr ( detail::has_index_traits<Tp> ) {
return detail::index_traits<Tp>{}(v.cast<Tp>(), i);
return detail::index_traits<Tp>{}(v.get_as_ref<Tp>(), i);
} else {
detail::throw_exception_with("value type doesn't have value index traits");
}
@@ -202,7 +202,7 @@ namespace meta_hpp
.less = +[]([[maybe_unused]] const uvalue& l, [[maybe_unused]] const uvalue& r) -> bool {
if constexpr ( detail::has_less_traits<Tp> ) {
return detail::less_traits<Tp>{}(l.cast<Tp>(), r.cast<Tp>());
return detail::less_traits<Tp>{}(l.get_as_ref<Tp>(), r.get_as_ref<Tp>());
} else {
detail::throw_exception_with("value type doesn't have value less traits");
}
@@ -210,7 +210,7 @@ namespace meta_hpp
.equals = +[]([[maybe_unused]] const uvalue& l, [[maybe_unused]] const uvalue& r) -> bool {
if constexpr ( detail::has_equals_traits<Tp> ) {
return detail::equals_traits<Tp>{}(l.cast<Tp>(), r.cast<Tp>());
return detail::equals_traits<Tp>{}(l.get_as_ref<Tp>(), r.get_as_ref<Tp>());
} else {
detail::throw_exception_with("value type doesn't have value equals traits");
}
@@ -218,7 +218,7 @@ namespace meta_hpp
.istream = +[]([[maybe_unused]] std::istream& is, [[maybe_unused]] uvalue& v) -> std::istream& {
if constexpr ( detail::has_istream_traits<Tp> ) {
return detail::istream_traits<Tp>{}(is, v.cast<Tp>());
return detail::istream_traits<Tp>{}(is, v.get_as_ref<Tp>());
} else {
detail::throw_exception_with("value type doesn't have value istream traits");
}
@@ -226,7 +226,7 @@ namespace meta_hpp
.ostream = +[]([[maybe_unused]] std::ostream& os, [[maybe_unused]] const uvalue& v) -> std::ostream& {
if constexpr ( detail::has_ostream_traits<Tp> ) {
return detail::ostream_traits<Tp>{}(os, v.cast<Tp>());
return detail::ostream_traits<Tp>{}(os, v.get_as_ref<Tp>());
} else {
detail::throw_exception_with("value type doesn't have value ostream traits");
}
@@ -327,85 +327,216 @@ namespace meta_hpp
}
template < typename T >
std::decay_t<T>& uvalue::cast() & {
using Tp = std::decay_t<T>;
if ( Tp* ptr = try_cast<Tp>() ) {
return *ptr;
}
detail::throw_exception_with("bad value cast");
T uvalue::get_as() & {
return meta_hpp::get_as<T>(*this);
}
template < typename T >
std::decay_t<T>&& uvalue::cast() && {
using Tp = std::decay_t<T>;
if ( Tp* ptr = try_cast<Tp>() ) {
return std::move(*ptr);
}
detail::throw_exception_with("bad value cast");
T uvalue::get_as() && {
return meta_hpp::get_as<T>(std::move(*this));
}
template < typename T >
const std::decay_t<T>& uvalue::cast() const & {
using Tp = std::decay_t<T>;
if ( const Tp* ptr = try_cast<const Tp>() ) {
return *ptr;
}
detail::throw_exception_with("bad value cast");
T uvalue::get_as() const & {
return meta_hpp::get_as<T>(*this);
}
template < typename T >
const std::decay_t<T>&& uvalue::cast() const && {
using Tp = std::decay_t<T>;
if ( const Tp* ptr = try_cast<const Tp>() ) {
return std::move(*ptr);
}
detail::throw_exception_with("bad value cast");
T uvalue::get_as() const && {
// NOLINTNEXTLINE(*-move-const-arg)
return meta_hpp::get_as<T>(std::move(*this));
}
template < typename T >
std::decay_t<T>* uvalue::try_cast() noexcept {
using Tp = std::decay_t<T>;
T& uvalue::get_as_ref() & {
return meta_hpp::get_as_ref<T>(*this);
}
const any_type& from_type = get_type();
template < typename T >
T&& uvalue::get_as_ref() && {
return meta_hpp::get_as_ref<T>(std::move(*this));
}
template < typename T >
const T& uvalue::get_as_ref() const & {
return meta_hpp::get_as_ref<T>(*this);
}
template < typename T >
const T&& uvalue::get_as_ref() const && {
// NOLINTNEXTLINE(*-move-const-arg)
return meta_hpp::get_as_ref<T>(std::move(*this));
}
//
template < typename T >
bool uvalue::can_get_as() & {
return meta_hpp::can_get_as<T>(*this);
}
template < typename T >
bool uvalue::can_get_as() && {
return meta_hpp::can_get_as<T>(std::move(*this));
}
template < typename T >
bool uvalue::can_get_as() const & {
return meta_hpp::can_get_as<T>(*this);
}
template < typename T >
bool uvalue::can_get_as() const && {
// NOLINTNEXTLINE(*-move-const-arg)
return meta_hpp::can_get_as<T>(std::move(*this));
}
template < typename T >
bool uvalue::can_get_as_ref() & {
return meta_hpp::can_get_as_ref<T>(*this);
}
template < typename T >
bool uvalue::can_get_as_ref() && {
return meta_hpp::can_get_as_ref<T>(std::move(*this));
}
template < typename T >
bool uvalue::can_get_as_ref() const & {
return meta_hpp::can_get_as_ref<T>(*this);
}
template < typename T >
bool uvalue::can_get_as_ref() const && {
// NOLINTNEXTLINE(*-move-const-arg)
return meta_hpp::can_get_as_ref<T>(std::move(*this));
}
}
namespace meta_hpp
{
template < typename T, detail::decay_value_kind V >
[[nodiscard]] auto get_as(V&& value) -> T {
using Tp = std::decay_t<T>;
static_assert(std::is_constructible_v<T, detail::add_cvref_t<V&&, Tp>>);
const any_type& from_type = value.get_type();
const any_type& to_type = resolve_type<Tp>();
if ( from_type == to_type ) {
return static_cast<Tp*>(data());
using to_ptr_t = detail::add_cv_t<V, Tp>*;
auto to_ptr = static_cast<to_ptr_t>(value.data());
return static_cast<T>(*to_ptr);
}
if ( from_type.is_class() && to_type.is_class() ) {
void* to_ptr = detail::pointer_upcast(data(), from_type.as_class(), to_type.as_class());
return static_cast<Tp*>(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();
using to_ptr_t = detail::add_cv_t<V, Tp>*;
auto to_ptr = static_cast<to_ptr_t>(detail::pointer_upcast(value.data(), from_class, to_class));
return static_cast<T>(*to_ptr);
}
return nullptr;
if constexpr ( std::is_pointer_v<T> ) {
if ( to_type.is_pointer() && from_type.is_nullptr() ) {
return static_cast<T>(nullptr);
}
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 ) {
using from_data_ptr_t = detail::add_cv_t<V, void*>*;
auto from_data_ptr = static_cast<from_data_ptr_t>(value.data());
if ( to_data_type.is_void() || to_data_type == from_data_type ) {
return static_cast<T>(*from_data_ptr);
}
if ( is_a(to_data_type, from_data_type) ) {
const class_type& to_data_class = to_data_type.as_class();
const class_type& from_data_class = from_data_type.as_class();
void* to_ptr = detail::pointer_upcast(*from_data_ptr, from_data_class, to_data_class);
return static_cast<T>(to_ptr);
}
}
}
}
detail::throw_exception_with("bad value cast");
}
template < typename T >
const std::decay_t<T>* uvalue::try_cast() const noexcept {
using Tp = std::decay_t<T>;
template < typename T, detail::decay_value_kind V >
[[nodiscard]] auto get_as_ref(V&& value) -> detail::add_cvref_t<V&&, T> {
static_assert(!std::is_reference_v<T>);
return get_as<detail::add_cvref_t<V&&, T>>(std::forward<V>(value));
}
const any_type& from_type = get_type();
template < typename T, detail::decay_value_kind V >
[[nodiscard]] bool can_get_as(V&& value) noexcept {
using Tp = std::decay_t<T>;
static_assert(std::is_constructible_v<T, detail::add_cvref_t<V&&, Tp>>);
const any_type& from_type = value.get_type();
const any_type& to_type = resolve_type<Tp>();
if ( from_type == to_type ) {
return static_cast<const Tp*>(data());
return true;
}
if ( from_type.is_class() && to_type.is_class() ) {
const void* to_ptr = detail::pointer_upcast(data(), from_type.as_class(), to_type.as_class());
return static_cast<const Tp*>(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) ) {
return true;
}
return nullptr;
if constexpr ( std::is_pointer_v<T> ) {
if ( to_type.is_pointer() && from_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;
}
}
}
}
return false;
}
template < typename T, detail::decay_value_kind V >
[[nodiscard]] bool can_get_as_ref(V&& value) noexcept {
static_assert(!std::is_reference_v<T>);
return can_get_as<detail::add_cvref_t<V&&, T>>(std::forward<V>(value));
}
}
@@ -420,7 +551,7 @@ namespace meta_hpp
const any_type& l_type = l.get_type();
const any_type& r_type = resolve_type<T>();
return (l_type < r_type) || (l_type == r_type && l.cast<T>() < r);
return (l_type < r_type) || (l_type == r_type && l.get_as_ref<T>() < r);
}
template < typename T >
@@ -432,7 +563,7 @@ namespace meta_hpp
const any_type& l_type = resolve_type<T>();
const any_type& r_type = r.get_type();
return (l_type < r_type) || (l_type == r_type && l < r.cast<T>());
return (l_type < r_type) || (l_type == r_type && l < r.get_as_ref<T>());
}
[[nodiscard]] inline bool operator<(const uvalue& l, const uvalue& r) {
@@ -462,7 +593,7 @@ namespace meta_hpp
const any_type& l_type = l.get_type();
const any_type& r_type = resolve_type<T>();
return l_type == r_type && l.cast<T>() == r;
return l_type == r_type && l.get_as_ref<T>() == r;
}
template < typename T >
@@ -474,7 +605,7 @@ namespace meta_hpp
const any_type& l_type = resolve_type<T>();
const any_type& r_type = r.get_type();
return l_type == r_type && l == r.cast<T>();
return l_type == r_type && l == r.get_as_ref<T>();
}
[[nodiscard]] inline bool operator==(const uvalue& l, const uvalue& r) {