handle deferred RTTI without UB

This commit is contained in:
Jean-Louis Leroy
2025-05-26 18:11:04 -04:00
committed by Jean-Louis Leroy
parent e8c57b44ff
commit ae60941daf
11 changed files with 232 additions and 227 deletions

View File

@@ -42,18 +42,18 @@ struct custom_rtti : bom::policies::rtti {
template<typename T>
static auto static_type() -> bom::type_id {
if constexpr (is_polymorphic<T>) {
return T::static_type;
return reinterpret_cast<bom::type_id>(T::static_type);
} else {
return 0;
return nullptr;
}
}
template<typename T>
static auto dynamic_type(const T& obj) -> bom::type_id {
if constexpr (is_polymorphic<T>) {
return obj.type;
return reinterpret_cast<bom::type_id>(obj.type);
} else {
return 0;
return nullptr;
}
}
};

View File

@@ -86,7 +86,7 @@ struct custom_rtti : bom::policies::rtti {
template<typename T>
static auto static_type() -> bom::type_id {
if constexpr (std::is_base_of_v<Animal, T>) {
return T::type_info.id;
return reinterpret_cast<bom::type_id>(T::type_info.id);
} else {
return 0;
}
@@ -95,9 +95,9 @@ struct custom_rtti : bom::policies::rtti {
template<typename T>
static auto dynamic_type(const T& obj) -> bom::type_id {
if constexpr (std::is_base_of_v<Animal, T>) {
return obj.type;
return reinterpret_cast<bom::type_id>(obj.type);
} else {
return 0;
return nullptr;
}
}

View File

@@ -239,7 +239,6 @@ struct compiler : detail::generic_compiler {
auto initialize();
void install_global_tables();
void resolve_static_type_ids();
void augment_classes();
void collect_transitive_bases(class_* cls, class_* base);
void calculate_transitive_derived(class_& cls);
@@ -283,7 +282,6 @@ void compiler<Registry>::install_global_tables() {
template<class Registry>
auto compiler<Registry>::compile() {
resolve_static_type_ids();
augment_classes();
augment_methods();
assign_slots();
@@ -306,52 +304,6 @@ template<class Registry>
compiler<Registry>::compiler() {
}
template<class Registry>
void compiler<Registry>::resolve_static_type_ids() {
if constexpr (Registry::template has_policy<
policies::deferred_static_rtti>) {
using namespace detail;
auto resolve = [](type_id* p) {
auto pf = reinterpret_cast<type_id (*)()>(*p);
*p = pf();
};
for (auto& ci : Registry::classes) {
resolve(&ci.type);
if (*ci.last_base == 0) {
for (auto& ti : range{ci.first_base, ci.last_base}) {
resolve(&ti);
}
*ci.last_base = 1;
}
}
for (auto& method : Registry::methods) {
for (auto& ti : range{method.vp_begin, method.vp_end}) {
if (*method.vp_end == 0) {
resolve(&ti);
}
for (auto& overrider : method.specs) {
if (*overrider.vp_end == 0) {
for (auto& ti :
range{overrider.vp_begin, overrider.vp_end}) {
resolve(&ti);
}
*overrider.vp_end = 1;
}
}
}
*method.vp_end = 1;
}
}
}
template<class Registry>
void compiler<Registry>::collect_transitive_bases(class_* cls, class_* base) {
if (base->mark == class_mark) {
@@ -378,6 +330,10 @@ void compiler<Registry>::augment_classes() {
// type_info object per class. However, it guarantees that the
// type_index for a class has a unique value.
for (auto& cr : Registry::classes) {
if constexpr (Registry::deferred_static_rtti) {
static_cast<deferred_class_info&>(cr).resolve_type_ids();
}
{
indent _(trace);
++trace << type_name(cr.type) << ": "
@@ -394,11 +350,6 @@ void compiler<Registry>::augment_classes() {
rtc->static_vptr = cr.static_vptr;
}
// In the unlikely case that a class does have more than one
// associated ti*, collect them in a vector. We don't use an
// unordered_set because, again, this situation is highly
// unlikely, and, were it to occur, the number of distinct ti*s
// would probably be small.
if (std::find(
rtc->type_ids.begin(), rtc->type_ids.end(), cr.type) ==
rtc->type_ids.end()) {
@@ -550,7 +501,11 @@ void compiler<Registry>::augment_methods() {
auto meth_iter = methods.begin();
for (auto& meth_info : Registry::methods) {
++trace << type_name(meth_info.method_type) << " "
if constexpr (Registry::deferred_static_rtti) {
static_cast<deferred_method_info&>(meth_info).resolve_type_ids();
}
++trace << type_name(meth_info.method_type_id) << " "
<< range{meth_info.vp_begin, meth_info.vp_end} << "\n";
indent _(trace);
@@ -583,13 +538,13 @@ void compiler<Registry>::augment_methods() {
}
if (Registry::template policy<policies::rtti>::type_index(
meth_info.return_type) !=
meth_info.return_type_id) !=
Registry::template policy<policies::rtti>::type_index(
Registry::template policy<policies::rtti>::template static_type<
void>())) {
auto covariant_return_iter = class_map.find(
Registry::template policy<policies::rtti>::type_index(
meth_info.return_type));
meth_info.return_type_id));
if (covariant_return_iter != class_map.end()) {
meth_iter->covariant_return_type =
@@ -609,6 +564,11 @@ void compiler<Registry>::augment_methods() {
auto spec_iter = meth_iter->specs.begin();
for (auto& overrider_info : meth_info.specs) {
if constexpr (Registry::deferred_static_rtti) {
static_cast<deferred_overrider_info&>(overrider_info)
.resolve_type_ids();
}
spec_iter->method_index = meth_iter - methods.begin();
spec_iter->spec_index = spec_iter - meth_iter->specs.begin();
@@ -733,7 +693,7 @@ void compiler<Registry>::assign_tree_slots(class_& cls, std::size_t base_slot) {
for (const auto& mp : cls.used_by_vp) {
++trace << " in " << cls << " for "
<< type_name(mp.method->info->method_type) << " parameter "
<< type_name(mp.method->info->method_type_id) << " parameter "
<< mp.param << ": " << next_slot << "\n";
mp.method->slots[mp.param] = next_slot++;
}
@@ -759,8 +719,8 @@ void compiler<Registry>::assign_lattice_slots(class_& cls) {
if (!cls.used_by_vp.empty()) {
for (const auto& mp : cls.used_by_vp) {
++trace << " in " << cls << " for "
<< type_name(mp.method->info->method_type) << " parameter "
<< mp.param << "\n";
<< type_name(mp.method->info->method_type_id)
<< " parameter " << mp.param << "\n";
indent _(trace);
@@ -828,7 +788,7 @@ void compiler<Registry>::build_dispatch_tables() {
for (auto& m : methods) {
++trace << "Building dispatch table for "
<< type_name(m.info->method_type) << "\n";
<< type_name(m.info->method_type_id) << "\n";
indent _(trace);
auto dims = m.arity();
@@ -1119,7 +1079,7 @@ void compiler<Registry>::write_global_data() {
if constexpr (trace_enabled) {
++trace << rflush(4, Registry::dispatch_data.size()) << " "
<< " method #" << m.dispatch_table[0]->method_index
<< " " << type_name(m.info->method_type) << "\n";
<< " " << type_name(m.info->method_type_id) << "\n";
indent _(trace);
for (auto& entry : m.dispatch_table) {
@@ -1141,7 +1101,7 @@ void compiler<Registry>::write_global_data() {
for (auto& m : methods) {
indent _(trace);
++trace << "method #"
<< " " << type_name(m.info->method_type) << "\n";
<< " " << type_name(m.info->method_type_id) << "\n";
for (auto& overrider : m.specs) {
if (overrider.next) {
@@ -1184,7 +1144,7 @@ void compiler<Registry>::write_global_data() {
auto spec = method.dispatch_table[entry.group_index];
trace << "spec #" << spec->spec_index << "\n";
indent _(trace);
++trace << type_name(method.info->method_type) << "\n";
++trace << type_name(method.info->method_type_id) << "\n";
++trace << spec_name(method, spec);
BOOST_ASSERT(gv_iter + 1 <= gv_last);
*gv_iter++ = spec->pf;
@@ -1192,7 +1152,7 @@ void compiler<Registry>::write_global_data() {
trace << "vp #" << entry.vp_index << " group #"
<< entry.group_index << "\n";
indent _(trace);
++trace << type_name(method.info->method_type);
++trace << type_name(method.info->method_type_id);
BOOST_ASSERT(gv_iter + 1 <= gv_last);
if (entry.vp_index == 0) {

View File

@@ -35,13 +35,10 @@
namespace boost::openmethod {
// =============================================================================
// Registering classes
// Helpers
namespace detail {
// =============================================================================
// Helpers
template<class Registry, class Class>
constexpr bool is_polymorphic = Registry::rtti::template is_polymorphic<Class>;
@@ -72,67 +69,46 @@ struct extract_registry<Type1, Type2, MoreTypes...> {
typename extract_registry<Type2, MoreTypes...>::others, Type1>;
};
template<class Registry, class Class>
auto collect_static_type_id() -> type_id {
using rtti = typename Registry::rtti;
template<class Registry, class... Class>
struct init_type_ids;
if constexpr (Registry::template has_policy<
policies::deferred_static_rtti>) {
return reinterpret_cast<type_id>(rtti::template static_type<Class>);
} else {
return rtti::template static_type<Class>();
template<class Registry, class... Class>
struct init_type_ids<Registry, mp11::mp_list<Class...>> {
static auto fn(type_id* ids) {
(..., (*ids++ = Registry::rtti::template static_type<Class>()));
return ids;
}
}
template<class TypeList, class Registry>
struct type_id_list;
template<typename... T, class Registry>
struct type_id_list<mp11::mp_list<T...>, Registry> {
// If using deferred 'static_type', add an extra element in 'value',
// default-initialized to zero, indicating the ids need to be resolved. Set
// to 1 after this is done.
static constexpr std::size_t values = sizeof...(T) +
Registry::template has_policy<policies::deferred_static_rtti>;
static type_id value[values];
static type_id* begin;
static type_id* end;
};
template<typename... T, class Registry>
type_id type_id_list<mp11::mp_list<T...>, Registry>::value[values] = {
collect_static_type_id<Registry, T>()...};
template<typename... T, class Registry>
type_id* type_id_list<mp11::mp_list<T...>, Registry>::begin = value;
template<typename... T, class Registry>
type_id* type_id_list<mp11::mp_list<T...>, Registry>::end =
value + sizeof...(T);
template<class Registry>
struct type_id_list<mp11::mp_list<>, Registry> {
static constexpr type_id* const begin = nullptr;
static constexpr auto end = begin;
};
template<class...>
struct class_declaration_aux;
struct use_class_aux;
template<class Registry, class Class, typename... Bases>
struct class_declaration_aux<Registry, mp11::mp_list<Class, Bases...>>
: class_info {
class_declaration_aux() {
this->type = collect_static_type_id<Registry, Class>();
this->first_base =
type_id_list<mp11::mp_list<Bases...>, Registry>::begin;
this->last_base = type_id_list<mp11::mp_list<Bases...>, Registry>::end;
Registry::classes.push_back(*this);
struct use_class_aux<Registry, mp11::mp_list<Class, Bases...>>
: std::conditional_t<
Registry::deferred_static_rtti, detail::deferred_class_info,
detail::class_info> {
inline static type_id bases[sizeof...(Bases)];
use_class_aux() {
this->first_base = bases;
this->last_base = bases + sizeof...(Bases);
this->is_abstract = std::is_abstract_v<Class>;
this->static_vptr = &Registry::template static_vptr<Class>;
if constexpr (!Registry::deferred_static_rtti) {
resolve_type_ids();
}
Registry::classes.push_back(*this);
}
~class_declaration_aux() {
void resolve_type_ids() {
this->type = Registry::rtti::template static_type<Class>();
auto iter = bases;
(..., (*iter++ = Registry::rtti::template static_type<Bases>()));
}
~use_class_aux() {
Registry::classes.remove(*this);
}
};
@@ -291,7 +267,7 @@ struct use_classes {
std::tuple,
boost::mp11::mp_transform_q<
boost::mp11::mp_bind_front<
detail::class_declaration_aux,
detail::use_class_aux,
typename detail::extract_registry<Classes...>::registry>,
boost::mp11::mp_apply<
detail::inheritance_map,
@@ -965,7 +941,9 @@ class method;
template<
typename Name, typename... Parameters, typename ReturnType, class Registry>
class method<Name, auto(Parameters...)->ReturnType, Registry>
: public detail::method_info {
: public std::conditional_t<
Registry::deferred_static_rtti, detail::deferred_method_info,
detail::method_info> {
// Aliases used in implementation only. Everything extracted from template
// arguments is capitalized like the arguments themselves.
using RegistryType = Registry;
@@ -987,6 +965,8 @@ class method<Name, auto(Parameters...)->ReturnType, Registry>
(true && ... &&
detail::using_same_registry<Registry, Parameters>::value));
inline static type_id vp_type_ids[Arity];
static std::size_t slots_strides[2 * Arity - 1];
// Slots followed by strides. No stride for first virtual argument.
// For 1-method: the offset of the method in the method table, which
@@ -996,6 +976,8 @@ class method<Name, auto(Parameters...)->ReturnType, Registry>
// the dispatch table, followed by the offset of the second argument and
// the stride in the second dimension, etc.
void resolve_type_ids();
template<typename ArgType>
auto vptr(const ArgType& arg) const -> vptr_type;
@@ -1032,6 +1014,8 @@ class method<Name, auto(Parameters...)->ReturnType, Registry>
method(method&&) = delete;
~method();
void resolve(); // perhaps virtual, perhaps not
public:
// Public aliases.
using name_type = Name;
@@ -1059,16 +1043,20 @@ class method<Name, auto(Parameters...)->ReturnType, Registry>
typename... OverriderParameters>
struct thunk<Overrider, OverriderReturn (*)(OverriderParameters...)> {
static auto fn(detail::remove_virtual<Parameters>... arg) -> ReturnType;
using OverriderParameterTypeIds = detail::type_id_list<
detail::overrider_virtual_types<
DeclaredParameters, mp11::mp_list<OverriderParameters...>,
Registry>,
using OverriderVirtualParameters = detail::overrider_virtual_types<
DeclaredParameters, mp11::mp_list<OverriderParameters...>,
Registry>;
};
template<auto Function, typename FnReturnType>
struct override_impl {
struct override_impl
: std::conditional_t<
Registry::deferred_static_rtti, detail::deferred_overrider_info,
detail::overrider_info> {
explicit override_impl(FunctionPointer* next = nullptr);
void resolve_type_ids();
inline static type_id vp_type_ids[Arity];
};
template<auto Function, typename FunctionType>
@@ -1129,23 +1117,35 @@ constexpr bool is_method = std::is_base_of_v<detail::method_info, T>;
template<
typename Name, typename... Parameters, typename ReturnType, class Registry>
method<Name, auto(Parameters...)->ReturnType, Registry>::method() {
method_info::slots_strides_ptr = slots_strides;
this->slots_strides_ptr = slots_strides;
using virtual_type_ids = detail::type_id_list<
boost::mp11::mp_transform_q<
boost::mp11::mp_bind_back<detail::virtual_type, Registry>,
VirtualParameters>,
Registry>;
method_info::vp_begin = virtual_type_ids::begin;
method_info::vp_end = virtual_type_ids::end;
method_info::not_implemented =
if constexpr (!Registry::deferred_static_rtti) {
resolve_type_ids();
}
this->vp_begin = vp_type_ids;
this->vp_end = vp_type_ids + Arity;
this->not_implemented =
reinterpret_cast<void (*)()>(not_implemented_handler);
method_info::method_type = rtti::template static_type<method>();
method_info::return_type = rtti::template static_type<
typename virtual_traits<ReturnType, Registry>::virtual_type>();
Registry::methods.push_back(*this);
}
template<
typename Name, typename... Parameters, typename ReturnType, class Registry>
void method<
Name, auto(Parameters...)->ReturnType, Registry>::resolve_type_ids() {
using namespace detail;
this->method_type_id = rtti::template static_type<method>();
this->return_type_id = rtti::template static_type<
typename virtual_traits<ReturnType, Registry>::virtual_type>();
init_type_ids<
Registry,
mp11::mp_transform_q<
mp11::mp_bind_back<virtual_type, Registry>,
VirtualParameters>>::fn(this->vp_type_ids);
}
template<
typename Name, typename... Parameters, typename ReturnType, class Registry>
std::size_t method<Name, auto(Parameters...)->ReturnType, Registry>::
@@ -1422,33 +1422,47 @@ auto method<Name, auto(Parameters...)->ReturnType, Registry>::
template<
typename Name, typename... Parameters, typename ReturnType, class Registry>
template<auto Function, typename FnReturnType>
method<Name, auto(Parameters...)->ReturnType, Registry>::override_impl<
method<Name, ReturnType(Parameters...), Registry>::override_impl<
Function, FnReturnType>::override_impl(FunctionPointer* p_next) {
using namespace detail;
// Work around MSVC bug: using &next<Function> as a default value
// for 'next' confuses it about Parameters not being expanded.
if (!p_next) {
p_next = &next<Function>;
}
static overrider_info info;
if (info.method) {
BOOST_ASSERT(info.method == &fn);
if (this->method) {
BOOST_ASSERT(this->method == &fn);
return;
}
info.method = &fn;
info.return_type = Registry::rtti::template static_type<
typename virtual_traits<FnReturnType, Registry>::virtual_type>();
info.type = Registry::rtti::template static_type<decltype(Function)>();
info.next = reinterpret_cast<void (**)()>(p_next);
this->method = &fn;
if constexpr (!Registry::deferred_static_rtti) {
resolve_type_ids();
}
this->next = reinterpret_cast<void (**)()>(
p_next ? p_next : &method::next<Function>);
using Thunk = thunk<Function, decltype(Function)>;
info.pf = reinterpret_cast<void (*)()>(Thunk::fn);
info.vp_begin = Thunk::OverriderParameterTypeIds::begin;
info.vp_end = Thunk::OverriderParameterTypeIds::end;
fn.specs.push_back(info);
this->pf = reinterpret_cast<void (*)()>(Thunk::fn);
this->vp_begin = vp_type_ids;
this->vp_end = vp_type_ids + Arity;
fn.specs.push_back(*this);
}
template<
typename Name, typename... Parameters, typename ReturnType, class Registry>
template<auto Function, typename FnReturnType>
void method<Name, auto(Parameters...)->ReturnType, Registry>::override_impl<
Function, FnReturnType>::resolve_type_ids() {
using namespace detail;
this->return_type = Registry::rtti::template static_type<
typename virtual_traits<FnReturnType, Registry>::virtual_type>();
this->type = Registry::rtti::template static_type<decltype(Function)>();
using Thunk = thunk<Function, decltype(Function)>;
detail::
init_type_ids<Registry, typename Thunk::OverriderVirtualParameters>::fn(
this->vp_type_ids);
}
} // namespace boost::openmethod

View File

@@ -29,12 +29,23 @@ union word {
word* pw;
};
#if defined(UINTPTR_MAX)
using uintptr = std::uintptr_t;
constexpr uintptr uintptr_max = UINTPTR_MAX;
#else
static_assert(
sizeof(std::size_t) == sizeof(void*),
"This implementation requires that size_t and void* have the same size.");
using uintptr = std::size_t;
constexpr uintptr uintptr_max = (std::numeric_limits<std::size_t>::max)();
#endif
} // namespace detail
using type_id = std::uintptr_t;
using vptr_type = const detail::word*;
using type_id = const void*;
template<typename T>
struct virtual_;
@@ -119,6 +130,10 @@ struct class_info : static_list<class_info>::static_link {
}
};
struct deferred_class_info : class_info {
virtual void resolve_type_ids() = 0;
};
// -----------
// method info
@@ -129,8 +144,8 @@ struct method_info : static_list<method_info>::static_link {
type_id* vp_end;
static_list<overrider_info> specs;
void (*not_implemented)();
type_id method_type;
type_id return_type;
type_id method_type_id;
type_id return_type_id;
std::size_t* slots_strides_ptr;
auto arity() const {
@@ -138,6 +153,10 @@ struct method_info : static_list<method_info>::static_link {
}
};
struct deferred_method_info : method_info {
virtual void resolve_type_ids() = 0;
};
struct overrider_info : static_list<overrider_info>::static_link {
~overrider_info() {
method->specs.remove(*this);
@@ -151,6 +170,10 @@ struct overrider_info : static_list<overrider_info>::static_link {
void (*pf)();
};
struct deferred_overrider_info : overrider_info {
virtual void resolve_type_ids() = 0;
};
} // namespace detail
} // namespace boost::openmethod

View File

@@ -26,7 +26,7 @@ struct fast_perfect_hash : type_hash {
template<class Registry>
struct fn {
inline static type_id hash_mult;
inline static std::size_t hash_mult;
inline static std::size_t hash_shift;
inline static std::size_t hash_min;
inline static std::size_t hash_max;
@@ -38,8 +38,10 @@ struct fast_perfect_hash : type_hash {
};
BOOST_FORCEINLINE
static auto hash(type_id type) -> type_id {
auto index = (hash_mult * type) >> hash_shift;
static auto hash(type_id type) -> std::size_t {
auto index =
(hash_mult * reinterpret_cast<detail::uintptr>(type)) >>
hash_shift;
if constexpr (Registry::template has_policy<runtime_checks>) {
check(index, type);
@@ -97,7 +99,7 @@ void fast_perfect_hash::fn<Registry>::initialize(
++M;
}
std::uniform_int_distribution<type_id> uniform_dist;
std::uniform_int_distribution<std::size_t> uniform_dist;
for (std::size_t pass = 0; pass < 4; ++pass, ++M) {
hash_shift = 8 * sizeof(type_id) - M;
@@ -117,7 +119,8 @@ void fast_perfect_hash::fn<Registry>::initialize(
buckets.resize(hash_size);
while (attempts < 100000) {
std::fill(buckets.begin(), buckets.end(), static_cast<type_id>(-1));
std::fill(
buckets.begin(), buckets.end(), type_id(detail::uintptr_max));
++attempts;
++total_attempts;
hash_mult = uniform_dist(rnd) | 1;
@@ -126,11 +129,13 @@ void fast_perfect_hash::fn<Registry>::initialize(
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
auto type = *type_iter;
auto index = (type * hash_mult) >> hash_shift;
auto index =
(detail::uintptr(type) * hash_mult) >> hash_shift;
hash_min = (std::min)(hash_min, index);
hash_max = (std::max)(hash_max, index);
if (buckets[index] != static_cast<type_id>(-1)) {
if (detail::uintptr(buckets[index]) !=
detail::uintptr_max) {
goto collision;
}

View File

@@ -26,14 +26,12 @@ struct std_rtti : rtti {
template<class Class>
static auto static_type() -> type_id {
auto tip = &typeid(Class);
return reinterpret_cast<type_id>(tip);
return &typeid(Class);
}
template<class Class>
static auto dynamic_type(const Class& obj) -> type_id {
auto tip = &typeid(obj);
return reinterpret_cast<type_id>(tip);
return &typeid(obj);
}
template<typename Stream>

View File

@@ -44,7 +44,7 @@ struct vptr_vector : extern_vptr {
for (auto iter = first; iter != last; ++iter) {
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
size = (std::max)(size, *type_iter);
size = (std::max)(size, std::size_t(*type_iter));
}
}
@@ -60,11 +60,13 @@ struct vptr_vector : extern_vptr {
for (auto iter = first; iter != last; ++iter) {
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
auto index = *type_iter;
std::size_t index;
if constexpr (Registry::template has_policy<type_hash>) {
index =
Registry::template policy<type_hash>::hash(index);
index = Registry::template policy<type_hash>::hash(
*type_iter);
} else {
index = std::size_t(*type_iter);
}
if constexpr (Registry::template has_policy<
@@ -81,10 +83,15 @@ struct vptr_vector : extern_vptr {
template<class Class>
static auto dynamic_vptr(const Class& arg) -> const vptr_type& {
auto index = Registry::template policy<rtti>::dynamic_type(arg);
auto dynamic_type =
Registry::template policy<rtti>::dynamic_type(arg);
std::size_t index;
if constexpr (Registry::template has_policy<type_hash>) {
index = Registry::template policy<type_hash>::hash(index);
index =
Registry::template policy<type_hash>::hash(dynamic_type);
} else {
index = std::size_t(dynamic_type);
}
if constexpr (Registry::template has_policy<indirect_vptr>) {

View File

@@ -185,6 +185,8 @@ struct registry : detail::registry_base {
using rtti = policy<policies::rtti>;
using error_handler = policy<policies::error_handler>;
static constexpr auto runtime_checks = has_policy<policies::runtime_checks>;
static constexpr auto deferred_static_rtti =
has_policy<policies::deferred_static_rtti>;
static constexpr auto trace = has_policy<policies::trace>;
};

View File

@@ -149,12 +149,12 @@ static_assert(detail::is_registry<default_registry>);
struct not_a_policy {};
static_assert(!detail::is_registry<not_a_policy>);
BOOST_AUTO_TEST_CASE(test_type_id_list) {
auto iter = type_id_list<mp11::mp_list<a&, b&>, default_registry>::begin;
auto last = type_id_list<mp11::mp_list<a&, b&>, default_registry>::end;
BOOST_TEST_REQUIRE(last - iter == 2);
BOOST_TEST_REQUIRE(*iter++ == type_id(&typeid(a)));
BOOST_TEST_REQUIRE(*iter++ == type_id(&typeid(b)));
BOOST_AUTO_TEST_CASE(test_init_type_ids) {
type_id ids[2];
auto last = init_type_ids<default_registry, mp11::mp_list<a&, b&>>::fn(ids);
BOOST_TEST_REQUIRE(last - ids == 2);
BOOST_TEST_REQUIRE(ids[0] == type_id(&typeid(a)));
BOOST_TEST_REQUIRE(ids[1] == type_id(&typeid(b)));
}
} // namespace test_virtual
@@ -263,15 +263,12 @@ static_assert(
std::is_same_v<
use_classes<Animal, Dog, Bulldog, Cat, Dolphin>::tuple_type,
std::tuple<
class_declaration_aux<
default_registry, mp11::mp_list<Animal, Animal>>,
class_declaration_aux<
default_registry, mp11::mp_list<Dog, Animal, Dog>>,
class_declaration_aux<
use_class_aux<default_registry, mp11::mp_list<Animal, Animal>>,
use_class_aux<default_registry, mp11::mp_list<Dog, Animal, Dog>>,
use_class_aux<
default_registry, mp11::mp_list<Bulldog, Animal, Dog, Bulldog>>,
class_declaration_aux<
default_registry, mp11::mp_list<Cat, Animal, Cat>>,
class_declaration_aux<
use_class_aux<default_registry, mp11::mp_list<Cat, Animal, Cat>>,
use_class_aux<
default_registry, mp11::mp_list<Dolphin, Animal, Dolphin>>>>);
} // namespace test_use_classes

View File

@@ -49,29 +49,31 @@ struct custom_rtti : policies::rtti {
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return reinterpret_cast<type_id>(T::static_type);
return T::static_type;
} else {
return 0;
return nullptr;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return reinterpret_cast<type_id>(obj.type);
return obj.type;
} else {
return 0;
return nullptr;
}
}
template<class Stream>
static void type_name(type_id type, Stream& stream) {
stream << (type == 0 ? "?" : reinterpret_cast<const char*>(type));
stream
<< (type == nullptr ? "?"
: reinterpret_cast<const char*>(type));
}
static auto type_index(type_id type) {
return std::string_view(
(type == 0 ? "?" : reinterpret_cast<const char*>(type)));
(type == nullptr ? "?" : reinterpret_cast<const char*>(type)));
}
};
};
@@ -140,31 +142,31 @@ struct custom_rtti : policies::rtti {
template<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
static constexpr auto invalid_type =
(std::numeric_limits<std::size_t>::max)();
inline static auto invalid_type_id = type_id(0xFFFFFF);
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return T::static_type;
return type_id(T::static_type);
} else {
return invalid_type;
return invalid_type_id;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return obj.type;
return type_id(obj.type);
} else {
return invalid_type;
return invalid_type_id;
}
}
template<class Stream>
static void type_name(type_id type, Stream& stream) {
static const char* name[] = {"Animal", "Dog", "Cat"};
stream << (type == invalid_type ? "?" : name[type]);
stream
<< (type == invalid_type_id ? "?" : name[std::size_t(type)]);
}
static auto type_index(type_id type) {
@@ -296,31 +298,28 @@ struct custom_rtti : policies::rtti {
template<class T>
static constexpr bool is_polymorphic = std::is_base_of_v<Animal, T>;
static constexpr auto invalid_type =
(std::numeric_limits<std::size_t>::max)();
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return T::static_type;
return type_id(T::static_type);
} else {
return invalid_type;
return type_id(0xFFFF);
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return obj.type;
return type_id(obj.type);
} else {
return invalid_type;
return type_id(0xFFFF);
}
}
template<class Stream>
static void type_name(type_id type, Stream& stream) {
static const char* name[] = {"Animal", "Dog", "Cat"};
stream << (type == invalid_type ? "?" : name[type]);
stream << (type == type_id(0xFFFF) ? "?" : name[std::size_t(type)]);
}
static auto type_index(type_id type) {
@@ -405,7 +404,7 @@ void call_poke(vptr<Animal> a, std::ostream& os) {
} // namespace using_vptr
} // namespace virtual_base
namespace defered_type_id {
namespace deferred_type_id {
struct Animal {
const char* name;
@@ -448,25 +447,25 @@ struct custom_rtti : policies::deferred_static_rtti {
template<typename T>
static auto static_type() {
if constexpr (is_polymorphic<T>) {
return T::static_type;
return type_id(T::static_type);
} else {
return 0;
return nullptr;
}
}
template<typename T>
static auto dynamic_type(const T& obj) {
if constexpr (is_polymorphic<T>) {
return obj.type;
return type_id(obj.type);
} else {
return 0;
return nullptr;
}
}
template<class Stream>
static void type_name(type_id type, Stream& stream) {
static const char* name[] = {"Animal", "Dog", "Cat"};
stream << (type == 0 ? "?" : name[type]);
stream << (type == nullptr ? "?" : name[std::size_t(type)]);
}
static auto type_index(type_id type) {
@@ -522,4 +521,4 @@ BOOST_AUTO_TEST_CASE(custom_rtti_deferred) {
}
}
} // namespace defered_type_id
} // namespace deferred_type_id