/******************************************************************************* * 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) ******************************************************************************/ #pragma once #include "../meta_base.hpp" #include "../meta_utilities.hpp" namespace meta_hpp::detail { template < decay_value_kind T > // NOLINTNEXTLINE(readability-named-parameter) arg_base::arg_base(T&&) : arg_base{type_list{}} {} template < decay_non_uvalue_kind T > // NOLINTNEXTLINE(readability-named-parameter) arg_base::arg_base(T&&) : arg_base{type_list{}} {} template < arg_lvalue_ref_kind T > requires decay_non_uvalue_kind // NOLINTNEXTLINE(readability-named-parameter) arg_base::arg_base(type_list) : ref_type_{std::is_const_v> ? ref_types::const_lvalue : ref_types::lvalue} , raw_type_{resolve_type>()} {} template < arg_rvalue_ref_kind T > requires decay_non_uvalue_kind // NOLINTNEXTLINE(readability-named-parameter) arg_base::arg_base(type_list) : ref_type_{std::is_const_v> ? ref_types::const_rvalue : ref_types::rvalue} , raw_type_{resolve_type>()} {} inline arg_base::arg_base(value& v) : ref_type_{ref_types::lvalue} , raw_type_{v.get_type()} {} inline arg_base::arg_base(const value& v) : ref_type_{ref_types::const_lvalue} , raw_type_{v.get_type()} {} inline arg_base::arg_base(value&& v) : ref_type_{ref_types::rvalue} , raw_type_{v.get_type()} {} inline arg_base::arg_base(const value&& v) : ref_type_{ref_types::const_rvalue} , raw_type_{v.get_type()} {} inline bool arg_base::is_const() const noexcept { return ref_type_ == ref_types::const_lvalue || ref_type_ == ref_types::const_rvalue; } inline bool arg_base::is_lvalue() const noexcept { return ref_type_ == ref_types::lvalue || ref_type_ == ref_types::const_lvalue; } inline bool arg_base::is_rvalue() const noexcept { return ref_type_ == ref_types::rvalue || ref_type_ == ref_types::const_rvalue; } inline arg_base::ref_types arg_base::get_ref_type() const noexcept { return ref_type_; } inline const any_type& arg_base::get_raw_type() const noexcept { return raw_type_; } template < typename To > // NOLINTNEXTLINE(readability-function-cognitive-complexity) bool arg_base::can_cast_to() const noexcept { using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; static_assert( !(std::is_reference_v && std::is_pointer_v), "references to pointers are not supported yet"); const any_type& from_type = get_raw_type(); const any_type& to_type = resolve_type(); 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 constexpr ( std::is_pointer_v ) { if ( to_type.is_pointer() && from_type.is_array() ) { 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 array_type& from_type_array = from_type.as_array(); const bool from_type_array_readonly = is_const(); const any_type& to_data_type = to_type_ptr.get_data_type(); const any_type& from_data_type = from_type_array.get_data_type(); if ( is_a(to_data_type, from_data_type) && to_type_ptr_readonly >= from_type_array_readonly ) { 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 ( is_a(to_data_type, from_data_type) && to_type_ptr_readonly >= from_type_ptr_readonly ) { return true; } } } if constexpr ( std::is_reference_v ) { const auto is_convertible = [this](){ switch ( get_ref_type() ) { case ref_types::lvalue: return std::is_convertible_v>; case ref_types::const_lvalue: return std::is_convertible_v>; case ref_types::rvalue: return std::is_convertible_v>; case ref_types::const_rvalue: return std::is_convertible_v>; default: return false; } }; if ( is_a(to_type, from_type) && is_convertible() ) { return true; } } if constexpr ( !std::is_pointer_v && !std::is_reference_v ) { const auto is_constructible = [this](){ switch ( get_ref_type() ) { case ref_types::lvalue: return std::is_constructible_v && can_cast_to(); case ref_types::const_lvalue: return std::is_constructible_v && can_cast_to(); case ref_types::rvalue: return std::is_constructible_v && can_cast_to(); case ref_types::const_rvalue: return std::is_constructible_v && can_cast_to(); default: return false; } }; if ( is_a(to_type, from_type) && is_constructible() ) { return true; } } return false; } } namespace meta_hpp::detail { template < decay_value_kind T > arg::arg(T&& v) : arg_base{std::forward(v)} // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) , data_{const_cast(v.data())} {} template < decay_non_uvalue_kind T > arg::arg(T&& v) : arg_base{std::forward(v)} // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) , data_{const_cast*>(std::addressof(v))} {} template < typename To > // NOLINTNEXTLINE(readability-function-cognitive-complexity) To arg::cast() const { if ( !can_cast_to() ) { throw std::logic_error("bad argument cast"); } using to_raw_type_cv = std::remove_reference_t; using to_raw_type = std::remove_cv_t; const any_type& from_type = get_raw_type(); const any_type& to_type = resolve_type(); if constexpr ( std::is_pointer_v ) { if ( to_type.is_pointer() && from_type.is_array() ) { const pointer_type& to_type_ptr = to_type.as_pointer(); const array_type& from_type_array = from_type.as_array(); const any_type& to_data_type = to_type_ptr.get_data_type(); const any_type& from_data_type = from_type_array.get_data_type(); if ( to_data_type == from_data_type ) { return static_cast(data_); } if ( to_data_type.is_class() && from_data_type.is_class() ) { 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(data_, from_data_class, to_data_class); return static_cast(to_ptr); } } if ( to_type.is_pointer() && from_type.is_pointer() ) { const pointer_type& to_type_ptr = to_type.as_pointer(); const pointer_type& from_type_ptr = from_type.as_pointer(); const any_type& to_data_type = to_type_ptr.get_data_type(); const any_type& from_data_type = from_type_ptr.get_data_type(); void** from_data_ptr = static_cast(data_); if ( to_data_type == from_data_type ) { return static_cast(*from_data_ptr); } if ( to_data_type.is_class() && from_data_type.is_class() ) { 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(to_ptr); } } } if constexpr ( std::is_reference_v ) { if ( to_type == from_type ) { if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(data_); } if constexpr ( std::is_rvalue_reference_v ) { return std::move(*static_cast(data_)); } } if ( to_type.is_class() && from_type.is_class() ) { const class_type& to_class = to_type.as_class(); const class_type& from_class = from_type.as_class(); void* to_ptr = detail::pointer_upcast(data_, from_class, to_class); if constexpr ( std::is_lvalue_reference_v ) { return *static_cast(to_ptr); } if constexpr ( std::is_rvalue_reference_v ) { return std::move(*static_cast(to_ptr)); } } } if constexpr ( !std::is_pointer_v && !std::is_reference_v ) { if constexpr ( std::is_constructible_v ) { if ( get_ref_type() == ref_types::lvalue ) { return To{cast()}; } } if constexpr ( std::is_constructible_v ) { if ( get_ref_type() == ref_types::const_lvalue ) { return To{cast()}; } } if constexpr ( std::is_constructible_v ) { if ( get_ref_type() == ref_types::rvalue ) { return To{cast()}; } } if constexpr ( std::is_constructible_v ) { if ( get_ref_type() == ref_types::const_rvalue ) { return To{cast()}; } } } throw std::logic_error("bad argument cast"); } }