Skip to content
This repository was archived by the owner on Jan 29, 2026. It is now read-only.

Commit 973b4b1

Browse files
authored
Add direct support for RTTI (#221)
1 parent 016ec22 commit 973b4b1

3 files changed

Lines changed: 432 additions & 0 deletions

File tree

proxy.h

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@
1515
#include <type_traits>
1616
#include <utility>
1717

18+
#ifdef __cpp_rtti
19+
#ifndef __cpp_exceptions
20+
#include <cstdlib> // For std::abort() when "throw" is not available
21+
#endif // __cpp_exceptions
22+
#include <optional>
23+
#include <typeinfo>
24+
#endif // __cpp_rtti
25+
1826
#if __has_cpp_attribute(msvc::no_unique_address)
1927
#define ___PRO_NO_UNIQUE_ADDRESS_ATTRIBUTE msvc::no_unique_address
2028
#elif __has_cpp_attribute(no_unique_address)
@@ -1240,6 +1248,10 @@ using proxy_view = proxy<observer_facade<F>>;
12401248
___PRO_DEBUG( \
12411249
accessor() noexcept { ::std::ignore = &accessor::__VA_ARGS__; })
12421250

1251+
#ifdef __cpp_rtti
1252+
struct bad_proxy_cast : std::bad_cast {};
1253+
#endif // __cpp_rtti
1254+
12431255
namespace details {
12441256

12451257
template <class F, class C>
@@ -1537,6 +1549,113 @@ struct sign {
15371549
template <std::size_t N>
15381550
sign(const char (&str)[N]) -> sign<N>;
15391551

1552+
#ifdef __cpp_rtti
1553+
#ifdef __cpp_exceptions
1554+
#define ___PRO_THROW(...) throw __VA_ARGS__
1555+
#else
1556+
#define ___PRO_THROW(...) std::abort()
1557+
#endif // __cpp_exceptions
1558+
1559+
struct proxy_cast_context {
1560+
const std::type_info* type_ptr;
1561+
bool is_ref;
1562+
bool is_const;
1563+
void* result_ptr;
1564+
};
1565+
1566+
template <class F, class C, class O>
1567+
struct proxy_cast_accessor_impl {
1568+
using _Self =
1569+
add_qualifier_t<adl_accessor_arg_t<F, C>, overload_traits<O>::qualifier>;
1570+
template <class T>
1571+
friend T proxy_cast(_Self self) {
1572+
static_assert(!std::is_rvalue_reference_v<T>);
1573+
if (!access_proxy<F>(self).has_value()) { ___PRO_THROW(bad_proxy_cast{}); }
1574+
if constexpr (std::is_lvalue_reference_v<T>) {
1575+
using U = std::remove_reference_t<T>;
1576+
void* result = nullptr;
1577+
proxy_cast_context ctx{.type_ptr = &typeid(T), .is_ref = true,
1578+
.is_const = std::is_const_v<U>, .result_ptr = &result};
1579+
proxy_invoke<C, O>(access_proxy<F>(std::forward<_Self>(self)), ctx);
1580+
if (result == nullptr) { ___PRO_THROW(bad_proxy_cast{}); }
1581+
return *static_cast<U*>(result);
1582+
} else {
1583+
std::optional<std::remove_const_t<T>> result;
1584+
proxy_cast_context ctx{.type_ptr = &typeid(T), .is_ref = false,
1585+
.is_const = false, .result_ptr = &result};
1586+
proxy_invoke<C, O>(access_proxy<F>(std::forward<_Self>(self)), ctx);
1587+
if (!result.has_value()) { ___PRO_THROW(bad_proxy_cast{}); }
1588+
return std::move(*result);
1589+
}
1590+
}
1591+
template <class T>
1592+
friend T* proxy_cast(std::remove_reference_t<_Self>* self) noexcept
1593+
requires(std::is_lvalue_reference_v<_Self>) {
1594+
if (!access_proxy<F>(*self).has_value()) { return nullptr; }
1595+
void* result = nullptr;
1596+
proxy_cast_context ctx{.type_ptr = &typeid(T), .is_ref = true,
1597+
.is_const = std::is_const_v<T>, .result_ptr = &result};
1598+
proxy_invoke<C, O>(access_proxy<F>(*self), ctx);
1599+
return static_cast<T*>(result);
1600+
}
1601+
};
1602+
1603+
#define ___PRO_DEF_PROXY_CAST_ACCESSOR(Q, ...) \
1604+
template <class F, class C> \
1605+
struct accessor<F, C, void(proxy_cast_context) Q> \
1606+
: proxy_cast_accessor_impl<F, C, void(proxy_cast_context) Q> {}
1607+
struct proxy_cast_dispatch {
1608+
template <class T>
1609+
void operator()(T&& self, proxy_cast_context ctx) {
1610+
if (typeid(T) == *ctx.type_ptr) {
1611+
if (ctx.is_ref) {
1612+
if constexpr (std::is_lvalue_reference_v<T>) {
1613+
if (ctx.is_const || !std::is_const_v<T>) {
1614+
*static_cast<void**>(ctx.result_ptr) = (void*)&self;
1615+
}
1616+
}
1617+
} else {
1618+
if constexpr (std::is_constructible_v<std::decay_t<T>, T>) {
1619+
static_cast<std::optional<std::decay_t<T>>*>(ctx.result_ptr)
1620+
->emplace(std::forward<T>(self));
1621+
}
1622+
}
1623+
}
1624+
}
1625+
___PRO_DEF_FREE_ACCESSOR_TEMPLATE(___PRO_DEF_PROXY_CAST_ACCESSOR)
1626+
};
1627+
#undef ___PRO_DEF_PROXY_CAST_ACCESSOR
1628+
1629+
struct proxy_typeid_reflector {
1630+
template <class T>
1631+
constexpr explicit proxy_typeid_reflector(std::in_place_type_t<T>)
1632+
: info(&typeid(T)) {}
1633+
constexpr proxy_typeid_reflector(const proxy_typeid_reflector&) = default;
1634+
1635+
template <class F, class R>
1636+
struct accessor {
1637+
friend const std::type_info& proxy_typeid(
1638+
const adl_accessor_arg_t<F, R>& self) noexcept {
1639+
const proxy<F>& p = access_proxy<F>(self);
1640+
if (!p.has_value()) { return typeid(void); }
1641+
const proxy_typeid_reflector& refl = proxy_reflect<R>(p);
1642+
return *refl.info;
1643+
}
1644+
___PRO_DEBUG(
1645+
accessor() noexcept { std::ignore = &accessor::_symbol_guard; }
1646+
1647+
private:
1648+
static inline const std::type_info& _symbol_guard(
1649+
const adl_accessor_arg_t<F, R>& self) noexcept
1650+
{ return proxy_typeid(self); }
1651+
)
1652+
};
1653+
1654+
const std::type_info* info;
1655+
};
1656+
#undef ___PRO_THROW
1657+
#endif // __cpp_rtti
1658+
15401659
} // namespace details
15411660

15421661
template <class Cs, class Rs, proxiable_ptr_constraints C>
@@ -1582,6 +1701,25 @@ struct basic_facade_builder {
15821701
template <constraint_level CL>
15831702
using support_destruction = basic_facade_builder<
15841703
Cs, Rs, details::make_destructible(C, CL)>;
1704+
#ifdef __cpp_rtti
1705+
using support_indirect_rtti =
1706+
basic_facade_builder<
1707+
details::add_conv_t<Cs, details::conv_impl<false,
1708+
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
1709+
void(details::proxy_cast_context) const&,
1710+
void(details::proxy_cast_context) &&>>,
1711+
details::add_tuple_t<Rs, details::refl_impl<false,
1712+
details::proxy_typeid_reflector>>, C>;
1713+
using support_direct_rtti =
1714+
basic_facade_builder<
1715+
details::add_conv_t<Cs, details::conv_impl<true,
1716+
details::proxy_cast_dispatch, void(details::proxy_cast_context) &,
1717+
void(details::proxy_cast_context) const&,
1718+
void(details::proxy_cast_context) &&>>,
1719+
details::add_tuple_t<Rs, details::refl_impl<true,
1720+
details::proxy_typeid_reflector>>, C>;
1721+
using support_rtti = support_indirect_rtti;
1722+
#endif // __cpp_rtti
15851723
template <class F>
15861724
using add_view = add_direct_convention<
15871725
details::proxy_view_dispatch, details::proxy_view_overload<F>>;

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_executable(msft_proxy_tests
2424
proxy_lifetime_tests.cpp
2525
proxy_reflection_tests.cpp
2626
proxy_regression_tests.cpp
27+
proxy_rtti_tests.cpp
2728
proxy_traits_tests.cpp
2829
proxy_view_tests.cpp
2930
)

0 commit comments

Comments
 (0)