diff --git a/develop/singles/headers/meta.hpp/meta_all.hpp b/develop/singles/headers/meta.hpp/meta_all.hpp index 9f54fc0..a92b08c 100644 --- a/develop/singles/headers/meta.hpp/meta_all.hpp +++ b/develop/singles/headers/meta.hpp/meta_all.hpp @@ -63,6 +63,60 @@ # define META_HPP_PP_CAT_I(x, y) x##y #endif +#if defined(_MSC_VER) +# define META_HPP_MSVC +#elif defined(__clang__) +# define META_HPP_CLANG +#elif defined(__GNUC__) +# define META_HPP_GCC +#endif + +#if defined(META_HPP_MSVC) +# define META_HPP_MSVC_IGNORE_WARNING(w) __pragma(warning(disable : w)) +# define META_HPP_MSVC_IGNORE_WARNINGS_PUSH() __pragma(warning(push)) +# define META_HPP_MSVC_IGNORE_WARNINGS_POP() __pragma(warning(pop)) +#else +# define META_HPP_MSVC_IGNORE_WARNING(w) +# define META_HPP_MSVC_IGNORE_WARNINGS_PUSH() +# define META_HPP_MSVC_IGNORE_WARNINGS_POP() +#endif + +#if defined(META_HPP_CLANG) +# define META_HPP_CLANG_PRAGMA_TO_STR(x) _Pragma(#x) +# define META_HPP_CLANG_IGNORE_WARNING(w) META_HPP_CLANG_PRAGMA_TO_STR(clang diagnostic ignored w) +# define META_HPP_CLANG_IGNORE_WARNINGS_PUSH() _Pragma("clang diagnostic push") +# define META_HPP_CLANG_IGNORE_WARNINGS_POP() _Pragma("clang diagnostic pop") +#else +# define META_HPP_CLANG_PRAGMA_TO_STR(x) +# define META_HPP_CLANG_IGNORE_WARNING(w) +# define META_HPP_CLANG_IGNORE_WARNINGS_PUSH() +# define META_HPP_CLANG_IGNORE_WARNINGS_POP() +#endif + +#if defined(META_HPP_GCC) +# define META_HPP_GCC_PRAGMA_TO_STR(x) _Pragma(#x) +# define META_HPP_GCC_IGNORE_WARNING(w) META_HPP_GCC_PRAGMA_TO_STR(GCC diagnostic ignored w) +# define META_HPP_GCC_IGNORE_WARNINGS_PUSH() _Pragma("GCC diagnostic push") +# define META_HPP_GCC_IGNORE_WARNINGS_POP() _Pragma("GCC diagnostic pop") +#else +# define META_HPP_GCC_PRAGMA_TO_STR(x) +# define META_HPP_GCC_IGNORE_WARNING(w) +# define META_HPP_GCC_IGNORE_WARNINGS_PUSH() +# define META_HPP_GCC_IGNORE_WARNINGS_POP() +#endif + +#define META_HPP_IGNORE_OVERRIDE_WARNINGS_PUSH() \ + META_HPP_MSVC_IGNORE_WARNINGS_PUSH() \ + META_HPP_CLANG_IGNORE_WARNINGS_PUSH() \ + META_HPP_GCC_IGNORE_WARNINGS_PUSH() \ + META_HPP_CLANG_IGNORE_WARNING("-Winconsistent-missing-override") \ + META_HPP_CLANG_IGNORE_WARNING("-Wsuggest-override") + +#define META_HPP_IGNORE_OVERRIDE_WARNINGS_POP() \ + META_HPP_GCC_IGNORE_WARNINGS_POP() \ + META_HPP_CLANG_IGNORE_WARNINGS_POP() \ + META_HPP_MSVC_IGNORE_WARNINGS_POP()\ + namespace meta_hpp::detail { template < typename Enum > @@ -458,6 +512,8 @@ namespace meta_hpp::detail enum class error_code { no_error, + bad_cast, + bad_const_access, bad_uvalue_access, @@ -473,6 +529,8 @@ namespace meta_hpp::detail switch ( error ) { case error_code::no_error: return "no error"; + case error_code::bad_cast: + return "bad cast"; case error_code::bad_const_access: return "bad const access"; case error_code::bad_uvalue_access: @@ -4285,67 +4343,67 @@ namespace meta_hpp::detail public: template < array_kind Array > [[nodiscard]] array_type resolve_array_type() { - return array_type{resolve_array_type_data()}; + return array_type{resolve_array_type_data>()}; } template < class_kind Class > [[nodiscard]] class_type resolve_class_type() { - return class_type{resolve_class_type_data()}; + return class_type{resolve_class_type_data>()}; } template < class_kind Class, typename... Args > [[nodiscard]] constructor_type resolve_constructor_type() { - return constructor_type{resolve_constructor_type_data()}; + return constructor_type{resolve_constructor_type_data, Args...>()}; } template < class_kind Class > [[nodiscard]] destructor_type resolve_destructor_type() { - return destructor_type{resolve_destructor_type_data()}; + return destructor_type{resolve_destructor_type_data>()}; } template < enum_kind Enum > [[nodiscard]] enum_type resolve_enum_type() { - return enum_type{resolve_enum_type_data()}; + return enum_type{resolve_enum_type_data>()}; } template < function_pointer_kind Function > [[nodiscard]] function_type resolve_function_type() { - return function_type{resolve_function_type_data()}; + return function_type{resolve_function_type_data>()}; } template < member_pointer_kind Member > [[nodiscard]] member_type resolve_member_type() { - return member_type{resolve_member_type_data()}; + return member_type{resolve_member_type_data>()}; } template < method_pointer_kind Method > [[nodiscard]] method_type resolve_method_type() { - return method_type{resolve_method_type_data()}; + return method_type{resolve_method_type_data>()}; } template < nullptr_kind Nullptr > [[nodiscard]] nullptr_type resolve_nullptr_type() { - return nullptr_type{resolve_nullptr_type_data()}; + return nullptr_type{resolve_nullptr_type_data>()}; } template < number_kind Number > [[nodiscard]] number_type resolve_number_type() { - return number_type{resolve_number_type_data()}; + return number_type{resolve_number_type_data>()}; } template < pointer_kind Pointer > [[nodiscard]] pointer_type resolve_pointer_type() { - return pointer_type{resolve_pointer_type_data()}; + return pointer_type{resolve_pointer_type_data>()}; } template < reference_kind Reference > [[nodiscard]] reference_type resolve_reference_type() { - return reference_type{resolve_reference_type_data()}; + return reference_type{resolve_reference_type_data>()}; } template < void_kind Void > [[nodiscard]] void_type resolve_void_type() { - return void_type{resolve_void_type_data()}; + return void_type{resolve_void_type_data>()}; } private: @@ -9043,6 +9101,122 @@ namespace meta_hpp::detail : type_data_base{type_id{type_list>{}}, type_kind::void_} {} } +namespace meta_hpp::detail +{ + template < + typename To, + typename From, + typename ToDT = std::remove_pointer_t, + typename FromDT = std::remove_pointer_t > + concept pointer_ucast_kind // + = (std::is_pointer_v && std::is_class_v) // + && (std::is_pointer_v && (std::is_class_v || std::is_void_v)) // + && (!std::is_const_v || std::is_const_v) // + && (!std::is_volatile_v || std::is_volatile_v); // + + template < + typename To, + typename From, + typename ToDT = std::remove_reference_t, + typename FromDT = std::remove_reference_t > + concept reference_ucast_kind // + = (std::is_reference_v && std::is_class_v) // + && (std::is_reference_v && std::is_class_v) // + && (!std::is_const_v || std::is_const_v) // + && (!std::is_volatile_v || std::is_volatile_v); // +} + +namespace meta_hpp +{ + template < typename To, typename From > + requires detail::pointer_ucast_kind + To ucast(From from); + + template < typename To, typename From > + requires detail::reference_ucast_kind + To ucast(From&& from); +} + +namespace meta_hpp::detail +{ + struct polymorphic_meta_info { + const void* ptr{}; + class_type type{}; + }; + + template < typename T > + concept check_polymorphic_cast_support // + = requires(type_registry& r, const T& v) { + { v.get_most_derived_polymorphic_meta_info(r) } -> std::convertible_to; + }; +} + +namespace meta_hpp +{ + template < typename To, typename From > + requires detail::pointer_ucast_kind + To ucast(From from) { + using from_data_type = std::remove_pointer_t; + using to_data_type = std::remove_pointer_t; + + static_assert( + detail::check_polymorphic_cast_support, + "The type doesn't support ucasts. Use the META_HPP_ENABLE_POLYMORPHIC_CAST macro to fix it." + ); + + if ( from == nullptr ) { + return nullptr; + } + + if constexpr ( std::is_same_v, std::remove_cv_t> ) { + return from; + } + + detail::type_registry& registry{detail::type_registry::instance()}; + const detail::polymorphic_meta_info& meta_info{from->get_most_derived_polymorphic_meta_info(registry)}; + + // NOLINTNEXTLINE(*-const-cast) + void* most_derived_object_ptr = const_cast(meta_info.ptr); + + if constexpr ( std::is_void_v> ) { + return most_derived_object_ptr; + } else { + const class_type& to_class_type = registry.resolve_class_type(); + return static_cast(detail::pointer_upcast(most_derived_object_ptr, meta_info.type, to_class_type)); + } + } + + template < typename To, typename From > + requires detail::reference_ucast_kind + To ucast(From&& from) { + using from_data_type = std::remove_reference_t; + using to_data_type = std::remove_reference_t; + + static_assert( + detail::check_polymorphic_cast_support, + "The type doesn't support ucasts. Use the META_HPP_ENABLE_POLYMORPHIC_CAST macro to fix it." + ); + + if ( to_data_type* ptr = ucast(std::addressof(from)) ) { + return *ptr; + } + + throw_exception(error_code::bad_cast); + } +} + +#define META_HPP_ENABLE_POLYMORPHIC_CAST() \ +public: \ + META_HPP_IGNORE_OVERRIDE_WARNINGS_PUSH() \ + virtual ::meta_hpp::detail::polymorphic_meta_info get_most_derived_polymorphic_meta_info( \ + ::meta_hpp::detail::type_registry& registry \ + ) const { \ + using self_type = std::remove_cvref_t; \ + return ::meta_hpp::detail::polymorphic_meta_info{.ptr = this, .type = registry.resolve_class_type()}; \ + } \ + META_HPP_IGNORE_OVERRIDE_WARNINGS_POP() \ +private: + namespace meta_hpp::detail { template < typename T > diff --git a/develop/unbench/dynamic_cast_bench.cpp b/develop/unbench/dynamic_cast_bench.cpp new file mode 100644 index 0000000..4c7a82e --- /dev/null +++ b/develop/unbench/dynamic_cast_bench.cpp @@ -0,0 +1,219 @@ +/******************************************************************************* + * 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-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include +#include + +#include +#include + +#include + +namespace +{ + namespace meta = meta_hpp; + namespace vmath = vmath_hpp; + + struct base1 { + unsigned b1{1}; + + base1() = default; + virtual ~base1() = default; + + base1(const base1&) = default; + base1& operator=(const base1&) = default; + + RTTR_ENABLE() + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct base2 : base1 { + unsigned b2{2}; + RTTR_ENABLE(base1) + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct base3 : base2 { + unsigned b3{3}; + RTTR_ENABLE(base2) + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct base4 : base3 { + unsigned b4{4}; + RTTR_ENABLE(base3) + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + base4* dynamic_cast_1(base1* b1) { + return dynamic_cast(b1); + } + + base4* dynamic_cast_2(base2* b2) { + return dynamic_cast(b2); + } + + base4* dynamic_cast_3(base3* b3) { + return dynamic_cast(b3); + } + + base4* dynamic_cast_4(base4* b4) { + return dynamic_cast(b4); + } + + const bool registered = [](){ + meta::class_(); + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_(); + return true; + }(); +} + +// +// native +// + +namespace +{ + [[maybe_unused]] + void dynamic_cast_1(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = dynamic_cast_1(&b4); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void dynamic_cast_2(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = dynamic_cast_2(&b4); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void dynamic_cast_3(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = dynamic_cast_3(&b4); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void dynamic_cast_4(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = dynamic_cast_4(&b4); + benchmark::DoNotOptimize(r); + } + } +} + +// +// meta +// + +namespace +{ + [[maybe_unused]] + void meta_dynamic_cast_1(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = meta::ucast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void meta_dynamic_cast_2(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = meta::ucast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void meta_dynamic_cast_3(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = meta::ucast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void meta_dynamic_cast_4(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = meta::ucast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } +} + +// +// rttr +// + +namespace +{ + [[maybe_unused]] + void rttr_dynamic_cast_1(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = rttr::rttr_cast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void rttr_dynamic_cast_2(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = rttr::rttr_cast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void rttr_dynamic_cast_3(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = rttr::rttr_cast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } + + [[maybe_unused]] + void rttr_dynamic_cast_4(benchmark::State &state) { + base4 b4; + for ( auto _ : state ) { + base4* r = rttr::rttr_cast(static_cast(&b4)); + benchmark::DoNotOptimize(r); + } + } +} + +BENCHMARK(dynamic_cast_1); +BENCHMARK(meta_dynamic_cast_1); +BENCHMARK(rttr_dynamic_cast_1); + +BENCHMARK(dynamic_cast_2); +BENCHMARK(meta_dynamic_cast_2); +BENCHMARK(rttr_dynamic_cast_2); + +BENCHMARK(dynamic_cast_3); +BENCHMARK(meta_dynamic_cast_3); +BENCHMARK(rttr_dynamic_cast_3); + +BENCHMARK(dynamic_cast_4); +BENCHMARK(meta_dynamic_cast_4); +BENCHMARK(rttr_dynamic_cast_4); diff --git a/develop/untests/meta_utilities/cast_tests.cpp b/develop/untests/meta_utilities/cast_tests.cpp new file mode 100644 index 0000000..75f57fb --- /dev/null +++ b/develop/untests/meta_utilities/cast_tests.cpp @@ -0,0 +1,370 @@ +/******************************************************************************* + * 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-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include +#include + +namespace +{ + struct A1 { + A1() = default; + A1(const A1&) = default; + virtual ~A1() = default; + char a{'a'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct B1 : A1 { + B1() = default; + B1(const B1&) = default; + char b{'b'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct C1 : B1 { + C1() = default; + C1(const C1&) = default; + char c{'c'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + // A1 <- B1 <- C1 + + struct A2 { + A2() = default; + A2(const A2&) = default; + virtual ~A2() = default; + char a{'a'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct B2 : virtual A2 { + B2() = default; + B2(const B2&) = default; + char b{'b'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct C2 : virtual A2 { + C2() = default; + C2(const C2&) = default; + char c{'c'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + + struct D2 { + D2() = default; + D2(const D2&) = default; + virtual ~D2() = default; + char d{'d'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct E2 : D2 { + E2() = default; + E2(const E2&) = default; + char e{'e'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + struct F2 : B2, C2, E2 { + F2() = default; + F2(const F2&) = default; + char f{'f'}; + META_HPP_ENABLE_POLYMORPHIC_CAST() + }; + + + // A2 <= B2 + // <- + // A2 <= C2 F2 + // <- + // D2 <- E2 + + namespace meta = meta_hpp; + + template < meta::detail::class_kind From, meta::detail::class_kind To, typename Value > + void check_casts_impl(Value& value) { + using from_void_cv = meta::detail::add_cv_t; + from_void_cv* from_void_ptr = dynamic_cast(std::addressof(value)); + REQUIRE(from_void_ptr == meta::ucast(std::addressof(value))); + + From* from_ptr = dynamic_cast(std::addressof(value)); + REQUIRE(from_ptr == meta::ucast(std::addressof(value))); + + using to_void_cv = meta::detail::add_cv_t; + to_void_cv* to_void_ptr = dynamic_cast(from_ptr); + REQUIRE(to_void_ptr == meta::ucast(from_ptr)); + + To* to_ptr = dynamic_cast(from_ptr); + REQUIRE(to_ptr == meta::ucast(from_ptr)); + + if ( from_ptr ) { + if ( to_ptr ) { + REQUIRE(to_ptr == std::addressof(meta::ucast(*from_ptr))); + } else { + REQUIRE_THROWS(meta::ucast(*from_ptr)); + } + } + } + + template < meta::detail::class_kind From, meta::detail::class_kind To, typename Value > + void check_casts(Value& value) { + check_casts_impl(value); + check_casts_impl(std::as_const(value)); + } +} + +TEST_CASE("meta/meta_utilities/ucast/_") { + namespace meta = meta_hpp; + + meta::class_(); + meta::class_().base_(); + meta::class_().base_(); + + meta::class_(); + meta::class_().base_(); + meta::class_().base_(); + meta::class_(); + meta::class_().base_(); + meta::class_().base_(); +} + +TEST_CASE("meta/meta_utilities/ucast") { + namespace meta = meta_hpp; + + SUBCASE("1") { + { + C1 c; + + check_casts(c); + check_casts(c); + check_casts(c); + + check_casts(c); + check_casts(c); + check_casts(c); + + check_casts(c); + check_casts(c); + check_casts(c); + } + { + B1 b; + + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + } + { + A1 a; + + check_casts(a); + check_casts(a); + check_casts(a); + + check_casts(a); + check_casts(a); + check_casts(a); + + check_casts(a); + check_casts(a); + check_casts(a); + } + } + + SUBCASE("2") { + { + F2 f; + + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + check_casts(f); + } + { + B2 b; + + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + check_casts(b); + } + { + E2 e; + + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + check_casts(e); + } + { + D2 d; + + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + check_casts(d); + } + } +} diff --git a/headers/meta.hpp/meta_all.hpp b/headers/meta.hpp/meta_all.hpp index e5edf2b..921f82f 100644 --- a/headers/meta.hpp/meta_all.hpp +++ b/headers/meta.hpp/meta_all.hpp @@ -65,6 +65,9 @@ #include "meta_types/reference_type.hpp" #include "meta_types/void_type.hpp" +#include "meta_ucast.hpp" +#include "meta_ucast/ucast.hpp" + #include "meta_uresult.hpp" #include "meta_uresult/uresult.hpp" diff --git a/headers/meta.hpp/meta_base/base.hpp b/headers/meta.hpp/meta_base/base.hpp index f07e52d..204d4e2 100644 --- a/headers/meta.hpp/meta_base/base.hpp +++ b/headers/meta.hpp/meta_base/base.hpp @@ -63,3 +63,57 @@ # define META_HPP_PP_CAT(x, y) META_HPP_PP_CAT_I(x, y) # define META_HPP_PP_CAT_I(x, y) x##y #endif + +#if defined(_MSC_VER) +# define META_HPP_MSVC +#elif defined(__clang__) +# define META_HPP_CLANG +#elif defined(__GNUC__) +# define META_HPP_GCC +#endif + +#if defined(META_HPP_MSVC) +# define META_HPP_MSVC_IGNORE_WARNING(w) __pragma(warning(disable : w)) +# define META_HPP_MSVC_IGNORE_WARNINGS_PUSH() __pragma(warning(push)) +# define META_HPP_MSVC_IGNORE_WARNINGS_POP() __pragma(warning(pop)) +#else +# define META_HPP_MSVC_IGNORE_WARNING(w) +# define META_HPP_MSVC_IGNORE_WARNINGS_PUSH() +# define META_HPP_MSVC_IGNORE_WARNINGS_POP() +#endif + +#if defined(META_HPP_CLANG) +# define META_HPP_CLANG_PRAGMA_TO_STR(x) _Pragma(#x) +# define META_HPP_CLANG_IGNORE_WARNING(w) META_HPP_CLANG_PRAGMA_TO_STR(clang diagnostic ignored w) +# define META_HPP_CLANG_IGNORE_WARNINGS_PUSH() _Pragma("clang diagnostic push") +# define META_HPP_CLANG_IGNORE_WARNINGS_POP() _Pragma("clang diagnostic pop") +#else +# define META_HPP_CLANG_PRAGMA_TO_STR(x) +# define META_HPP_CLANG_IGNORE_WARNING(w) +# define META_HPP_CLANG_IGNORE_WARNINGS_PUSH() +# define META_HPP_CLANG_IGNORE_WARNINGS_POP() +#endif + +#if defined(META_HPP_GCC) +# define META_HPP_GCC_PRAGMA_TO_STR(x) _Pragma(#x) +# define META_HPP_GCC_IGNORE_WARNING(w) META_HPP_GCC_PRAGMA_TO_STR(GCC diagnostic ignored w) +# define META_HPP_GCC_IGNORE_WARNINGS_PUSH() _Pragma("GCC diagnostic push") +# define META_HPP_GCC_IGNORE_WARNINGS_POP() _Pragma("GCC diagnostic pop") +#else +# define META_HPP_GCC_PRAGMA_TO_STR(x) +# define META_HPP_GCC_IGNORE_WARNING(w) +# define META_HPP_GCC_IGNORE_WARNINGS_PUSH() +# define META_HPP_GCC_IGNORE_WARNINGS_POP() +#endif + +#define META_HPP_IGNORE_OVERRIDE_WARNINGS_PUSH() \ + META_HPP_MSVC_IGNORE_WARNINGS_PUSH() \ + META_HPP_CLANG_IGNORE_WARNINGS_PUSH() \ + META_HPP_GCC_IGNORE_WARNINGS_PUSH() \ + META_HPP_CLANG_IGNORE_WARNING("-Winconsistent-missing-override") \ + META_HPP_CLANG_IGNORE_WARNING("-Wsuggest-override") + +#define META_HPP_IGNORE_OVERRIDE_WARNINGS_POP() \ + META_HPP_GCC_IGNORE_WARNINGS_POP() \ + META_HPP_CLANG_IGNORE_WARNINGS_POP() \ + META_HPP_MSVC_IGNORE_WARNINGS_POP() diff --git a/headers/meta.hpp/meta_base/exceptions.hpp b/headers/meta.hpp/meta_base/exceptions.hpp index 9b134fa..65cb337 100644 --- a/headers/meta.hpp/meta_base/exceptions.hpp +++ b/headers/meta.hpp/meta_base/exceptions.hpp @@ -23,6 +23,8 @@ namespace meta_hpp::detail enum class error_code { no_error, + bad_cast, + bad_const_access, bad_uvalue_access, @@ -38,6 +40,8 @@ namespace meta_hpp::detail switch ( error ) { case error_code::no_error: return "no error"; + case error_code::bad_cast: + return "bad cast"; case error_code::bad_const_access: return "bad const access"; case error_code::bad_uvalue_access: diff --git a/headers/meta.hpp/meta_ucast.hpp b/headers/meta.hpp/meta_ucast.hpp new file mode 100644 index 0000000..2481d87 --- /dev/null +++ b/headers/meta.hpp/meta_ucast.hpp @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "meta_base.hpp" + +namespace meta_hpp::detail +{ + template < + typename To, + typename From, + typename ToDT = std::remove_pointer_t, + typename FromDT = std::remove_pointer_t > + concept pointer_ucast_kind // + = (std::is_pointer_v && std::is_class_v) // + && (std::is_pointer_v && (std::is_class_v || std::is_void_v)) // + && (!std::is_const_v || std::is_const_v) // + && (!std::is_volatile_v || std::is_volatile_v); // + + template < + typename To, + typename From, + typename ToDT = std::remove_reference_t, + typename FromDT = std::remove_reference_t > + concept reference_ucast_kind // + = (std::is_reference_v && std::is_class_v) // + && (std::is_reference_v && std::is_class_v) // + && (!std::is_const_v || std::is_const_v) // + && (!std::is_volatile_v || std::is_volatile_v); // +} + +namespace meta_hpp +{ + template < typename To, typename From > + requires detail::pointer_ucast_kind + To ucast(From from); + + template < typename To, typename From > + requires detail::reference_ucast_kind + To ucast(From&& from); +} diff --git a/headers/meta.hpp/meta_ucast/ucast.hpp b/headers/meta.hpp/meta_ucast/ucast.hpp new file mode 100644 index 0000000..4d23e9b --- /dev/null +++ b/headers/meta.hpp/meta_ucast/ucast.hpp @@ -0,0 +1,93 @@ +/******************************************************************************* + * 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-2023, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include "../meta_base.hpp" +#include "../meta_ucast.hpp" + +#include "../meta_detail/type_registry.hpp" +#include "../meta_detail/value_utilities/utraits.hpp" + +namespace meta_hpp::detail +{ + struct polymorphic_meta_info { + const void* ptr{}; + class_type type{}; + }; + + template < typename T > + concept check_polymorphic_cast_support // + = requires(type_registry& r, const T& v) { + { v.get_most_derived_polymorphic_meta_info(r) } -> std::convertible_to; + }; +} + +namespace meta_hpp +{ + template < typename To, typename From > + requires detail::pointer_ucast_kind + To ucast(From from) { + using from_data_type = std::remove_pointer_t; + using to_data_type = std::remove_pointer_t; + + static_assert( + detail::check_polymorphic_cast_support, + "The type doesn't support ucasts. Use the META_HPP_ENABLE_POLYMORPHIC_CAST macro to fix it." + ); + + if ( from == nullptr ) { + return nullptr; + } + + if constexpr ( std::is_same_v, std::remove_cv_t> ) { + return from; + } + + detail::type_registry& registry{detail::type_registry::instance()}; + const detail::polymorphic_meta_info& meta_info{from->get_most_derived_polymorphic_meta_info(registry)}; + + // NOLINTNEXTLINE(*-const-cast) + void* most_derived_object_ptr = const_cast(meta_info.ptr); + + if constexpr ( std::is_void_v> ) { + return most_derived_object_ptr; + } else { + const class_type& to_class_type = registry.resolve_class_type(); + return static_cast(detail::pointer_upcast(most_derived_object_ptr, meta_info.type, to_class_type)); + } + } + + template < typename To, typename From > + requires detail::reference_ucast_kind + To ucast(From&& from) { + using from_data_type = std::remove_reference_t; + using to_data_type = std::remove_reference_t; + + static_assert( + detail::check_polymorphic_cast_support, + "The type doesn't support ucasts. Use the META_HPP_ENABLE_POLYMORPHIC_CAST macro to fix it." + ); + + if ( to_data_type* ptr = ucast(std::addressof(from)) ) { + return *ptr; + } + + throw_exception(error_code::bad_cast); + } +} + +#define META_HPP_ENABLE_POLYMORPHIC_CAST() \ +public: \ + META_HPP_IGNORE_OVERRIDE_WARNINGS_PUSH() \ + virtual ::meta_hpp::detail::polymorphic_meta_info get_most_derived_polymorphic_meta_info( \ + ::meta_hpp::detail::type_registry& registry \ + ) const { \ + using self_type = std::remove_cvref_t; \ + return ::meta_hpp::detail::polymorphic_meta_info{.ptr = this, .type = registry.resolve_class_type()}; \ + } \ + META_HPP_IGNORE_OVERRIDE_WARNINGS_POP() \ +private: