pass compiler to policies' initialize()

This commit is contained in:
Jean-Louis Leroy
2025-10-26 15:45:04 -04:00
parent e5762bd6e6
commit f7d47fbd35
7 changed files with 164 additions and 166 deletions

View File

@@ -2792,10 +2792,6 @@ using boost::openmethod::virtual_ptr;
#ifdef __MRDOCS__
namespace detail {
struct unspecified;
}
//! Blueprint for a specialization of @ref virtual_traits (exposition only).
//!
//! Specializations of @ref virtual_traits must implement the members listed

View File

@@ -182,7 +182,17 @@ struct generic_compiler {
return nullptr;
}
std::deque<class_> classes;
auto classes_begin() const {
return classes.begin();
}
auto classes_end() const {
return classes.end();
}
std::vector<method> methods;
std::size_t class_mark = 0;
bool compilation_done = false;
@@ -190,15 +200,12 @@ struct generic_compiler {
template<class Compiler>
struct trace_stream {
trace_stream(bool trace) : trace(trace) {
}
bool trace;
bool on = false;
std::size_t indentation_level{0};
auto operator++() -> trace_stream& {
if constexpr (Compiler::has_trace) {
if (trace) {
if (on) {
for (std::size_t i = 0; i < indentation_level; ++i) {
Compiler::Registry::output::os << " ";
}
@@ -296,7 +303,7 @@ struct range;
template<class Compiler, typename T, typename F>
auto write_range(trace_stream<Compiler>& tr, range<T> range, F fn) -> auto& {
if constexpr (Compiler::has_trace) {
if (tr.trace) {
if (tr.on) {
tr << "(";
const char* sep = "";
for (auto value : range) {
@@ -314,7 +321,7 @@ auto write_range(trace_stream<Compiler>& tr, range<T> range, F fn) -> auto& {
template<class Compiler, typename T>
auto operator<<(trace_stream<Compiler>& tr, const T& value) -> auto& {
if constexpr (Compiler::has_trace) {
if (tr.trace) {
if (tr.on) {
Compiler::Registry::output::os << value;
}
}
@@ -324,7 +331,7 @@ auto operator<<(trace_stream<Compiler>& tr, const T& value) -> auto& {
template<class Compiler>
auto operator<<(trace_stream<Compiler>& tr, const rflush& rf) -> auto& {
if constexpr (Compiler::has_trace) {
if (tr.trace) {
if (tr.on) {
std::size_t digits = 1;
auto tmp = rf.value / 10;
@@ -349,7 +356,7 @@ template<class Compiler>
auto operator<<(trace_stream<Compiler>& tr, const boost::dynamic_bitset<>& bits)
-> auto& {
if constexpr (Compiler::has_trace) {
if (tr.trace) {
if (tr.on) {
auto i = bits.size();
while (i != 0) {
--i;
@@ -388,9 +395,6 @@ template<class... Options>
struct registry<Policies...>::compiler : detail::generic_compiler {
using type_index_type = decltype(rtti::type_index(0));
static constexpr bool use_n2216 =
mp11::mp_contains<mp11::mp_list<Options...>, n2216>::value;
typename detail::aggregate_reports<mp11::mp_list<report>, policy_list>::type
report;
@@ -425,10 +429,15 @@ struct registry<Policies...>::compiler : detail::generic_compiler {
is_more_specific(const overrider* a, const overrider* b) -> bool;
static auto is_base(const overrider* a, const overrider* b) -> bool;
std::tuple<Options...> opts;
static constexpr bool has_trace =
mp11::mp_contains<mp11::mp_list<Options...>, openmethod::trace>::value;
bool trace = false;
std::tuple<Options...> options;
template<class Option>
static constexpr bool has_option =
mp11::mp_contains<mp11::mp_list<Options...>, Option>::value;
static constexpr bool has_trace = has_option<trace>;
static constexpr bool has_n2216 = has_option<n2216>;
mutable detail::trace_stream<compiler> tr;
using indent = typename detail::trace_stream<compiler>::indent;
};
@@ -467,11 +476,42 @@ void registry<Policies...>::compiler<Options...>::initialize() {
registry<Policies...>::initialized = true;
}
#ifdef _MSC_VER
namespace detail {
template<bool HasTrace, typename T>
struct msvc_tuple_get;
template<typename T>
struct msvc_tuple_get<true, T> {
template<class Tuple>
static decltype(auto) fn(const Tuple& t) {
return std::get<T>(t);
}
};
template<typename T>
struct msvc_tuple_get<false, T> {
template<class Tuple>
static decltype(auto) fn(const Tuple&) {
return T();
}
};
} // namespace detail
#endif
template<class... Policies>
template<class... Options>
registry<Policies...>::compiler<Options...>::compiler(Options... opts)
: opts(opts...), trace(detail::option<openmethod::trace>(this->opts).on),
tr(trace) {
: options(opts...) {
if constexpr (has_trace) {
#ifdef _MSC_VER
tr.on = detail::msvc_tuple_get<has_trace, trace>::fn(options).on;
#else
// Even with the constexpr has_trace guard, msvc errors on this.
tr.on = std::get<trace>(options).on;
#endif
}
}
template<class... Policies>
@@ -1148,7 +1188,7 @@ void registry<Policies...>::compiler<Options...>::build_dispatch_table(
m.dispatch_table.push_back(&m.not_implemented);
++m.report.not_implemented;
} else {
if constexpr (!use_n2216) {
if constexpr (!has_option<n2216>) {
if (remaining > 1) {
++tr << "ambiguous\n";
m.dispatch_table.push_back(&m.ambiguous);
@@ -1209,7 +1249,7 @@ void registry<Policies...>::compiler<Options...>::build_dispatch_table(
select_dominant_overriders(overriders, pick, remaining);
if constexpr (!use_n2216) {
if constexpr (!has_option<n2216>) {
if (remaining > 1) {
++tr << "ambiguous 'next'\n";
overrider->next = &m.ambiguous;
@@ -1368,7 +1408,7 @@ void registry<Policies...>::compiler<Options...>::write_global_data() {
++tr << rflush(4, dispatch_data_size) << " " << gv_iter << " end\n";
if constexpr (has_vptr) {
vptr::initialize(classes.begin(), classes.end(), opts);
vptr::initialize(*this, options);
}
new_dispatch_data.swap(dispatch_data);
@@ -1407,7 +1447,7 @@ void registry<Policies...>::compiler<Options...>::select_dominant_overriders(
return;
}
if constexpr (use_n2216) {
if constexpr (has_option<n2216>) {
if (!candidates[pick]->covariant_return_type) {
return;
}
@@ -1571,10 +1611,6 @@ void registry<Policies...>::compiler<Options...>::print(
//! @endcode
template<class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY, class... Options>
inline auto initialize(Options&&... options) {
static_assert(
(std::is_base_of_v<detail::option_base, Options> && ...),
"invalid option type");
if (detail::odr_check<Registry>::count > 1) {
// Multiple definitions of default_registry detected.
// This indicates an ODR violation.

View File

@@ -80,10 +80,10 @@ struct fast_perfect_hash : type_hash {
static void check(std::size_t index, type_id type);
template<class ForwardIterator, class... Options>
template<class InitializeContext, class... Options>
static void initialize(
ForwardIterator first, ForwardIterator last,
std::vector<type_id>& buckets, std::tuple<Options...> options);
const InitializeContext& ctx, std::vector<type_id>& buckets,
const std::tuple<Options...>& options);
public:
//! Find the hash factors
@@ -95,25 +95,18 @@ struct fast_perfect_hash : type_hash {
//! If no suitable values are found, calls the error handler with
//! a @ref hash_error object then calls `abort`.
//!
//! @tparam ForwardIterator An iterator to a range of @ref
//! IdsToVptr objects.
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param first An iterator to the beginning of the range.
//! @param last An iterator to the end of the range.
//! @param options Zero or more option objects.
//! @tparam Context An @ref InitializeContext.
//! @param ctx A Context object.
//! @return A pair containing the minimum and maximum hash values.
template<class ForwardIterator, class... Options>
static auto initialize(
ForwardIterator first, ForwardIterator last,
std::tuple<Options...> options) {
template<class Context, class... Options>
static auto
initialize(const Context& ctx, const std::tuple<Options...>& options) {
if constexpr (Registry::has_runtime_checks) {
initialize(
first, last, detail::fast_perfect_hash_control<Registry>,
options);
ctx, detail::fast_perfect_hash_control<Registry>, options);
} else {
std::vector<type_id> buckets;
initialize(first, last, buckets, options);
initialize(ctx, buckets, options);
}
return std::pair{min_value, max_value};
@@ -150,7 +143,7 @@ struct fast_perfect_hash : type_hash {
//! arguments.
//! @param options Zero or more option objects.
template<class... Options>
static auto finalize(std::tuple<Options...>) -> void {
static auto finalize(const std::tuple<Options...>&) -> void {
detail::fast_perfect_hash_control<Registry>.clear();
}
};
@@ -169,21 +162,16 @@ template<class Registry>
std::size_t fast_perfect_hash::fn<Registry>::max_value;
template<class Registry>
template<class ForwardIterator, class... Options>
template<class InitializeContext, class... Options>
void fast_perfect_hash::fn<Registry>::initialize(
ForwardIterator first, ForwardIterator last, std::vector<type_id>& buckets,
std::tuple<Options...> opts) {
using namespace policies;
const InitializeContext& ctx, std::vector<type_id>& buckets,
const std::tuple<Options...>& options) {
(void)options;
const auto N = std::distance(first, last);
(void)opts;
const auto N = std::distance(ctx.classes_begin(), ctx.classes_end());
if constexpr (
detail::has_option<trace, Options...> && Registry::has_output) {
if (detail::option<trace>(opts).on) {
Registry::output::os << "Finding hash factor for " << N
<< " types\n";
}
if constexpr (mp11::mp_contains<mp11::mp_list<Options...>, trace>::value) {
Registry::output::os << "Finding hash factor for " << N << " types\n";
}
std::default_random_engine rnd(13081963);
@@ -202,13 +190,9 @@ void fast_perfect_hash::fn<Registry>::initialize(
min_value = (std::numeric_limits<std::size_t>::max)();
max_value = (std::numeric_limits<std::size_t>::min)();
if constexpr (
mp11::mp_contains<mp11::mp_list<Options...>, trace>::value &&
Registry::has_output) {
if (detail::option<trace>(opts).on) {
Registry::output::os << " trying with M = " << M << ", "
<< hash_size << " buckets\n";
}
if constexpr (InitializeContext::template has_option<trace>) {
ctx.tr << " trying with M = " << M << ", " << hash_size
<< " buckets\n";
}
std::size_t attempts = 0;
@@ -221,7 +205,8 @@ void fast_perfect_hash::fn<Registry>::initialize(
++total_attempts;
mult = uniform_dist(rnd) | 1;
for (auto iter = first; iter != last; ++iter) {
for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
++iter) {
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
auto type = *type_iter;
@@ -238,15 +223,10 @@ void fast_perfect_hash::fn<Registry>::initialize(
}
}
if constexpr (
mp11::mp_contains<mp11::mp_list<Options...>, trace>::value &&
Registry::has_output) {
if (detail::option<trace>(opts).on) {
Registry::output::os
<< " found " << mult << " after " << total_attempts
<< " attempts; span = [" << min_value << ", "
<< max_value << "]\n";
}
if constexpr (InitializeContext::template has_option<trace>) {
ctx.tr << " found " << mult << " after " << total_attempts
<< " attempts; span = [" << min_value << ", "
<< max_value << "]\n";
}
return;

View File

@@ -38,20 +38,17 @@ class vptr_map : public vptr {
public:
//! Stores the v-table pointers.
//!
//! @tparam ForwardIterator An iterator to a range of @ref
//! IdsToVptr objects.
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param first An iterator to the beginning of the range.
//! @param last An iterator to the end of the range.
//! @param options Zero or more option objects.
template<class ForwardIterator, class... Options>
static void initialize(
ForwardIterator first, ForwardIterator last,
std::tuple<Options...>) {
//! @tparam Context An @ref InitializeContext.
//! @tparam Options... Zero or more option types.
//! @param ctx A Context object.
//! @param options A tuple of option objects.
template<class Context, class... Options>
static void
initialize(const Context& ctx, const std::tuple<Options...>&) {
decltype(vptrs) new_vptrs;
for (auto iter = first; iter != last; ++iter) {
for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
++iter) {
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
@@ -109,11 +106,11 @@ class vptr_map : public vptr {
//! Clears the map.
//!
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param options Zero or more option objects.
//! @tparam Options... Zero or more option types.
//! @param ctx A Context object.
//! @param options A tuple of option objects.
template<class... Options>
static auto finalize(std::tuple<Options...>) -> void {
static auto finalize(const std::tuple<Options...>&) -> void {
vptrs.clear();
}
};

View File

@@ -58,29 +58,24 @@ struct vptr_vector : vptr {
//! function is called. Its result determines the size of the vector.
//! The v-table pointers are copied into the vector.
//!
//! @tparam ForwardIterator An iterator to a range of @ref
//! IdsToVptr objects.
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param first An iterator to the beginning of the range.
//! @param last An iterator to the end of the range.
//! @param options Zero or more option objects.
template<class ForwardIterator, class... Options>
//! @tparam Context An @ref InitializeContext.
//! @tparam Options... Zero or more option types.
//! @param ctx A Context object.
//! @param options A tuple of option objects.
template<class Context, class... Options>
static auto initialize(
ForwardIterator first, ForwardIterator last,
std::tuple<Options...> opts) -> void {
(void)opts;
const Context& ctx, const std::tuple<Options...>& options) -> void {
std::size_t size;
(void)options;
if constexpr (has_type_hash) {
auto [_, max_value] =
type_hash::template initialize<ForwardIterator, Options...>(
first, last, opts);
auto [_, max_value] = type_hash::initialize(ctx, options);
size = max_value + 1;
} else {
size = 0;
for (auto iter = first; iter != last; ++iter) {
for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
++iter) {
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
size = (std::max)(size, std::size_t(*type_iter));
@@ -96,7 +91,8 @@ struct vptr_vector : vptr {
detail::vptr_vector_vptrs<Registry>.resize(size);
}
for (auto iter = first; iter != last; ++iter) {
for (auto iter = ctx.classes_begin(); iter != ctx.classes_end();
++iter) {
for (auto type_iter = iter->type_id_begin();
type_iter != iter->type_id_end(); ++type_iter) {
std::size_t index;
@@ -173,13 +169,13 @@ struct vptr_vector : vptr {
}
}
//! Clears the vector.
//! Releases the memory allocated by `initialize`.
//!
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @tparam Options... Zero or more option types, deduced from the function
//! arguments.
//! @param options Zero or more option objects.
template<class... Options>
static auto finalize(std::tuple<Options...>) -> void {
static auto finalize(const std::tuple<Options...>&) -> void {
using namespace policies;
if constexpr (Registry::has_indirect_vptr) {

View File

@@ -368,6 +368,8 @@ struct deferred_overrider_info : overrider_info {
virtual void resolve_type_ids() = 0;
};
struct unspecified {};
} // namespace detail
#ifdef __MRDOCS__
@@ -385,28 +387,6 @@ struct LightweightOutputStream {
#endif
namespace detail {
template<class Target, class... Options>
constexpr bool has_option =
mp11::mp_contains<mp11::mp_list<Options...>, Target>::value;
template<class Target, class... Options>
inline auto option(std::tuple<Options...> opts) {
constexpr auto index =
mp11::mp_find<mp11::mp_list<Options...>, Target>::value;
if constexpr (index == sizeof...(Options)) {
return Target();
} else {
return std::get<index>(opts);
}
}
struct option_base {};
} // namespace detail
//! N2216 ambiguity resolution.
//!
//! If `n2216` is present in @ref initialize\'s `Options`, additional steps are
@@ -422,7 +402,7 @@ struct option_base {};
//! - Otherwise, pick one of the overriders. Which one is used is unspecified,
//! but remains the same throughtout the program, and across different runs of
//! the same program.
struct n2216 : detail::option_base {};
struct n2216 {};
//! Enable `initialize` tracing.
//!
@@ -437,7 +417,7 @@ struct n2216 : detail::option_base {};
//! The content of the trace is neither specified, nor stable across versions.
//! It is comprehensive, and useful for troubleshooting missing class
//! registrations, missing or ambiguous overriders, etc.
struct trace : detail::option_base {
struct trace {
//! Enable trace if `true`.
bool on = true;
@@ -477,23 +457,48 @@ namespace policies {
#ifdef __MRDOCS__
//! Type ids and v-table pointers for a class (exposition only).
struct IdsToVptr {
//! Returns an iterator to the beginning of a range of `type_id`s for a
//! single registered class.
auto type_id_begin() const;
//! Class information for initializing a policy (exposition only).
//!
//! Provides the v-table pointer for a class, identified by one or more type
//! ids, via the members described on this page.
struct InitializeClass {
//! Beginning of a range of type ids for a class.
//!
//! @return A forward iterator to the beginning of a range of type ids for
//! a class.
auto type_id_begin() const -> detail::unspecified;
//! Returns an iterator to the end of a range of `type_id`s for a
//! single registered class.
auto type_id_end() const;
//! End of a range of type ids for a class.
//!
//! @return A forward iterator to the end of a range of type ids for a
//! class.
auto type_id_end() const -> detail::unspecified;
//! Returns a range of `type_id`s that this assignment applies to.
//! Reference to the v-table pointer for the class.
//!
//! @return A reference to the v-table pointer for the class.
auto vptr() const -> const vptr_type&;
};
#endif
//! Context for initializing a policy (exposition only).
//!
//! @ref initialize passes a "context" object, of unspecified type, to the
//! `initialize` functions of the policies that have one. It provides the
//! v-table pointer for the registered classes, via the members described on
//! this page.
struct InitializeContext {
//! Beginning of a range of `InitializeClass` objects.
//!
//! @return A forward iterator to the beginning of a range of @ref
//! InitializeClass objects.
detail::unspecified classes_begin() const;
#ifdef __MRDOCS__
//! End of a range of `InitializeClass` objects.
//!
//! @return A forward iterator to the end of a range of @ref
//! InitializeClass objects.
detail::unspecified classes_end() const;
};
//! Blueprint for @ref rtti metafunctions (exposition only).
template<class Registry>
@@ -588,11 +593,6 @@ struct rtti {
};
};
#ifdef __MRDOCS__
struct std_rtti;
struct static_rtti;
#endif
//! Policy for deferred type id collection.
//!
//! Some custom RTTI systems rely on static constructors to assign type ids.
@@ -643,17 +643,10 @@ struct VptrFn {
//! Called by @ref registry::initialize to let the policy store the v-table
//! pointer associated to each `type_id`.
//!
//! @tparam ForwardIterator An iterator to a range of @ref
//! IdsToVptr objects.
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param first An iterator to the beginning of the range.
//! @param last An iterator to the end of the range.
//! @param options Zero or more option objects.
template<typename ForwardIterator, class... Options>
static auto initialize(
ForwardIterator first, ForwardIterator last,
std::tuple<Options...> opts);
//! @tparam Context A class that conforms to the @ref InitializeContext
//! blueprint.
template<class Context>
static auto initialize(const Context& ctx) -> void;
//! Return a *reference* to a v-table pointer for an object.
//!
@@ -712,7 +705,7 @@ struct TypeHashFn {
//! Initialize the hash table.
//!
//! @tparam ForwardIterator An iterator to a range of @ref
//! IdsToVptr objects.
//! InitializeClass objects.
//! @tparam Options... Zero or more option types, deduced from the
//! function arguments.
//! @param first An iterator to the beginning of the range.

View File

@@ -294,7 +294,7 @@ BOOST_AUTO_TEST_CASE(cast_shared_ptr_lvalue_reference) {
bool cast_moves() {
std::shared_ptr<Animal> animal = std::make_shared<Dog>();
(void) std::static_pointer_cast<Dog>(animal);
(void)std::static_pointer_cast<Dog>(animal);
return animal.get() == nullptr;
}