From 1b064e53e8b41fd949eb53d720973cdbfa3a36f2 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Sat, 14 Dec 2024 17:11:30 +0800 Subject: [PATCH 1/3] merge --- proxy.h | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/proxy.h b/proxy.h index e3d37a4..db2e591 100644 --- a/proxy.h +++ b/proxy.h @@ -15,6 +15,14 @@ #include #include +#ifdef __cpp_rtti +#ifndef __cpp_exceptions +#include // For std::abort() when "throw" is not available +#endif // __cpp_exceptions +#include +#include +#endif // __cpp_rtti + #if __has_cpp_attribute(msvc::no_unique_address) #define ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE msvc::no_unique_address #elif __has_cpp_attribute(no_unique_address) @@ -1233,6 +1241,10 @@ proxy make_proxy(T&& value) { ___PRO_DEBUG( \ accessor() noexcept { ::std::ignore = &accessor::__VA_ARGS__; }) +#ifdef __cpp_rtti +struct bad_proxy_cast : std::bad_cast {}; +#endif // __cpp_rtti + namespace details { template @@ -1431,6 +1443,111 @@ struct sign { template sign(const char (&str)[N]) -> sign; +#ifdef __cpp_rtti +#ifdef __cpp_exceptions +#define ___PRO_THROW(...) throw __VA_ARGS__ +#else +#define ___PRO_THROW(...) std::abort() +#endif // __cpp_exceptions + +struct proxy_cast_context { + const std::type_info* type_ptr; + bool is_ref; + bool is_const; + void* result_ptr; +}; + +template +struct proxy_cast_accessor_impl { + using _Self = add_qualifier_t< + proxy_cast_accessor_impl, overload_traits::qualifier>; + template + friend T proxy_cast(_Self self) { + static_assert(!std::is_rvalue_reference_v); + if (!access_proxy(self).has_value()) { ___PRO_THROW(bad_proxy_cast{}); } + if constexpr (std::is_lvalue_reference_v) { + using U = std::remove_reference_t; + void* result = nullptr; + proxy_cast_context ctx{.type_ptr = &typeid(T), .is_ref = true, + .is_const = std::is_const_v, .result_ptr = &result}; + proxy_invoke(access_proxy(std::forward<_Self>(self)), ctx); + if (result == nullptr) { ___PRO_THROW(bad_proxy_cast{}); } + return *static_cast(result); + } else { + std::optional> result; + proxy_cast_context ctx{.type_ptr = &typeid(T), .is_ref = false, + .is_const = false, .result_ptr = &result}; + proxy_invoke(access_proxy(std::forward<_Self>(self)), ctx); + if (!result.has_value()) { ___PRO_THROW(bad_proxy_cast{}); } + return std::move(*result); + } + } + template + friend T* proxy_cast(std::remove_reference_t<_Self>* self) noexcept + requires(std::is_lvalue_reference_v<_Self>) { + if (!access_proxy(*self).has_value()) { return nullptr; } + void* result = nullptr; + proxy_cast_context ctx{.type_ptr = &typeid(T), .is_ref = true, + .is_const = std::is_const_v, .result_ptr = &result}; + proxy_invoke(access_proxy(*self), ctx); + return static_cast(result); + } +}; + +#define ___PRO_DEF_PROXY_CAST_ACCESSOR(Q, ...) \ + template \ + struct accessor \ + : proxy_cast_accessor_impl {} +struct proxy_cast_dispatch { + template + void operator()(T&& self, proxy_cast_context ctx) { + if (typeid(T) == *ctx.type_ptr) { + if (ctx.is_ref) { + if constexpr (std::is_lvalue_reference_v) { + if (ctx.is_const || !std::is_const_v) { + *static_cast(ctx.result_ptr) = (void*)&self; + } + } + } else { + if constexpr (std::is_constructible_v, T>) { + static_cast>*>(ctx.result_ptr) + ->emplace(std::forward(self)); + } + } + } + } + ___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_PROXY_CAST_ACCESSOR) +}; +#undef ___PRO_DEF_PROXY_CAST_ACCESSOR + +class proxy_typeid_reflector { + public: + template + constexpr explicit proxy_typeid_reflector(std::in_place_type_t) + : type_(typeid(T)) {} + + template + struct accessor { + friend const std::type_info& proxy_typeid(const accessor& self) noexcept { + const proxy& p = access_proxy(self); + if (!p.has_value()) { return typeid(void); } + const proxy_typeid_reflector& refl = proxy_reflect(p); + return refl.type_; + } +___PRO_DEBUG( + accessor() noexcept { std::ignore = &accessor::_symbol_guard; } + + private: + static inline const std::type_info& _symbol_guard(const accessor& self) + noexcept { return proxy_typeid(self); } +) + }; + + const std::type_info& type_; +}; +#undef ___PRO_THROW +#endif // __cpp_rtti + } // namespace details template @@ -1476,6 +1593,21 @@ struct basic_facade_builder { template using support_destruction = basic_facade_builder< Cs, Rs, details::make_destructible(C, CL)>; +#ifdef __cpp_rtti + using support_indirect_rtti = add_indirect_convention< + details::proxy_cast_dispatch, + void(details::proxy_cast_context) &, + //void(details::proxy_cast_context) const&, + void(details::proxy_cast_context) &&> + ::template add_indirect_reflection; + using support_direct_rtti = add_direct_convention< + details::proxy_cast_dispatch, + void(details::proxy_cast_context) &, + //void(details::proxy_cast_context) const&, + void(details::proxy_cast_context) &&> + ::template add_direct_reflection; + using support_rtti = support_indirect_rtti; +#endif // __cpp_rtti using build = details::facade_impl; basic_facade_builder() = delete; }; From a889664522fb2a5fdc4346a96a4a21216907f648 Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 16 Dec 2024 22:47:26 +0800 Subject: [PATCH 2/3] Add unit tests --- proxy.h | 23 +-- tests/CMakeLists.txt | 1 + tests/proxy_rtti_tests.cpp | 293 +++++++++++++++++++++++++++++++++++++ 3 files changed, 307 insertions(+), 10 deletions(-) create mode 100644 tests/proxy_rtti_tests.cpp diff --git a/proxy.h b/proxy.h index db2e591..02bc5be 100644 --- a/proxy.h +++ b/proxy.h @@ -1459,8 +1459,8 @@ struct proxy_cast_context { template struct proxy_cast_accessor_impl { - using _Self = add_qualifier_t< - proxy_cast_accessor_impl, overload_traits::qualifier>; + using _Self = + add_qualifier_t, overload_traits::qualifier>; template friend T proxy_cast(_Self self) { static_assert(!std::is_rvalue_reference_v); @@ -1524,26 +1524,29 @@ class proxy_typeid_reflector { public: template constexpr explicit proxy_typeid_reflector(std::in_place_type_t) - : type_(typeid(T)) {} + : type_(&typeid(T)) {} + constexpr proxy_typeid_reflector(const proxy_typeid_reflector&) = default; template struct accessor { - friend const std::type_info& proxy_typeid(const accessor& self) noexcept { + friend const std::type_info& proxy_typeid( + const adl_accessor_arg_t& self) noexcept { const proxy& p = access_proxy(self); if (!p.has_value()) { return typeid(void); } const proxy_typeid_reflector& refl = proxy_reflect(p); - return refl.type_; + return *refl.type_; } ___PRO_DEBUG( accessor() noexcept { std::ignore = &accessor::_symbol_guard; } private: - static inline const std::type_info& _symbol_guard(const accessor& self) - noexcept { return proxy_typeid(self); } + static inline const std::type_info& _symbol_guard( + const adl_accessor_arg_t& self) noexcept + { return proxy_typeid(self); } ) }; - const std::type_info& type_; + const std::type_info* type_; }; #undef ___PRO_THROW #endif // __cpp_rtti @@ -1597,13 +1600,13 @@ struct basic_facade_builder { using support_indirect_rtti = add_indirect_convention< details::proxy_cast_dispatch, void(details::proxy_cast_context) &, - //void(details::proxy_cast_context) const&, + void(details::proxy_cast_context) const&, void(details::proxy_cast_context) &&> ::template add_indirect_reflection; using support_direct_rtti = add_direct_convention< details::proxy_cast_dispatch, void(details::proxy_cast_context) &, - //void(details::proxy_cast_context) const&, + void(details::proxy_cast_context) const&, void(details::proxy_cast_context) &&> ::template add_direct_reflection; using support_rtti = support_indirect_rtti; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6c188e3..859e5da 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,6 +24,7 @@ add_executable(msft_proxy_tests proxy_lifetime_tests.cpp proxy_reflection_tests.cpp proxy_regression_tests.cpp + proxy_rtti_tests.cpp proxy_traits_tests.cpp ) target_include_directories(msft_proxy_tests PRIVATE .) diff --git a/tests/proxy_rtti_tests.cpp b/tests/proxy_rtti_tests.cpp new file mode 100644 index 0000000..6a78b0e --- /dev/null +++ b/tests/proxy_rtti_tests.cpp @@ -0,0 +1,293 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include "proxy.h" + +namespace proxy_rtti_tests_details { + +struct TestFacade : pro::facade_builder + ::support_rtti + ::support_direct_rtti + ::build {}; + +} // namespace proxy_rtti_tests_details + +namespace details = proxy_rtti_tests_details; + +TEST(ProxyRttiTests, TestIndirectCast_Void_Fail) { + pro::proxy p; + bool exception_thrown = false; + try { + proxy_cast(*p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestIndirectCast_Ref_Succeed) { + int v = 123; + pro::proxy p = &v; + proxy_cast(*p) = 456; + ASSERT_EQ(v, 456); +} + +TEST(ProxyRttiTests, TestIndirectCast_Ref_Fail) { + int v = 123; + pro::proxy p = &v; + bool exception_thrown = false; + try { + proxy_cast(*p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestIndirectCast_ConstRef_Succeed) { + int v = 123; + pro::proxy p = &v; + const int& r = proxy_cast(*p); + ASSERT_EQ(&v, &r); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestIndirectCast_ConstRef_Fail) { + int v = 123; + pro::proxy p = &v; + bool exception_thrown = false; + try { + proxy_cast(*p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestIndirectCast_Copy_Succeed) { + int v1 = 123; + pro::proxy p = &v1; + int v2 = proxy_cast(*p); + ASSERT_EQ(v1, 123); + ASSERT_EQ(v2, 123); +} + +TEST(ProxyRttiTests, TestIndirectCast_Copy_Fail) { + int v = 123; + pro::proxy p = &v; + bool exception_thrown = false; + try { + proxy_cast(*p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestIndirectCast_Move_Succeed) { + const std::vector v1{1, 2, 3}; + auto p = pro::make_proxy(v1); + auto v2 = proxy_cast>(std::move(*p)); + ASSERT_EQ(v2, v1); + v2 = proxy_cast>(std::move(*p)); + ASSERT_TRUE(v2.empty()); +} + +TEST(ProxyRttiTests, TestIndirectCast_Move_Fail) { + auto p = pro::make_proxy(std::vector{1, 2, 3}); + bool exception_thrown = false; + try { + proxy_cast>(std::move(*p)); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestIndirectCast_Ptr_Succeed) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&*p); + static_assert(std::is_same_v); + ASSERT_EQ(ptr, &v); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestIndirectCast_Ptr_Fail) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&*p); + static_assert(std::is_same_v); + ASSERT_EQ(ptr, nullptr); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestIndirectCast_ConstPtr_Succeed) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&*p); + static_assert(std::is_same_v); + ASSERT_EQ(ptr, &v); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestIndirectCast_ConstPtr_Fail) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&*p); + static_assert(std::is_same_v); + ASSERT_EQ(ptr, nullptr); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestIndirectTypeid_Void) { + pro::proxy p; + ASSERT_EQ(proxy_typeid(*p), typeid(void)); +} + +TEST(ProxyRttiTests, TestIndirectTypeid_Value) { + int a = 123; + pro::proxy p = &a; + ASSERT_EQ(proxy_typeid(*p), typeid(int)); +} + +TEST(ProxyRttiTests, TestDirectCast_Void_Fail) { + pro::proxy p; + bool exception_thrown = false; + try { + proxy_cast(p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestDirectCast_Ref_Succeed) { + int v = 123; + pro::proxy p = &v; + *proxy_cast(p) = 456; + ASSERT_EQ(v, 456); +} + +TEST(ProxyRttiTests, TestDirectCast_Ref_Fail) { + int v = 123; + pro::proxy p = &v; + bool exception_thrown = false; + try { + proxy_cast(p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestDirectCast_ConstRef_Succeed) { + int v = 123; + pro::proxy p = &v; + int* const& r = proxy_cast(p); + ASSERT_EQ(&v, r); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestDirectCast_ConstRef_Fail) { + int v = 123; + pro::proxy p = &v; + bool exception_thrown = false; + try { + proxy_cast(p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestDirectCast_Copy_Succeed) { + int v1 = 123; + pro::proxy p = &v1; + int* v2 = proxy_cast(p); + ASSERT_EQ(v2, &v1); + ASSERT_EQ(v1, 123); +} + +TEST(ProxyRttiTests, TestDirectCast_Copy_Fail) { + int v = 123; + pro::proxy p = &v; + bool exception_thrown = false; + try { + proxy_cast(p); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); +} + +TEST(ProxyRttiTests, TestDirectCast_Move_Succeed) { + int v1 = 123; + pro::proxy p = &v1; + auto v2 = proxy_cast(std::move(p)); + ASSERT_EQ(v2, &v1); + ASSERT_EQ(v1, 123); + ASSERT_FALSE(p.has_value()); +} + +TEST(ProxyRttiTests, TestDirectCast_Move_Fail) { + int v1 = 123; + pro::proxy p = &v1; + bool exception_thrown = false; + try { + proxy_cast(std::move(p)); + } catch (const pro::bad_proxy_cast&) { + exception_thrown = true; + } + ASSERT_TRUE(exception_thrown); + ASSERT_FALSE(p.has_value()); +} + +TEST(ProxyRttiTests, TestDirectCast_Ptr_Succeed) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&p); + static_assert(std::is_same_v); + ASSERT_EQ(*ptr, &v); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestDirectCast_Ptr_Fail) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&p); + static_assert(std::is_same_v); + ASSERT_EQ(ptr, nullptr); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestDirectCast_ConstPtr_Succeed) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&p); + static_assert(std::is_same_v); + ASSERT_EQ(*ptr, &v); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestDirectCast_ConstPtr_Fail) { + int v = 123; + pro::proxy p = &v; + auto ptr = proxy_cast(&*p); + static_assert(std::is_same_v); + ASSERT_EQ(ptr, nullptr); + ASSERT_EQ(v, 123); +} + +TEST(ProxyRttiTests, TestDirectTypeid_Void) { + pro::proxy p; + ASSERT_EQ(proxy_typeid(p), typeid(void)); +} + +TEST(ProxyRttiTests, TestDirectTypeid_Value) { + int a = 123; + pro::proxy p = &a; + ASSERT_EQ(proxy_typeid(p), typeid(int*)); +} From 050e596e350ecd5f09e2529a5c34e8258527480b Mon Sep 17 00:00:00 2001 From: Mingxin Wang Date: Mon, 16 Dec 2024 23:16:08 +0800 Subject: [PATCH 3/3] Fix compilation error --- proxy.h | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/proxy.h b/proxy.h index 02bc5be..e570854 100644 --- a/proxy.h +++ b/proxy.h @@ -1520,11 +1520,10 @@ struct proxy_cast_dispatch { }; #undef ___PRO_DEF_PROXY_CAST_ACCESSOR -class proxy_typeid_reflector { - public: +struct proxy_typeid_reflector { template constexpr explicit proxy_typeid_reflector(std::in_place_type_t) - : type_(&typeid(T)) {} + : info(&typeid(T)) {} constexpr proxy_typeid_reflector(const proxy_typeid_reflector&) = default; template @@ -1534,7 +1533,7 @@ class proxy_typeid_reflector { const proxy& p = access_proxy(self); if (!p.has_value()) { return typeid(void); } const proxy_typeid_reflector& refl = proxy_reflect(p); - return *refl.type_; + return *refl.info; } ___PRO_DEBUG( accessor() noexcept { std::ignore = &accessor::_symbol_guard; } @@ -1546,7 +1545,7 @@ ___PRO_DEBUG( ) }; - const std::type_info* type_; + const std::type_info* info; }; #undef ___PRO_THROW #endif // __cpp_rtti @@ -1597,18 +1596,22 @@ struct basic_facade_builder { using support_destruction = basic_facade_builder< Cs, Rs, details::make_destructible(C, CL)>; #ifdef __cpp_rtti - using support_indirect_rtti = add_indirect_convention< - details::proxy_cast_dispatch, - void(details::proxy_cast_context) &, - void(details::proxy_cast_context) const&, - void(details::proxy_cast_context) &&> - ::template add_indirect_reflection; - using support_direct_rtti = add_direct_convention< - details::proxy_cast_dispatch, - void(details::proxy_cast_context) &, - void(details::proxy_cast_context) const&, - void(details::proxy_cast_context) &&> - ::template add_direct_reflection; + using support_indirect_rtti = + basic_facade_builder< + details::add_conv_t>, + details::add_tuple_t>, C>; + using support_direct_rtti = + basic_facade_builder< + details::add_conv_t>, + details::add_tuple_t>, C>; using support_rtti = support_indirect_rtti; #endif // __cpp_rtti using build = details::facade_impl;