Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions doc/modules/ROOT/pages/ref_headers.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ is defined. That policy needs no header - see above.

### link:{headers-url}/boost/openmethod/policies/static_rtti.hpp[<boost/openmethod/policies/static_rtti.hpp>]

Provides a minimal implementation of the `rtti` policy that does not depend on
standard RTTI.
Provides cpp:static_rtti[], a minimal implementation of the `rtti` policy that
does not depend on standard RTTI, and cpp:named_static_rtti[], a variant that
works across shared libraries.

### link:{headers-url}/boost/openmethod/policies/throw_error_handler.hpp[<boost/openmethod/policies/throw_error_handler.hpp>]

Expand Down
9 changes: 9 additions & 0 deletions doc/modules/ROOT/pages/shared_libraries.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,15 @@ in each module; cpp:initialize[] copes - it treats them as several ids for the
same class - but they are extra ids for the hash to separate. This is why the
library's own shared-library tests mark their classes `BOOST_SYMBOL_VISIBLE`.

NOTE: Under cpp:static_rtti[], a type id is the address of a static variable
in a template of the library, which has hidden visibility under
`-fvisibility=hidden`, and is never shared by a Windows DLL. Each module then
has its own id for every class, and nothing tells cpp:initialize[] that they
designate the same class: it takes them for unrelated classes, and a call made
in one module on an object created in another lands in the wrong overrider.
Use cpp:named_static_rtti[] instead: it maps the ids to the names of the
classes, and cpp:initialize[] merges them.

### What it costs

cpp:fast_perfect_hash[], the default, searches for a multiplier `M` and a shift
Expand Down
93 changes: 84 additions & 9 deletions include/boost/openmethod/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2256,34 +2256,109 @@ using overrider_virtual_types = boost::mp11::mp_remove<
MethodParameters, OverriderParameters>,
void>;

template<class Method, class Rtti, std::size_t Index>
// Whether a registration of a class says that it is `declared`, or derives from
// it. Only the bases listed in the same `use_classes` are known.
template<class Registry>
auto is_or_derives_from(const class_info& ci, type_id declared) -> bool {
using rtti = typename Registry::rtti;

auto key = rtti::type_index(declared);

if (rtti::type_index(ci.type) == key) {
return true;
}

for (auto base = ci.first_base; base != ci.last_base; ++base) {
if (rtti::type_index(*base) == key) {
return true;
}
}

return false;
}

// The class of an object, found from its v-table pointer, for an rtti policy
// that cannot tell the dynamic type. Used only to report an error.
//
// The object's own class always matches, because the v-table pointer came from
// its `static_vptr`. Another class may match as well: a v-table whose leading
// slots are unused is pointed to before its start, possibly at the start of
// another class' v-table. Then only a class that derives from `declared` will
// do. If that still does not settle it, `declared` is the answer: it is less
// precise, but true.
template<class Registry>
auto class_of_vptr(vptr_type vptr, type_id declared) -> type_id {
using rtti = typename Registry::rtti;

if (!vptr) {
return declared;
}

for (bool derived_only : {false, true}) {
type_id found = nullptr;
bool unique = true;

for (auto& ci : Registry::state().classes) {
if (*ci.static_vptr != vptr ||
(derived_only && !is_or_derives_from<Registry>(ci, declared))) {
continue;
}

if (!found) {
found = ci.type;
} else if (rtti::type_index(ci.type) != rtti::type_index(found)) {
unique = false;
break;
}
}

if (found && unique) {
return found;
}
}

return declared;
}

template<class Method, class Registry, std::size_t Index>
struct init_bad_call {
template<typename Arg, typename... Args>
static auto fn(bad_call& error, const Arg& arg, const Args&... args) {
using rtti = typename Registry::rtti;

if constexpr (Index == 0u) {
error.method = Rtti::template static_type<Method>();
error.method = rtti::template static_type<Method>();
error.arity = sizeof...(args) + 1;
}

type_id arg_type_id;

if constexpr (is_virtual_ptr<Arg>) {
arg_type_id = Rtti::dynamic_type(*arg);
using Class = std::remove_cv_t<typename Arg::element_type>;

if constexpr (rtti::template is_polymorphic<Class>) {
arg_type_id = rtti::dynamic_type(*arg);
} else {
arg_type_id = class_of_vptr<Registry>(
arg.vptr(), rtti::template static_type<Class>());
}
} else if constexpr (rtti::template is_polymorphic<Arg>) {
arg_type_id = rtti::dynamic_type(arg);
} else {
arg_type_id = Rtti::dynamic_type(arg);
arg_type_id = rtti::template static_type<Arg>();
}

error.types[Index] = arg_type_id;

init_bad_call<Method, Rtti, Index + 1>::fn(error, args...);
init_bad_call<Method, Registry, Index + 1>::fn(error, args...);
}

static auto fn(bad_call&) {
}
};

template<class Method, class Rtti>
struct init_bad_call<Method, Rtti, bad_call::max_types> {
template<class Method, class Registry>
struct init_bad_call<Method, Registry, bad_call::max_types> {
static auto fn(bad_call&) {
}
};
Expand Down Expand Up @@ -3051,7 +3126,7 @@ method<Id, ReturnType(Parameters...), Registry>::fn_not_implemented(

if constexpr (Registry::has_error_handler) {
no_overrider error;
detail::init_bad_call<method, rtti, 0u>::fn(
detail::init_bad_call<method, Registry, 0u>::fn(
error,
detail::parameter_traits<Parameters, Registry>::peek(args)...);
Registry::error_handler::error(error);
Expand All @@ -3069,7 +3144,7 @@ method<Id, ReturnType(Parameters...), Registry>::fn_ambiguous(

if constexpr (Registry::has_error_handler) {
ambiguous_call error;
detail::init_bad_call<method, rtti, 0u>::fn(
detail::init_bad_call<method, Registry, 0u>::fn(
error,
detail::parameter_traits<Parameters, Registry>::peek(args)...);
Registry::error_handler::error(error);
Expand Down
51 changes: 51 additions & 0 deletions include/boost/openmethod/detail/type_key.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) 2017-2026 Jean-Louis Leroy
// Distributed under the Boost Software License, Version 1.0.
// See accompanying file LICENSE_1_0.txt
// or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_OPENMETHOD_DETAIL_TYPE_KEY_HPP
#define BOOST_OPENMETHOD_DETAIL_TYPE_KEY_HPP

#include <cstddef>
#include <functional>
#include <string_view>

namespace boost::openmethod::detail {

// A key that identifies a class across modules, for an rtti policy that
// identifies classes by name: the name, and for a class that cannot be shared
// across translation units - one with internal linkage, or none - the type_id
// as well, which keeps it apart from other classes with the same name.
struct type_key {
std::string_view name;
const void* local;

friend auto operator==(const type_key& a, const type_key& b) -> bool {
return a.local == b.local && a.name == b.name;
}

friend auto operator!=(const type_key& a, const type_key& b) -> bool {
return !(a == b);
}

friend auto operator<(const type_key& a, const type_key& b) -> bool {
if (a.name != b.name) {
return a.name < b.name;
}

return std::less<const void*>()(a.local, b.local);
}
};

} // namespace boost::openmethod::detail

template<>
struct std::hash<boost::openmethod::detail::type_key> {
auto operator()(const boost::openmethod::detail::type_key& key) const
-> std::size_t {
return std::hash<std::string_view>()(key.name) ^
std::hash<const void*>()(key.local);
}
};

#endif
Loading
Loading