// <boost/openmethod.hpp>, flattened for Compiler Explorer.
// This file is self-contained.
//
// Generated by dev/flatten.py from Boost.OpenMethod 6832603. Do not edit.
// See https://github.com/boostorg/openmethod

// 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_HPP
#define BOOST_OPENMETHOD_HPP


// 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_CORE_HPP
#define BOOST_OPENMETHOD_CORE_HPP

#include <stdint.h>
#include <algorithm>
#include <cstdlib>
#include <tuple>
#include <type_traits>
#include <utility>

#include <boost/assert.hpp>
#include <boost/config.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/integral.hpp>
#include <boost/mp11/list.hpp>


#ifndef BOOST_OPENMETHOD_REGISTRY_HPP
#define BOOST_OPENMETHOD_REGISTRY_HPP


// 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_REFLECTION_HPP
#define BOOST_OPENMETHOD_DETAIL_REFLECTION_HPP

// Detect C++26 reflection (P2996). Both the language feature (the `^^`
// operator) and the library (`std::meta`) are required.
#if !defined(BOOST_OPENMETHOD_HAS_REFLECTION)
#if defined(__cpp_impl_reflection) && __has_include(<meta>)
#include <meta>
#if defined(__cpp_lib_reflection)
#define BOOST_OPENMETHOD_HAS_REFLECTION 1
#endif
#endif
#endif

#if !defined(BOOST_OPENMETHOD_HAS_REFLECTION)
#define BOOST_OPENMETHOD_HAS_REFLECTION 0
#endif

#if BOOST_OPENMETHOD_HAS_REFLECTION

#include <cstddef>
#include <type_traits>
#include <vector>

namespace boost::openmethod::detail {

// One argument of `register_classes`: a group of reflections, written in
// braces - or, for a group of one, as the reflection itself, which the
// converting constructor turns into a group of one.
//
// The constructor is constrained, so that it cannot be picked over the copy
// constructor, and `consteval`, as `std::meta::info` is a consteval-only type.
//
// `Size` is deduced from the number of items; the array is never empty, as
// `{}` must produce a valid type too.
template<std::size_t Size>
struct reflection_group {
    std::meta::info items[Size ? Size : 1]{};
    std::size_t size = Size;

    consteval reflection_group() = default;

    template<class... T>
        requires(... && std::is_same_v<T, std::meta::info>)
    consteval reflection_group(T... items_) :
        items{items_...}, size(sizeof...(T)) {
    }
};

template<class... T>
reflection_group(T...) -> reflection_group<sizeof...(T)>;
reflection_group() -> reflection_group<0>;

// =============================================================================
// vectors of reflections

consteval auto contains(
    const std::vector<std::meta::info>& types, std::meta::info type) -> bool {
    for (auto seen : types) {
        if (seen == type) {
            return true;
        }
    }

    return false;
}

consteval void push_unique(
    std::vector<std::meta::info>& types, std::meta::info type) {
    if (!contains(types, type)) {
        types.push_back(type);
    }
}

// =============================================================================
// base classes

// Append `type` and all the base classes transitively reachable from it to
// `types`, skipping the ones already present. Only public base specifiers are
// followed: a class reached solely through a private or protected base cannot
// take part in dispatch, because the conversion is not available to the
// library. A base the walk arrives at again is appended to `repeated` as well:
// it is a candidate for ambiguity, which `collect_dispatchable_bases` decides.
consteval void collect_reflected_bases(
    std::meta::info type, std::vector<std::meta::info>& types,
    std::vector<std::meta::info>& repeated) {
    if (contains(types, type)) {
        push_unique(repeated, type);

        return;
    }

    types.push_back(type);

    // The range is held in a named local instead of being left to the
    // range-for to lifetime-extend, to work around GCC PR124645/PR124646.
    // r16-8235 marks a lifetime-extended temporary of consteval-only type -
    // which `vector<meta::info>` is - `DECL_EXTERNAL` unconditionally, and the
    // constant evaluator then hands every frame of a recursive call the same
    // object: the inner call destroys the vector the outer call is still
    // walking, and the loop fails with "accessing '<anonymous>' outside its
    // lifetime". A plain automatic variable is not an extended-ref temporary,
    // so each frame gets its own. `scan_scope` below recurses too, and does
    // the same.
    //
    // Fixed upstream in r16-8430. Drop this once no supported toolchain sits
    // in between - Ubuntu 26.04, which Boost.CI uses for the C++26 leg, ships
    // 16-20260322 (r16-8246) and does.
    auto bases =
        std::meta::bases_of(type, std::meta::access_context::unchecked());

    for (auto base : bases) {
        if (std::meta::is_public(base)) {
            collect_reflected_bases(std::meta::type_of(base), types, repeated);
        }
    }
}

// True if a `derived` can be converted to a `base` - that is, if `base` is a
// public base class of `derived` and naming it is unambiguous. Exact, as it
// asks the language: a virtual base is one subobject however many paths reach
// it. It costs a template instantiation, so ask it only where the answer can
// differ from what the walk above already knows.
consteval auto is_dispatchable_base(
    std::meta::info derived, std::meta::info base) -> bool {
    return std::meta::is_convertible_type(
        std::meta::add_pointer(derived), std::meta::add_pointer(base));
}

// Append to `types` the classes `type` can dispatch as: itself, and the base
// classes reachable from it through public inheritance, less the ones that
// repeated inheritance makes ambiguous. An ambiguous base cannot take part in
// dispatch - no reference to it can be formed - so it is left out.
// `use_classes` rejects such a hierarchy outright; `register_classes` cannot,
// because it sees classes it was never asked about.
//
// Only the bases the walk arrived at more than once can be ambiguous, and only
// those are put to `is_dispatchable_base`: a hierarchy without repeated
// inheritance instantiates nothing.
consteval void collect_dispatchable_bases(
    std::meta::info type, std::vector<std::meta::info>& types) {
    std::vector<std::meta::info> reachable, repeated;
    collect_reflected_bases(type, reachable, repeated);

    for (auto base : reachable) {
        if (contains(repeated, base) && !is_dispatchable_base(type, base)) {
            continue;
        }

        types.push_back(base);
    }
}

// =============================================================================
// scan

// True if `member` is a namespace that a recursive scan does not enter: `std`
// and `boost`. Walking them would cost a great deal and find nothing: a method
// cannot be declared on a class the program has never heard of. The nested
// namespaces - `std::chrono`, `boost::mp11`, the inline versioning ones - are
// reached only through their parent, so they are left out with it. The
// exclusion applies only to recursion: a namespace listed explicitly is always
// scanned, which is how a class in `std` or `boost` is registered.
consteval auto is_excluded_namespace(std::meta::info member) -> bool {
    auto ns = std::meta::dealias(member);

    return ns == ^^::std || ns == ^^::boost;
}

// The class template specialization that `member` names: `member` itself, if it
// is a type - or an alias for one - that is a specialization; or the class that
// encloses `member`'s type, if `member` is a variable of a nested type. This is
// how a `method` is found: the core interface names it in an alias, and a
// registrar - the one `BOOST_OPENMETHOD_OVERRIDE` creates, or one written by
// hand - is a variable of type `method<...>::override<...>`. Returns an invalid
// reflection if `member` names no specialization.
consteval auto specialization_named_by(std::meta::info member)
    -> std::meta::info {
    if (std::meta::is_type(member)) {
        auto type = std::meta::dealias(member);

        if (std::meta::has_template_arguments(type)) {
            return type;
        }

        return std::meta::info();
    }

    if (std::meta::is_variable(member)) {
        auto enclosing = std::meta::type_of(member);

        if (std::meta::has_parent(enclosing)) {
            auto parent = std::meta::parent_of(enclosing);

            if (std::meta::is_type(parent) &&
                std::meta::has_template_arguments(parent)) {
                return parent;
            }
        }
    }

    return std::meta::info();
}

// Walk `scope` - a namespace, or a class - and the namespaces and classes
// nested in it, collecting the specializations of `Template` that its members
// name, and the complete class types they declare. Nothing else is retained:
// the scan of a large namespace must not build a list of everything in it.
//
// A member that *names* a class registers it, which is how a class reached
// through an alias is found. Recursion is narrower: it enters only a class the
// scope actually *declares*, which `parent_of` answers. Following an alias
// instead would walk whatever it points at - a member `using` for
// `std::string` would drag the whole of `basic_string` in behind it - and
// `std` is excluded from the scan for that very reason.
consteval void scan_scope(
    std::meta::info scope, std::meta::info Template,
    std::vector<std::meta::info>& specializations,
    std::vector<std::meta::info>& classes) {
    // A named local, for the reason given in `collect_reflected_bases`.
    auto members =
        std::meta::members_of(scope, std::meta::access_context::unchecked());

    for (auto member : members) {
        if (std::meta::is_namespace(member)) {
            if (!is_excluded_namespace(member)) {
                scan_scope(member, Template, specializations, classes);
            }

            continue;
        }

        auto specialization = specialization_named_by(member);

        if (specialization != std::meta::info() &&
            std::meta::template_of(specialization) == Template) {
            push_unique(specializations, specialization);
        }

        if (std::meta::is_type(member)) {
            // An alias may add cv-qualification -
            // `using CDog = const Dog` - which is not a distinct class to
            // register: the registry would hold `Dog` and `const Dog` as two
            // lattice nodes, each with its own hash slot and dispatch table
            // row.
            auto type = std::meta::remove_cv(std::meta::dealias(member));

            if (std::meta::is_class_type(type) &&
                std::meta::is_complete_type(type)) {
                auto known = contains(classes, type);
                push_unique(classes, type);

                // The injected class name makes a class a member of itself,
                // and a class already walked may be named again; `known`
                // stops both.
                if (!known && std::meta::has_parent(type) &&
                    std::meta::parent_of(type) == scope) {
                    scan_scope(type, Template, specializations, classes);
                }
            }
        }
    }
}

} // namespace boost::openmethod::detail

#endif

#endif


// 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_STATIC_LIST_HPP
#define BOOST_OPENMETHOD_DETAIL_STATIC_LIST_HPP

#include <algorithm>
#include <boost/assert.hpp>

namespace boost::openmethod {

namespace detail {

template<typename T>
class static_list {
  public:
    static_list(static_list&) = delete;
    static_list() = default;

    class static_link {
      public:
        static_link(const static_link&) = delete;
        static_link() = default;

        auto next() -> T* {
            return next_ptr;
        }

      protected:
        friend class static_list;
        T* prev_ptr;
        T* next_ptr;
    };

    void push_back(T& node) {
        BOOST_ASSERT(node.prev_ptr == nullptr);
        BOOST_ASSERT(node.next_ptr == nullptr);

        if (!first) {
            first = &node;
            node.prev_ptr = &node;
            return;
        }

        auto last = first->prev_ptr;
        last->next_ptr = &node;
        node.prev_ptr = last;
        first->prev_ptr = &node;
    }

    void remove(T& node) {
        BOOST_ASSERT(first != nullptr);

        auto prev = node.prev_ptr;
        auto next = node.next_ptr;
        auto last = first->prev_ptr;

        node.prev_ptr = nullptr;
        node.next_ptr = nullptr;

        if (&node == last) {
            if (&node == first) {
                first = nullptr;
                return;
            }

            first->prev_ptr = prev;
            prev->next_ptr = nullptr;
            return;
        }

        if (&node == first) {
            first = next;
            first->prev_ptr = last;
            return;
        }

        prev->next_ptr = next;
        next->prev_ptr = prev;
    }

    void clear() {
        auto next = first;
        first = nullptr;

        while (next) {
            auto cur = next;
            next = cur->next_ptr;
            cur->prev_ptr = nullptr;
            cur->next_ptr = nullptr;
        }
    }

    class iterator {
      public:
        using iterator_category = std::forward_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = T;
        using pointer = value_type*;
        using reference = value_type&;

        iterator() : ptr(nullptr) {
        }
        explicit iterator(T* p) : ptr(p) {
        }

        auto operator*() -> reference {
            return *ptr;
        }
        auto operator->() -> pointer {
            return ptr;
        }

        auto operator++() -> iterator& {
            BOOST_ASSERT(ptr);
            ptr = ptr->next_ptr;
            return *this;
        }

        auto operator++(int) -> iterator {
            auto tmp = *this;
            ++(*this);
            return tmp;
        }

        friend auto operator==(const iterator& a, const iterator& b) -> bool {
            return a.ptr == b.ptr;
        }

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

      private:
        T* ptr;
    };

    auto begin() -> iterator {
        return iterator(first);
    }

    auto end() -> iterator {
        return iterator(nullptr);
    }

    class const_iterator {
      public:
        using iterator_category = std::forward_iterator_tag;
        using difference_type = std::ptrdiff_t;
        using value_type = const T;
        using pointer = value_type*;
        using reference = value_type&;

        const_iterator() : ptr(nullptr) {
        }
        explicit const_iterator(T* p) : ptr(p) {
        }

        auto operator*() -> reference {
            return *ptr;
        }
        auto operator->() -> pointer {
            return ptr;
        }

        auto operator++() -> const_iterator& {
            BOOST_ASSERT(ptr);
            ptr = ptr->next_ptr;
            return *this;
        }

        auto operator++(int) -> const_iterator {
            auto tmp = *this;
            ++(*this);
            return tmp;
        }

        friend auto operator==(const const_iterator& a, const const_iterator& b)
            -> bool {
            return a.ptr == b.ptr;
        }

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

      private:
        T* ptr;
    };

    auto begin() const -> const_iterator {
        return const_iterator(first);
    }

    auto end() const -> const_iterator {
        return const_iterator(nullptr);
    }

    auto size() const -> std::size_t {
        return std::distance(begin(), end());
    }

    auto empty() const -> bool {
        return !first;
    }

  protected:
    T* first;
};

} // namespace detail
} // namespace boost::openmethod

#endif


#include <boost/config.hpp>
#include <boost/mp11/algorithm.hpp>
#include <boost/mp11/bind.hpp>
#include <boost/mp11/utility.hpp>
#include <boost/preprocessor/cat.hpp>

#include <stdlib.h>
#include <vector>
#include <cstdint>
#include <string_view>
#include <tuple>

#ifdef _MSC_VER
#pragma warning(push)
// 4702: unreachable code. 4251: registry_state<R>::st (dll-exported) has type
// registry_state_type<R>, which intentionally has no dll-interface; benign for
// a static member, which is not part of object layout.
#pragma warning(disable : 4702 4251)
#endif

namespace boost::openmethod {

// -----------------------------------------------------------------------------
// word

namespace detail {

union word {
    word() {
    } // undefined
    word(void (*pf)()) : pf(pf) {
    }
    word(word* pw) : pw(pw) {
    }
    word(std::size_t i) : i(i) {
    }

    void (*pf)();
    std::size_t i;
    word* pw;
};

} // namespace detail

// -----------------------------------------------------------------------------
// public aliases

//! Alias to v-table pointer type.
//!
//! `vptr_type` is an alias to the type of a v-table pointer.
using vptr_type = const detail::word*;

//! Type used to identify a class.
//!
//! `type_id` is the return type of the @ref static_type and @ref dynamic_type
//! functions. It can be used as an actual data pointer (e.g. to a
//! `std::type_info` object), or as an opaque integer type.
using type_id = const void*;

// -----------------------------------------------------------------------------
// virtual types and traits

//! Decorator for virtual parameters.
//!
//! `virtual_` marks a formal parameter of a method as virtual. It is a @em
//! decorator, not an actual type that can be instantiated (it does not have a
//! definition). It is removed from the method's signature.
//!
//! @note `virtual_` can be used @em only in method declarations, @em not in
//! overriders. A parameter in overriders is implicitly virtual if it is in
//! the same position as a virtual parameter in the method's declaration.
//!
//! @par Requirements
//!
//! - @ref virtual_traits must be specialized for `T`.
//!
//! @tparam T A class.
//!
//! @see [Virtual Pointer Alternatives](xref:ROOT:virtual_ptr_alt.adoc)
template<typename T>
struct virtual_;

template<typename T, class Registry>
struct virtual_traits;

// =============================================================================
// Error handling

//! Base class for all OpenMethod errors.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct openmethod_error {};

//! One Definition Rule violation.
//!
//! This error is raised if the definition of @ref default_registry is
//! inconsistent across translation units, due to misuse of
//! @ref BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct odr_violation : openmethod_error {
    //! Write a description of the error to a stream.
    //! @tparam Registry The registry containing this policy.
    //! @param stream The stream to write to.
    template<class Registry, class Stream>
    auto write(Stream& stream) const {
        stream << "conflicting definitions of ";
        Registry::rtti::type_name(
            Registry::rtti::template static_type<Registry>(), stream);
    }
};

namespace detail {

template<class Registry>
struct BOOST_SYMBOL_VISIBLE odr_check {
    static std::size_t count;
    template<class R>
    static std::size_t inc;

    odr_check() {
        [[maybe_unused]] auto _ = &inc<typename Registry::registry_type>;
    }
};

template<class Registry>
std::size_t odr_check<Registry>::count;

template<class Registry>
template<class R>
std::size_t odr_check<Registry>::inc = count++;

} // namespace detail

//! Registry not initialized
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct not_initialized : openmethod_error {
    //! Write a short description to an output stream
    //! @param os The output stream
    //! @tparam Registry The registry
    //! @tparam Stream A @ref LightweightOutputStream
    template<class Registry, class Stream>
    auto write(Stream& os) const {
        os << "not initialized";
    }
};

//! Missing class.
//!
//! A class used as a virtual parameter in a method, an overrider or a method
//! call was not registered.
//!
//! @par Examples
//!
//! @note The error goes to the registry's
//! @ref boost::openmethod::policies::error_handler policy, which writes the
//! description shown in the comments; the program is then terminated. A
//! handler may throw instead, to keep the program running.
//!
//! Missing registration of a class used as a virtual parameter in a method:
//!
//! include:errors_missing_class_method.cpp#classes;init
//!
//! Missing registration of a class used as a virtual parameter in an overrider:
//!
//! include:errors_missing_class_overrider.cpp#classes;init
//!
//! Missing registration of a class used as a virtual parameter in a call:
//!
//! include:errors_missing_class_call.cpp#classes;use
//!
//! @note With a compiler that supports C++26 reflection, @ref
//! BOOST_OPENMETHOD_REGISTER_CLASSES registers these classes on its own, and the
//! examples above no longer report anything. The error remains reachable - for
//! a class in a namespace the scan does not cover, or in a registry with an
//! @ref boost::openmethod::policies::explicit_class_registration policy, which
//! is what the examples use.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct missing_class : openmethod_error {
    //! The type_id of the unknown class.
    type_id type;

    //! Write a short description to an output stream
    //! @param os The output stream
    //! @tparam Registry The registry
    //! @tparam Stream A @ref LightweightOutputStream
    template<class Registry, class Stream>
    auto write(Stream& os) const;
};

//! Missing base.
//!
//! A class used in an overrider virtual parameter was not registered as a
//! derived class of the class in the same position in the method's virtual
//! parameter list.
//!
//! @par Example
//!
//! @note The error goes to the registry's
//! @ref boost::openmethod::policies::error_handler policy, which writes the
//! description shown in the comments; the program is then terminated. A
//! handler may throw instead, to keep the program running.
//!
//! In the following code, OpenMethod cannot infer that `Dog` is derived from
//! `Animal`, because they are not registered in a same call to @ref
//! BOOST_OPENMETHOD_CLASSES.
//!
//! include:errors_missing_base.cpp#classes;init
//!
//! Fix:
//!
//! include:errors_missing_class_call.cpp#fix
//!
//! @note With a compiler that supports C++26 reflection, @ref
//! BOOST_OPENMETHOD_REGISTER_CLASSES registers these classes on its own, and the
//! examples above no longer report anything. The error remains reachable - for
//! a class in a namespace the scan does not cover, or in a registry with an
//! @ref boost::openmethod::policies::explicit_class_registration policy, which
//! is what the examples use.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct missing_base : openmethod_error {
    //! The type_id of the base class.
    type_id base;
    //! The type_id of the derived class.
    type_id derived;

    //! Write a short description to an output stream
    //! @param os The output stream
    //! @tparam Registry The registry
    //! @tparam Stream A @ref LightweightOutputStream
    template<class Registry, class Stream>
    auto write(Stream& os) const;
};

//! No valid overrider
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct bad_call : openmethod_error {
    //! The type_id of method that was called
    type_id method;
    //! The number of @em virtual arguments in the call
    std::size_t arity;
    //! The maximum size of `types`
    static constexpr std::size_t max_types = 16;
    //! The type_ids of the arguments.
    type_id types[max_types];
};

//! No overrider for virtual tuple
//!
//! The data members are documented on @ref bad_call.
//!
//! @see @ref bad_call
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct no_overrider : bad_call {
    //! Write a short description to an output stream
    //! @param os The output stream
    //! @tparam Registry The registry
    //! @tparam Stream A @ref LightweightOutputStream
    template<class Registry, class Stream>
    auto write(Stream& os) const {
        os << "not implemented";
    }
};

//! Ambiguous call
//!
//! The data members are documented on @ref bad_call.
//!
//! @see @ref bad_call
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct ambiguous_call : bad_call {
    //! Write a short description to an output stream
    //! @param os The output stream
    //! @tparam Registry The registry
    //! @tparam Stream A @ref LightweightOutputStream
    template<class Registry, class Stream>
    auto write(Stream& os) const {
        os << "ambiguous";
    }
};

//! Static and dynamic type mismatch in "final" construct
//!
//! If runtime checks are enabled, the "final" construct checks that the static
//! and dynamic types of the object, as reported by the `rtti` policy,  are the
//! same. If they are not, and if the registry contains an @ref error_handler
//! policy, its @ref error function is called with a `final_error` object, then
//! the program is terminated with `abort`.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct final_error : openmethod_error {
    type_id static_type, dynamic_type;

    //! Write a short description to an output stream
    //! @param os The output stream
    //! @tparam Registry The registry
    //! @tparam Stream A @ref LightweightOutputStream
    template<class Registry, class Stream>
    auto write(Stream& os) const;
};

namespace detail {

// =============================================================================
// generic registrars

// -----------------------------------------------------------------------------
// class info

struct class_info : static_list<class_info>::static_link {
    type_id type;
    vptr_type* static_vptr;
    type_id *first_base, *last_base;
    bool is_abstract{false};

    auto vptr() const -> const vptr_type& {
        return *static_vptr;
    }

    auto type_id_begin() const {
        return &type;
    }

    auto type_id_end() const {
        return &type + 1;
    }
};

struct deferred_class_info : class_info {
    virtual void resolve_type_ids() = 0;
};

// -----------
// method info

struct overrider_info;

struct method_info : static_list<method_info>::static_link {
    type_id* vp_begin;
    type_id* vp_end;
    static_list<overrider_info> overriders;
    void (*not_implemented)();
    void (*ambiguous)();
    type_id method_type_id;
    type_id return_type_id;
    std::size_t* slots_strides_ptr;

    auto arity() const {
        return std::distance(vp_begin, vp_end);
    }
};

struct deferred_method_info : method_info {
    virtual void resolve_type_ids() = 0;
};

struct overrider_info : static_list<overrider_info>::static_link {
    ~overrider_info() {
        method->overriders.remove(*this);
    }

    method_info* method; // for the destructor, to remove definition
    type_id return_type; // for N2216 disambiguation
    type_id type;        // of the function, for trace
    void (**next)();
    type_id *vp_begin, *vp_end;
    void (*pf)();
    // Set by BOOST_OPENMETHOD_INLINE_OVERRIDE (see the Inline template
    // parameter of override_impl/override_aux and class inline_override in
    // core.hpp). Only an overrider defined `inline` can legally have an
    // identical definition appear in more than one translation unit/module
    // (ODR requires it for a non-template, non-inline function to be defined
    // exactly once in the program), so augment_methods()'s cross-module
    // dedup only ever merges two overrider_info entries when both have
    // inline_ == true.
    bool inline_ = false;
};

struct deferred_overrider_info : overrider_info {
    virtual void resolve_type_ids() = 0;
};

} // namespace detail

// =============================================================================
// initialize options

#ifdef __MRDOCS__

namespace detail {
struct unspecified {};
} // namespace detail

//! Blueprint for a lightweight output stream (exposition only).
//!
//! Classes used as output streams in policies must provide the operations
//! described on this page, either as members or as free functions.
struct LightweightOutputStream {
    //! Writes a null-terminated string to the stream.
    LightweightOutputStream& operator<<(const char* str);

    //! Writes a string view to the stream.
    LightweightOutputStream& operator<<(const std::string_view& view);

    //! Writes a pointer value to the stream.
    LightweightOutputStream& operator<<(const void* value);

    //! Writes a size_t value to the stream.
    LightweightOutputStream& operator<<(std::size_t value);
};

#endif

// -----------------------------------------------------------------------------
// n2216

//! N2216 ambiguity resolution.
//!
//! If `n2216` is present in @ref initialize\'s `Options`, additional steps are
//! taken to select a single overrider in presence of ambiguous overriders sets,
//! according to the rules defined in the N2216 paper. If the normal resolution
//! procedure fails to select a single overrider, the following steps are
//! applied, in order:
//!
//! - If the return types of the remaining overriders are all polymorphic and
//!   covariant, and one of the return types is more specialized thjat all the
//!   others, use it.
//!
//! - 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 {};

// -----------------------------------------------------------------------------
// trace

//! Enable `initialize` tracing.
//!
//! If `trace` is passed to @ref initialize, tracing code is added to various
//! parts of the initialization process (dispatch table construction, hash
//! factors search, etc). The tracing code is executed only if
//! @ref trace::on is set to `true`.
//!
//! `trace` requires the registry being initialized to have an @ref output
//! policy.
//!
//! 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 {
    //! Enable trace if `true`.
    bool on = true;

    trace(bool on = true) : on(on) {
    }

    //! Returns a `trace` object with `on` set to `true` if the environment
    //! variable `BOOST_OPENMETHOD_TRACE` is set to the string "1", and false
    //! otherwise.
    static trace from_env();
};

inline trace trace::from_env() {
#ifdef _MSC_VER
    char* env;
    std::size_t len;
    auto result = _dupenv_s(&env, &len, "BOOST_OPENMETHOD_TRACE") == 0 && env &&
        len == 2 && *env == '1';
    free(env);
    return trace(result);
#else
    auto env = getenv("BOOST_OPENMETHOD_TRACE");
    return trace(env && *env++ == '1' && *env++ == 0);
#endif
}

// =============================================================================
// policies

//! Namespace for policies.
//!
//! Classes with snake case names are "blueprints", i.e. exposition-only classes
//! that describe the requirements for policies of a given category. Classes
//! implementing these blueprints must provide a `fn<Registry>` metafunction
//! that conforms to the blueprint's requirements.
//!
//! @ref registry carries a complete explanation of registries and policies.
//!
//! @see @ref registry
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)

namespace policies {

#ifdef __MRDOCS__

//! 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;

    //! 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;

    //! 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&;
};

//! 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;

    //! 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>
struct RttiFn {
    //! Tests if a class is polymorphic.
    //!
    //! @tparam Class A class.
    template<class Class>
    static constexpr bool is_polymorphic = std::is_polymorphic_v<Class>;

    //! Returns the static @ref type_id of a type.
    //!
    //! @note `Class` is not necessarily a @e registered class. This
    //! function is also called to acquire the type_id of non-virtual
    //! parameters, library types, etc, for diagnostic and trace purposes.
    //!
    //! @tparam Class A class.
    //! @return The static type_id of Class.
    template<class Class>
    static auto static_type() -> type_id;

    //! Returns the dynamic @ref type_id of an object.
    //!
    //! @tparam Class A registered class.
    //! @param obj A reference to an instance of `Class`.
    //! @return The type_id of `obj`'s class.
    template<class Class>
    static auto dynamic_type(const Class& obj) -> type_id;

    //! Writes a representation of a @ref type_id to a stream.
    //!
    //! @tparam Stream A LightweightOutputStream.
    //! @param type The `type_id` to write.
    //! @param stream The stream to write to.
    template<typename Stream>
    static auto type_name(type_id type, Stream& stream);

    //! Returns a key that uniquely identifies a class.
    //!
    //! @param type A `type_id`.
    //! @return A unique value that identifies a class with the given
    //! `type_id`.
    static auto type_index(type_id type);

    //! Casts an object to a type.
    //!
    //! @tparam D A reference to a subclass of `B`.
    //! @tparam B A registered class.
    //! @param obj A reference to an instance of `B`.
    template<typename D, typename B>
    static auto dynamic_cast_ref(B&& obj) -> D;
};

#endif

// -----------------------------------------------------------------------------
// rtti

//! Policy for manipulating type information.
//!
//! `rtti` policies are responsible for type information acquisition and dynamic
//! casting.
//!
//! @par Requirements
//!
//! Classes implementing this policy must:
//! @li derive from @c rtti.
//! @li provide a @c fn<Registry> metafunction that conforms to the @ref RttiFn
//! blueprint.
//!
//! @see [Custom RTTI](xref:ROOT:custom_rtti.adoc)
struct rtti {
    // Policy category.
    using category = rtti;

    //! Default implementations of some `rtti` requirements.
    struct defaults {
        //! Default implementation for `type_index`.
        //!
        //! @param type A `type_id`.
        //!
        //! @return `type` itself.
        static auto type_index(type_id type) -> type_id {
            return type;
        }

        //! Default implementation of `type_name`.
        //!
        //! Executes `stream << "type_id(" << type << ")"`.
        //!
        //! @param type A `type_id`.
        //! @param stream A stream to write to.
        template<typename Stream>
        static void type_name(type_id type, Stream& stream) {
            stream << "type_id(" << type << ")";
        }
    };
};

// -----------------------------------------------------------------------------
// deferred rtti

//! Policy for deferred type id collection.
//!
//! Some custom RTTI systems rely on static constructors to assign type ids.
//! OpenMethod itself relies on static constructors to register classes, methods
//! and overriders. This creates order-of-initialization issues. Deriving a @e
//! rtti policy from this class - instead of just `rtti` - causes the collection
//! of type ids to be deferred until the first call to @ref initialize.
//!
//! @see [Custom RTTI](xref:ROOT:custom_rtti.adoc)
struct deferred_static_rtti : rtti {};

// -----------------------------------------------------------------------------
// error handler

#ifdef __MRDOCS__
//! Blueprint for @ref error_handler metafunctions (exposition only).
template<class Registry>
struct ErrorHandlerFn {
    //! Called when an error is detected.
    //!
    //! `error` is a function, or a set of functions, that can be called
    //! with an instance of any subclass of `openmethod_error`.
    static auto error(const auto& error) -> void;
};
#endif

//! Policy for error handling.
//!
//! A @e error_handler policy runs code before the library terminates the
//! program due to an error. This can be useful for throwing, logging, cleanup,
//! or other actions.
//!
//! @par Requirements
//!
//! Classes implementing this policy must:
//! @li derive from @c error_handler.
//! @li provide a @c fn<Registry> metafunction that conforms to the @ref
//! ErrorHandlerFn blueprint.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct error_handler {
    // Policy category.
    using category = error_handler;
};

// -----------------------------------------------------------------------------
// vptr

#ifdef __MRDOCS__

//! Blueprint for `vptr` metafunctions (exposition only).
//!
//! @tparam Registry The registry containing the policy.
template<class Registry>
struct VptrFn {
    //! Register the v-table pointers.
    //!
    //! Called by @ref registry::initialize to let the policy store the v-table
    //! pointer associated to each `type_id`.
    //!
    //! @tparam Context A class that conforms to the @ref InitializeContext
    //! blueprint.
    //! @tparam Options... Zero or more option types, deduced from the
    //! function arguments.
    //! @param ctx A Context object.
    //! @param options A tuple of option objects.
    template<class Context, class... Options>
    static auto initialize(
        const Context& ctx, const std::tuple<Options...>& options) -> void;

    //! Return a *reference* to a v-table pointer for an object.
    //!
    //! @tparam Class A registered class.
    //! @param arg A reference to a const object of type `Class`.
    //! @return A reference to the v-table pointer for `Class`.
    template<class Class>
    static auto dynamic_vptr(const Class& arg) -> const vptr_type&;

    // Added by the `std::any` interop, under the name `type_vptr`. An `any`
    // knows the `type_id` of the value it contains, but has no object of that
    // type to hand to `dynamic_vptr`.

    //! Return a *reference* to the v-table pointer for a type.
    //!
    //! Return a reference to the v-table pointer that `initialize` associated
    //! to `type`.
    //!
    //! This function is optional. Implement it if the registry is to be used
    //! with virtual parameters whose `virtual_traits` supply a `type_id`
    //! themselves, instead of an object - see @ref VirtualTraits::vptr. Both
    //! @ref vptr_vector and @ref vptr_map provide it, and implement
    //! `dynamic_vptr` in terms of it.
    //!
    //! @param type A `type_id`.
    //! @return A reference to the v-table pointer for `type`.
    static auto vptr(type_id type) -> const vptr_type&;

    //! Release the resources allocated by `initialize`.
    //!
    //! This function is optional.
    //!
    //! @tparam Options... Zero or more option types, deduced from the
    //! function arguments.
    //! @param options A tuple of option objects.
    template<class... Options>
    static auto finalize(const std::tuple<Options...>& options) -> void;
};

#endif

//! Policy for v-table pointer acquisition.
//!
//! @par Requirements
//!
//! Classes implementing this policy must:
//! @li derive from @c vptr.
//! @li provide a @c fn<Registry> metafunction that conforms to the @ref
//! VptrFn blueprint.
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
struct vptr {
    // Policy category.
    using category = vptr;
};

//! Policy to add an indirection to pointers to v-tables.
//!
//! If this policy is present, constructs like @ref virtual_ptr, @ref
//! inplace_vptr, @ref vptr_vector, etc use pointers to pointers to v-tables.
//! These indirect pointers remain valid after a call to @ref initialize, after
//! dynamically loading a library that adds classes, methods and overriders to
//! the registry.
//!
//! @see [Shared Libraries](xref:ROOT:shared_libraries.adoc)
struct indirect_vptr final {
    // Policy category.
    using category = indirect_vptr;
    template<class Registry>
    struct fn {};
};

// -----------------------------------------------------------------------------
// type_hash

#ifdef __MRDOCS__
//! Blueprint for @ref type_hash metafunctions (exposition only).
//!
//! @tparam Registry The registry containing the policy.
template<class Registry>
struct TypeHashFn {
    //! Initialize the hash table.
    //!
    //! @tparam Context A class that conforms to the @ref InitializeContext
    //! blueprint.
    //! @tparam Options... Zero or more option types, deduced from the
    //! function arguments.
    //! @param ctx A Context object.
    //! @param options A tuple of option objects.
    //!
    //! Use @ref hash_range to retrieve the minimum and maximum hash values
    //! after calling this function.
    template<class Context, class... Options>
    static auto initialize(
        const Context& ctx, const std::tuple<Options...>& options) -> void;

    //! Return the range of hash values produced by @ref hash.
    //!
    //! Only valid after a call to @ref initialize.
    //!
    //! @return A pair containing the minimum and maximum hash values.
    static auto hash_range() -> std::pair<std::size_t, std::size_t>;

    //! Hash a `type_id`.
    //!
    //! @param type A @ref type_id.
    //! @return A hash value for the given `type_id`.
    static auto hash(type_id type) -> std::size_t;

    //! Release the resources allocated by `initialize`.
    //!
    //! This function is optional.
    //!
    //! @tparam Options... Zero or more option types, deduced from the
    //! function arguments.
    //! @param options A tuple of option objects.
    template<class... Options>
    static auto finalize(const std::tuple<Options...>& options) -> void;
};
#endif

//! Policy for hashing type ids.
//!
//! @par Requirements
//!
//! Classes implementing this policy must:
//! @li derive from @c type_hash.
//! @li provide a @c fn<Registry> metafunction that conforms to the @ref
//! TypeHashFn blueprint.
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
struct type_hash {
    // Policy category.
    using category = type_hash;
};

#ifdef __MRDOCS__

//! Blueprint for @ref output metafunctions (exposition only).
//!
//! @tparam Registry The registry containing the policy.
template<class Registry>
struct OutputFn {
    //! A @ref LightweightOutputStream.
    inline static LightweightOutputStream os;
};

#endif

// -----------------------------------------------------------------------------
// output

//! Policy for writing diagnostics and trace.
//!
//! If an `output` policy is present, the default error handler uses it to write
//! error messages to its output stream. @ref registry::initialize can also use
//! it to write trace messages.
//!
//! @par Requirements
//!
//! Classes implementing this policy must:
//! @li derive from @c output.
//! @li provide a @c fn<Registry> metafunction that conforms to the @ref
//! OutputFn blueprint.
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct output {
    // Policy category.
    using category = output;
};

// -----------------------------------------------------------------------------
// runtime_checks

//! Policy for post-initialize runtime checks.
//!
//! If this policy is present, performs the following checks:
//! @li Classes of virtual arguments have been registered.
//! @li Dynamic and static types match in "final" constructs (@ref
//! final_virtual_ptr and related functions).
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct runtime_checks final {
    // Policy category.
    using category = runtime_checks;
    template<class Registry>
    struct fn {};
};

// -----------------------------------------------------------------------------
// explicit_class_registration

//! Policy to disable reflection-based class registration.
//!
//! When the compiler supports C++26 reflection, the library registers the
//! classes of virtual parameters, and their base classes, on its own; see @ref
//! use_classes. If this policy is present, it does not: every class must be
//! registered with @ref use_classes or @ref BOOST_OPENMETHOD_CLASSES, exactly
//! as in C++17.
//!
//! The policy has no effect if the compiler does not support reflection.
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
struct explicit_class_registration final {
    // Policy category.
    using category = explicit_class_registration;
    template<class Registry>
    struct fn {};
};

} // namespace policies

// -----------------------------------------------------------------------------
// registry and policy helpers

namespace detail {

struct registry_base {};

// A minimal tuple. Unlike std::tuple (very expensive to instantiate with
// MSVC), it has no converting constructors, no comparisons, and no EBO
// machinery. It is used as a plain holder of heterogeneous objects
// (policy states, registrars), which are always default-constructed; `get`
// retrieves an element by type. Elements are held in base classes, so element
// types must be unique - lists that may contain duplicates are passed through
// mp_unique first.
//
// Measured (MSVC 19.51.36248, /Bt+ front-end time, synthetic
// BOOST_OPENMETHOD_CLASSES lists): ~11% less compile time at 40 classes
// (2.68s -> 2.39s, avg of 3 runs), ~7% at 100 classes (10.99s -> 10.22s).
// This is a constant-factor win that widens with list size, not a fix for
// superlinear blowups; on this library's own test suite (2-5 classes per
// list) the effect is within noise.
template<class T>
struct tuple_element {
    T element;
};

template<class... Ts>
struct tuple : tuple_element<Ts>... {};

template<class T, class... Ts>
auto get(tuple<Ts...>& t) -> T& {
    return static_cast<tuple_element<T>&>(t).element;
}

// Extracts ::state from T.
template<class T>
using policy_state_t = typename T::state;

// Detects whether T has a nested ::state type.
template<class T>
using has_policy_state = mp11::mp_valid<policy_state_t, T>;

// Quoted metafunction: maps a policy type P to P::template fn<Registry>.
template<class Registry>
struct policy_fn_q {
    template<class P>
    using fn = typename P::template fn<Registry>;
};

template<class Registry>
struct registry_state_type {
    static_list<class_info> classes;
    static_list<method_info> methods;
    bool initialized;
    std::vector<word> dispatch_data;
    // The per-policy `state` objects are held in a detail::tuple, whose
    // element types must be unique (each is a distinct base class). If two
    // stateful policies resolve to the same `state` type, raw instantiation
    // fails with a cryptic "duplicate base type ... invalid" pointing into
    // library internals. Diagnose it here instead. Do NOT mp_unique this
    // list to "fix" the error: that would silently alias the two policies
    // onto one shared state object.
    using policy_state_list = mp11::mp_transform<
        policy_state_t,
        mp11::mp_filter<
            has_policy_state,
            mp11::mp_transform_q<
                policy_fn_q<Registry>, typename Registry::policy_list>>>;
    static_assert(
        mp11::mp_size<policy_state_list>::value ==
            mp11::mp_size<mp11::mp_unique<policy_state_list>>::value,
        "two or more stateful policies in this registry share the same "
        "nested `state` type; each stateful policy must define its own "
        "distinct `state` type (give each its own nested struct)");
    using policies_type = mp11::mp_apply<detail::tuple, policy_state_list>;
    policies_type policies;
};

template<typename T>
constexpr bool is_registry = std::is_base_of_v<registry_base, T>;

template<typename T>
constexpr bool is_not_void = !std::is_same_v<T, void>;

template<class Base, class List, typename Default>
struct find_first_derived_of_aux;

template<class Base, class List, typename Default = void>
using find_first_derived_of =
    typename find_first_derived_of_aux<Base, List, Default>::type;

template<class Base, typename Default>
struct find_first_derived_of_aux<Base, mp11::mp_list<>, Default> {
    using type = Default;
};

template<class Base, typename Default, typename First, typename... More>
struct find_first_derived_of_aux<Base, mp11::mp_list<First, More...>, Default> {
    using type = std::conditional_t<
        std::is_base_of_v<Base, First>, First,
        find_first_derived_of<Base, mp11::mp_list<More...>, Default>>;
};

template<class Registry, class Policy>
struct get_policy_aux {
    using type = typename Policy::template fn<Registry>;
};

template<class Registry>
struct get_policy_aux<Registry, void> {
    using type = void;
};

template<class Policies, class...>
struct with_aux;

template<class Policies>
struct with_aux<Policies> {
    using type = Policies;
};

template<class Policies, class Policy, class... MorePolicies>
struct with_aux<Policies, Policy, MorePolicies...> {
    using replace = mp11::mp_replace_if_q<
        Policies,
        mp11::mp_bind_front_q<
            mp11::mp_quote_trait<std::is_base_of>, typename Policy::category>,
        Policy>;
    using replace_or_add = std::conditional_t<
        std::is_same_v<replace, Policies>, mp11::mp_push_back<Policies, Policy>,
        replace>;
    using type = typename with_aux<replace_or_add, MorePolicies...>::type;
};

template<class Policies, class...>
struct without_aux;

template<class Policies>
struct without_aux<Policies> {
    using type = Policies;
};

template<class Policies, class Policy, class... MorePolicies>
struct without_aux<Policies, Policy, MorePolicies...> {
    using type = typename without_aux<
        mp11::mp_remove_if_q<
            Policies,
            mp11::mp_bind_front_q<
                mp11::mp_quote_trait<std::is_base_of>,
                typename Policy::category>>,
        MorePolicies...>::type;
};

template<class...>
struct use_class_aux;

template<typename, class...>
struct initialize_aux;

} // namespace detail

#define BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN(FN)                              \
    template<typename, class, class...>                                        \
    struct BOOST_PP_CAT(has_, BOOST_PP_CAT(FN, _aux)) : std::false_type {};    \
    template<class T, class... Args>                                           \
    struct BOOST_PP_CAT(has_, BOOST_PP_CAT(FN, _aux))<                         \
        std::void_t<decltype(T::FN(std::declval<Args>()...))>, T, Args...> :   \
        std::true_type {};                                                     \
    template<class T, class... Args>                                           \
    constexpr bool BOOST_PP_CAT(has_, FN) =                                    \
        BOOST_PP_CAT(has_, BOOST_PP_CAT(FN, _aux))<void, T, Args...>::value

//! The single shared instance of a registry's state.
//!
//! `registry_state` is a thin, function-free class whose only member, `st`,
//! holds all of a registry's mutable state (of type @ref
//! detail::registry_state_type). Reach it through `Registry::state()`.
//!
//! It is deliberately a *separate* one-member class, rather than
//! `registry_state_type` itself, because sharing the state across a DLL
//! boundary on Windows requires exporting and importing a *whole class*:
//!
//! @li MSVC honors @c __declspec(dllexport/dllimport) on a class explicit
//!   instantiation, but NOT on a variable template (clients silently get a
//!   private copy) nor on a static-data-member instantiation (error C2720).
//!   So the exported symbol must be a class member reached via whole-class
//!   instantiation.
//! @li dllexporting @c registry_state_type directly would also decorate its
//!   member functions and, transitively, the policies' nested @c state types,
//!   which MSVC rejects (error C2513).
//!
//! A one-member, function-free class is the only shape MSVC will export as a
//! whole and import via `extern template`.
//!
//! To share the state across modules, use
//! @ref BOOST_OPENMETHOD_IMPORT_REGISTRY, @ref BOOST_OPENMETHOD_EXPORT_REGISTRY
//! and @ref BOOST_OPENMETHOD_INSTANTIATE_REGISTRY. They hide a platform
//! incompatibility: the export goes on the declaration on ELF and Mach-O, but
//! on the instantiation on declspec platforms, where `extern` and
//! `__declspec(dllexport)` cannot be combined.
//! @code
//! // header, every translation unit of a client module:
//! BOOST_OPENMETHOD_IMPORT_REGISTRY(boost::openmethod::default_registry);
//! // header, every translation unit of the owning module:
//! BOOST_OPENMETHOD_EXPORT_REGISTRY(boost::openmethod::default_registry);
//! // exactly one .cpp of the owning module:
//! BOOST_OPENMETHOD_INSTANTIATE_REGISTRY(boost::openmethod::default_registry);
//! @endcode
//!
//! @see [Shared Libraries](xref:ROOT:shared_libraries.adoc)
template<class Registry>
struct registry_state {
    static detail::registry_state_type<Registry> st;
};

template<class Registry>
detail::registry_state_type<Registry> registry_state<Registry>::st;

//! Methods, classes and policies.
//!
//! Methods exist in the context of a registry. Any class used as a method or
//! overrider parameter, or in as a method call argument, must be registered
//! with the same registry.
//!
//! Before calling a method, its registry must be initialized with the @ref
//! initialize function. This is typically done at the beginning of `main`.
//!
//! Multiple registries can co-exist in the same program. They must be
//! initialized individually. Classes referenced by methods in different
//! registries must be registered with each registry.
//!
//! A registry also contains a set of @ref policies that control how certain
//! operations are performed. For example, the `rtti` policy provides type
//! information, implements dynamic casting, etc. It can be replaced to
//! interface with custom RTII systems (like LLVM's).
//!
//! Policies are implemented as Boost.MP11 quoted metafunctions. A policy class
//! must contain a `fn<Registry>` template that provides a set of static
//! members, specific to the responsibility of the policy. Registries
//! instantiate policies by passing themselves to the nested `fn` class
//! templates.
//!
//! There are two reason for this design.
//!
//! Some policies are "stateful": they contain static _data_ members. Since
//! several registries can co-exist in the same program, each stateful policy
//! needs its own, separate set of static data members. For example, @ref
//! vptr_vector, a "vptr" policy, contains a static vector of vptrs, which
//! cannot be shared with other registries.
//!
//! Also, some policies need access to other policies in the same registry. They
//! can be accessed via the `Registry` template parameter. For example, @ref
//! vptr_vector hashes type_ids before using them as an indexes, if `Registry`
//! cotains a `type_hash` policy. It performs out-of-bounds checks if `Registry`
//! contains the `runtime_checks` policy. If an error is detected, it invokes
//! the @ref error_handler policy if there is  one.
//!
//! A registry is identified by its policy list, not by the class that derives
//! from it. Everything a registry owns is keyed on the `registry`
//! specialization, which is what @ref registry_type aliases. Two classes built
//! from the same policies, in the same order, are therefore the same registry:
//!
//! include:../examples/registry_identity.cpp#shared
//!
//! This matters when a second registry exists to isolate a set of methods from
//! another, since it would naturally be given the same policies. Give each one
//! a policy of its own to keep them apart:
//!
//! include:../examples/registry_identity.cpp#distinct
//!
//! @tparam Policy The policies used in the registry.
//!
//! @par Requirements
//!
//! @li @c Policy must contain a @c category alias to its root base class. The
//! registry may contain at most one policy per category.
//!
//! @li @c Policy must contain a @c fn<Registry> metafunction.
//!
//! @see @ref policies
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
template<class... Policy>
class registry : public detail::registry_base {

  public:
    //! List of policies selected in a registry.
    //!
    //! `policy_list` is a Boost.Mp11 list containing the policies passed to the
    //! @ref registry clas template.
    //!
    //! @tparam Class A registered class.
    using policy_list = mp11::mp_list<Policy...>;

    //! Find a policy by category.
    //!
    //! `policy` searches for a policy that derives from the specified @ref
    //! Category. If none is found, it aliases to `void`. Otherwise, it aliases
    //! to the policy's `fn` metafunction, applied to the registry.
    //!
    //! @tparam A policy.
    template<class Category>
    using policy = typename detail::get_policy_aux<
        registry, detail::find_first_derived_of<Category, policy_list>>::type;

  private:
    template<class...>
    friend struct detail::use_class_aux;
    template<typename Name, typename ReturnType, class Registry>
    friend class method;

    using static_ = registry_state<registry>;

  public:
    //! The type of this registry.
    //!
    //! `registry_type` is the `registry` specialization itself - for a
    //! registry defined as a struct deriving from `registry` (like @ref
    //! default_registry), the base class, not the struct. It is the type on
    //! which the registry's state is keyed. Two structs that derive from the
    //! same specialization therefore share one state, and are one registry;
    //! see @ref registry for how to keep two of them apart. It also appears in
    //! the explicit instantiation / `extern template` declaration pair that
    //! shares a custom registry's state across shared libraries:
    //! `registry_state<my_registry::registry_type>` (see @ref
    //! registry_state).
    using registry_type = registry;

    //! Return the registry's mutable state.
    //!
    //! Everything mutable in a registry - the class and method registration
    //! lists, the dispatch tables, and the state of every stateful policy -
    //! is agglomerated in a single variable,
    //! `registry_state<registry_type>::st`. `state` returns a reference to
    //! it.
    //!
    //! Modules (executables and shared libraries) contributing to the same
    //! registry must share this one variable; see @ref registry_state.
    static auto& state() {
        return static_::st;
    }

    //! Return a policy's state.
    //!
    //! Returns a reference to the `P::fn<registry_type>::state` object held
    //! in the registry's state. This is how stateful policies access their
    //! data members: policies do not keep their own global variables.
    //!
    //! @tparam P A stateful policy of this registry, i.e. one whose
    //! `fn<Registry>` contains a nested `state` type.
    template<class P>
    static auto& state() {
        return detail::get<typename P::template fn<registry>::state>(
            static_::st.policies);
    }

    //! Return an address identifying the registry's state.
    //!
    //! The address is the same in all the modules of a program if, and only
    //! if, they share the registry's state. Useful for diagnosing shared
    //! library setups.
    static const void* id() {
        return static_cast<const void*>(&static_::st.classes);
    }

    template<class... Options>
    struct compiler;

    //! Check that the registry is initialized.
    //!
    //! Check if `initialize` has been called for this registry, and report an
    //! error if not.
    //!
    //! @par Errors
    //!
    //! @li @ref not_initialized: The registry is not initialized.
    static void require_initialized();

    template<class... Options>
    static void finalize(Options... opts);

    //! A pointer to the virtual table for a registered class.
    //!
    //! `static_vptr` is set by @ref registry::initialize to the address of the
    //! class' virtual table. It remains valid until the next call to
    //! `initialize` or `finalize`.
    //!
    //! @tparam Class A registered class.
    template<class Class>
    inline static vptr_type static_vptr;

    //! Add or replace policies.
    //!
    //! `with` aliases to a registry with additional policies, overwriting any
    //! existing policies in the same category as the new ones.
    //!
    //! @tparam NewPolicies Policies, i.e. classes implementing one of the
    //! blueprints in @ref policies.
    template<class... NewPolicies>
    using with = boost::mp11::mp_apply<
        registry, typename detail::with_aux<policy_list, NewPolicies...>::type>;

    //! Remove policies.
    //!
    //! `without` aliases to a registry containing the same policies, except those
    //! that derive from `Categories`.
    //!
    //! @tparam Categories Policy categories, i.e. the blueprints in
    //! @ref policies.
    template<class... Categories>
    using without = boost::mp11::mp_apply<
        registry,
        typename detail::without_aux<policy_list, Categories...>::type>;

    //! The registry's rtti policy.
    using rtti = policy<policies::rtti>;

    //! The registry's vptr policy if it contains one, or `void`.
    using vptr = policy<policies::vptr>;

    //! `true` if the registry has a vptr policy.
    static constexpr auto has_vptr = !std::is_same_v<vptr, void>;

    //! The registry's error_handler policy if it contains one, or `void`.
    using error_handler = policy<policies::error_handler>;

    //! `true` if the registry has an error_handler policy.
    static constexpr auto has_error_handler =
        !std::is_same_v<error_handler, void>;

    //! The registry's output policy if it contains one, or `void`.
    using output = policy<policies::output>;

    //! `true` if the registry has an output policy.
    static constexpr auto has_output = !std::is_same_v<output, void>;

    //! `true` if the registry has a deferred_static_rtti policy.
    static constexpr auto has_deferred_static_rtti =
        !std::is_same_v<policy<policies::deferred_static_rtti>, void>;

    //! `true` if the registry has a runtime_checks policy.
    static constexpr auto has_runtime_checks =
        !std::is_same_v<policy<policies::runtime_checks>, void>;

    //! `true` if the registry has an indirect_vptr policy.
    static constexpr auto has_indirect_vptr =
        !std::is_same_v<policy<policies::indirect_vptr>, void>;

    //! `true` if the library registers classes by reflection.
    //!
    //! `true` if the compiler supports C++26 reflection and the registry does
    //! not have an @ref policies::explicit_class_registration policy.
    static constexpr auto has_reflected_class_registration =
        BOOST_OPENMETHOD_HAS_REFLECTION &&
        std::is_same_v<policy<policies::explicit_class_registration>, void>;
};

template<class... Policies>
void registry<Policies...>::require_initialized() {
    if constexpr (registry::has_runtime_checks) {
        if (!static_::st.initialized) {
            if constexpr (registry::has_error_handler) {
                error_handler::error(not_initialized());
            }

            abort();
        }
    }
}

template<class Registry, class Stream>
auto missing_class::write(Stream& os) const {
    os << "unknown class ";
    Registry::rtti::type_name(type, os);
}

template<class Registry, class Stream>
auto missing_base::write(Stream& os) const {
    os << "missing base ";
    Registry::rtti::type_name(base, os);
    os << " -<| ";
    Registry::rtti::type_name(derived, os);
}

template<class Registry, class Stream>
auto final_error::write(Stream& os) const {
    os << "invalid call to final construct: static type = ";
    Registry::rtti::type_name(static_type, os);
    os << ", dynamic type = ";
    Registry::rtti::type_name(dynamic_type, os);
}

struct default_registry;
} // namespace boost::openmethod

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif // BOOST_OPENMETHOD_REGISTRY_HPP


// 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_DEFAULT_REGISTRY_HPP
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY_HPP




// 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_POLICY_STD_RTTI_HPP
#define BOOST_OPENMETHOD_POLICY_STD_RTTI_HPP




#ifndef BOOST_NO_RTTI
#include <string_view>
#include <typeinfo>
#include <boost/core/demangle.hpp>
#endif

namespace boost::openmethod::policies {

//! Implements the @ref rtti policy using standard RTTI.
//!
//! `std_rtti` implements the `rtti` policy using the standard C++ RTTI system.
//! It is the default RTTI policy.
//!
//! @par Example
//! include:policies.cpp#std_rtti;std_rtti_dispatch
//!
//! @see [Custom RTTI](xref:ROOT:custom_rtti.adoc)
struct std_rtti : rtti {
    //! A RttiFn metafunction.
    //!
    //! @tparam Registry The registry containing this policy.
    template<class Registry>
    struct fn {
#ifndef BOOST_NO_RTTI
        //! Tests if a class is polymorphic.
        //!
        //! Evaluates to `true` if `Class` is a polymorphic class, as defined by
        //! the C++ standard, i.e. a class that contains at least one virtual
        //! function.
        //!
        //! @tparam Class A class.
        template<class Class>
        static constexpr bool is_polymorphic = std::is_polymorphic_v<Class>;

        //! Returns the static @ref type_id of a type.
        //!
        //! Returns `&typeid(Class)`, cast to `type_id`.
        //!
        //! @tparam Class A class.
        //! @return The static type_id of Class.
        template<class Class>
        static auto static_type() -> type_id {
            return &typeid(Class);
        }

        //! Returns the dynamic @ref type_id of an object.
        //!
        //! Returns `&typeid(obj)`, cast to `type_id`.
        //!
        //! @tparam Class A registered class.
        //! @param obj A reference to an instance of `Class`.
        //! @return The type_id of `obj`'s class.
        template<class Class>
        static auto dynamic_type(const Class& obj) -> type_id {
            return &typeid(obj);
        }

        //! Writes a representation of a @ref type_id to a stream.
        //!
        //! Writes the demangled name of the class identified by `type` to
        //! `stream`.
        //!
        //! @tparam Stream A SimpleOutputStream.
        //! @param type The `type_id` to write.
        //! @param stream The stream to write to.
        template<typename Stream>
        static auto type_name(type_id type, Stream& stream) -> void {
            stream << boost::core::demangle(
                reinterpret_cast<const std::type_info*>(type)->name());
        }

        //! Returns a key that uniquely identifies a class.
        //!
        //! C++ does *not* guarantee that there is a single instance of
        //! `std::type_info` per type: a class used by several modules of a
        //! program typically has one per module. `type_index` maps a `type_id`
        //! to a key that compares equal for all the `type_id`s of one class,
        //! which is what @ref initialize uses to group the registrations coming
        //! from different modules.
        //!
        //! The key is the *name*, not the address and not a `std::type_index`.
        //! `std::type_index` would delegate to `std::type_info::operator==`,
        //! and that is only as good as the platform's RTTI uniqueness:
        //! libstdc++ falls back to comparing names, but libc++ on Darwin
        //! compares uniquely-named RTTI by address. There, two modules'
        //! `type_info` objects for the same type compare *unequal* unless the
        //! symbol happens to be exported and coalesced by dyld - which, for a
        //! template like `method<Id, Signature, Registry>`, requires every
        //! template argument to have default visibility as well. Under
        //! `-fvisibility=hidden` that is not the case, the copies are not
        //! grouped, each module's method keeps its own overrider list, and
        //! calls report @ref no_overrider. Comparing names sidesteps the
        //! platform's uniqueness rules entirely.
        //!
        //! @param type A `type_id`.
        //! @return The mangled name of the class identified by `type`.
        static auto type_index(type_id type) -> std::string_view {
            return reinterpret_cast<const std::type_info*>(type)->name();
        }

        //! Casts an object to a type.
        //!
        //! Casts `obj` to a reference to an instance of `D`, using
        //! `dynamic_cast`.
        //!
        //! @tparam D A reference to a subclass of `B`.
        //! @tparam B A registered class.
        //! @param obj A reference to an instance of `B`.
        template<typename D, typename B>
        static auto dynamic_cast_ref(B&& obj) -> D {
            return dynamic_cast<D>(obj);
        }
#endif
    };
};

} // namespace boost::openmethod::policies

#endif


// 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_POLICY_VPTR_VECTOR_HPP
#define BOOST_OPENMETHOD_POLICY_VPTR_VECTOR_HPP




#include <tuple>
#include <variant>
#include <vector>

namespace boost::openmethod {

namespace policies {

//! Stores v-table pointers in a vector.
//!
//! `vptr_vector` stores v-table pointers in a global vector. If `Registry`
//! contains a @ref type_hash policy, it is used to convert `type_id`s to
//! indices. Otherwise, `type_id`s are used directly as indices.
//!
//! If the registry contains the @ref indirect_vptr policy, stores pointers to
//! pointers to v-tables in the vector.
//!
//! @par Example
//! include:policies.cpp#vptr_vector
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
struct vptr_vector : vptr {
  public:
    //! A VptrFn metafunction.
    //!
    //! Keeps track of v-table pointers using a `std::vector`.
    //!
    //! If `Registry` contains a @ref type_hash policy, it is used to convert
    //! `type_id`s to indices; otherwise, `type_id`s are used as indices.
    //!
    //! If `Registry` contains the @ref indirect_vptr policy, stores pointers to
    //! pointers to v-tables in the map.
    //!
    //! @tparam Registry The registry containing this policy.
    template<class Registry>
    struct fn {
        using type_hash =
            typename Registry::template policy<policies::type_hash>;
        static constexpr auto has_type_hash = !std::is_same_v<type_hash, void>;

        // vptr_vector::initialize reads the type_hash policy's state, so that
        // policy must have been initialized first, i.e. must appear before
        // vptr_vector in the registry's policy_list (policies are initialized
        // left to right; see detail::initialize_policies). type_hash aliases
        // the policy's fn<Registry>, so locate the policy itself in the list.
        static_assert(
            !has_type_hash ||
                boost::mp11::mp_find<
                    typename Registry::policy_list,
                    detail::find_first_derived_of<
                        policies::type_hash, typename Registry::policy_list>>::
                        value < boost::mp11::mp_find<
                                    typename Registry::policy_list,
                                    detail::find_first_derived_of<
                                        vptr_vector,
                                        typename Registry::policy_list>>::value,
            "the type_hash policy must appear before vptr_vector in the "
            "registry's policy_list");

      public:
        //! The policy's state: the vector of v-table pointers. Held in the
        //! registry's shared state (see @ref registry_state).
        struct state {
            //! The v-table pointers (pointers to v-table pointers if
            //! `Registry` contains the @ref indirect_vptr policy), indexed by
            //! (possibly hashed) type ids.
            std::conditional_t<
                Registry::has_indirect_vptr, std::vector<const vptr_type*>,
                std::vector<vptr_type>>
                vptrs;
        };

        //! Stores the v-table pointers.
        //!
        //! If `Registry` contains a @ref type_hash policy, its `initialize`
        //! function is called. Its result determines the size of the vector.
        //! The v-table pointers are copied into the vector.
        //!
        //! @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(
            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::hash_range();
                size = max_value + 1;
            } else {
                size = 0;

                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));
                    }
                }

                ++size;
            }

            st().vptrs.resize(size);

            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;

                    if constexpr (has_type_hash) {
                        index = type_hash::hash(*type_iter);
                    } else {
                        index = std::size_t(*type_iter);
                    }

                    if constexpr (Registry::has_indirect_vptr) {
                        st().vptrs[index] = &iter->vptr();
                    } else {
                        st().vptrs[index] = iter->vptr();
                    }
                }
            }
        }

        //! Returns a *reference* to a v-table pointer for an object.
        //!
        //! Acquires the dynamic @ref type_id of `arg`, using the registry's
        //! @ref rtti policy.
        //!
        //! If the registry has a @ref type_hash policy, uses it to convert the
        //! type id to an index; otherwise, uses the type_id as the index.
        //!
        //! If the registry contains the @ref runtime_checks policy, verifies
        //! that the index falls within the limits of the vector. If it does
        //! not, and if the registry contains a @ref error_handler policy, calls
        //! its @ref error function with a @ref missing_class value, then
        //! terminates the program with `abort`.
        //!
        //! @tparam Class A registered class.
        //! @param arg A reference to a const object of type `Class`.
        //! @return A reference to the v-table pointer for `Class`.
        template<class Class>
        static auto dynamic_vptr(const Class& arg) -> const vptr_type& {
            return vptr(Registry::rtti::dynamic_type(arg));
        };

        //! Returns a *reference* to a v-table pointer for a type.
        //!
        //! If the registry has a @ref type_hash policy, uses it to convert the
        //! type id to an index; otherwise, uses the type_id as the index.
        //!
        //! If the registry contains the @ref runtime_checks policy, verifies
        //! that the index falls within the limits of the vector. If it does
        //! not, and if the registry contains a @ref error_handler policy, calls
        //! its @ref error function with a @ref missing_class value, then
        //! terminates the program with `abort`.
        //!
        //! @param type A `type_id`.
        //! @return A reference to the v-table pointer for `type`.
        static auto vptr(type_id type) -> const vptr_type& {
            std::size_t index;
            if constexpr (has_type_hash) {
                index = type_hash::hash(type);
            } else {
                index = std::size_t(type);

                if constexpr (Registry::has_runtime_checks) {
                    std::size_t max_index = st().vptrs.size();

                    if (index >= max_index) {
                        if constexpr (Registry::has_error_handler) {
                            missing_class error;
                            error.type = type;
                            Registry::error_handler::error(error);
                        }

                        abort();
                    }
                }
            }

            if constexpr (Registry::has_indirect_vptr) {
                return *st().vptrs[index];
            } else {
                return st().vptrs[index];
            }
        }

        //! Releases the memory allocated by `initialize`.
        //!
        //! @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(const std::tuple<Options...>&) -> void {
            st().vptrs.clear();
        }

      private:
        static auto& st() {
            return Registry::template state<vptr_vector>();
        }
    };
};

} // namespace policies
} // namespace boost::openmethod

#endif


// 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_POLICIES_STANDARD_ERROR_OUTPUT_HPP
#define BOOST_OPENMETHOD_POLICIES_STANDARD_ERROR_OUTPUT_HPP




// 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_OSTDSTREAM_HPP
#define BOOST_OPENMETHOD_DETAIL_OSTDSTREAM_HPP

#include <array>
#include <cstdio>
#include <charconv>
#include <random>
#include <string_view>

namespace boost::openmethod {

namespace detail {

// -----------------------------------------------------------------------------
// lightweight ostream

struct ostdstream {
    FILE* stream = nullptr;

    ostdstream(FILE* s = nullptr) : stream(s) {
    }

    void on(FILE* s = stderr) {
        this->stream = s;
    }

    void off() {
        stream = nullptr;
    }

    auto is_on() const -> bool {
        return stream != nullptr;
    }
};

struct ostderr : ostdstream {
    ostderr() : ostdstream(stderr) {
    }
};

inline auto operator<<(ostdstream& os, const char* str) -> ostdstream& {
    if (os.stream) {
        fputs(str, os.stream);
    }

    return os;
}

inline auto operator<<(ostdstream& os, const std::string_view& view)
    -> ostdstream& {
    if (os.stream) {
        fwrite(view.data(), sizeof(*view.data()), view.length(), os.stream);
    }

    return os;
}

inline auto operator<<(ostdstream& os, const void* value) -> ostdstream& {
    if (os.stream) {
        std::array<char, 20> str;
        auto end = std::to_chars(
                       str.data(), str.data() + str.size(),
                       reinterpret_cast<uintptr_t>(value), 16)
                       .ptr;
        os << std::string_view(str.data(), end - str.data());
    }

    return os;
}

inline auto operator<<(ostdstream& os, void (*value)()) -> ostdstream& {
    if (os.stream) {
        std::array<char, 20> str;
        auto end = std::to_chars(
                       str.data(), str.data() + str.size(),
                       reinterpret_cast<uintptr_t>(value), 16)
                       .ptr;
        os << std::string_view(str.data(), end - str.data());
    }

    return os;
}

inline auto operator<<(ostdstream& os, std::size_t value) -> ostdstream& {
    if (os.stream) {
        std::array<char, 20> str;
        auto end =
            std::to_chars(str.data(), str.data() + str.size(), value).ptr;
        os << std::string_view(str.data(), end - str.data());
    }

    return os;
}

} // namespace detail

} // namespace boost::openmethod

#endif


namespace boost::openmethod {

namespace policies {

//! Writes to the C standard error stream.
//!
//! `stderr_output` writes to standard error using the C API.
//!
//! @par Example
//! include:policies.cpp#stderr_output
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)
struct stderr_output : output {
    //! An OutputFn metafunction.
    template<class Registry>
    struct fn {
      public:
        //! The policy's state: the output stream object. Held in the
        //! registry's shared state (see @ref registry_state).
        struct state {
            detail::ostderr os;
        };

        [[deprecated]] inline static detail::ostderr os;

        //! Returns the stream diagnostics are written to.
        //!
        //! @return A reference to the policy's output stream.
        static auto& stream() {
            return Registry::template state<stderr_output>().os;
        }
    };
};

} // namespace policies
} // namespace boost::openmethod

#endif


// 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_POLICY_FAST_PERFECT_HASH_HPP
#define BOOST_OPENMETHOD_POLICY_FAST_PERFECT_HASH_HPP




#include <limits>
#include <random>
#include <tuple>
#include <type_traits>
#include <variant>
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4702) // unreachable code
#endif

namespace boost::openmethod {

namespace detail {

#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

struct hash_fn {
    std::size_t mult;
    std::size_t shift;
    std::size_t min_value;
    std::size_t max_value;

    auto operator()(type_id type) const -> std::size_t {
        return (mult * reinterpret_cast<uintptr>(type)) >> shift;
    }
};

} // namespace detail

namespace policies {

//! Hash type ids using a fast, perfect hash function.
//!
//! `fast_perfect_hash` implements the @ref type_hash policy using a hash
//! function in the form `H(x)=(M*x)>>S`. It attempts to determine values for
//! `M` and `S` that do not result in collisions for the set of registered
//! type_ids. This may fail for certain sets of inputs, although it is very
//! likely to succeed for addresses of `std::type_info` objects.
//!
//! There is no guarantee that every value in the codomain of the function
//! corresponds to a value in the domain, or even that the codomain is a dense
//! range of integers. In other words, a lot of space may be wasted in presence
//! of large sets of type_ids.
//!
//! @par Example
//! include:policies.cpp#fast_perfect_hash
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
struct fast_perfect_hash : type_hash {

    //! Cannot find hash factors
    struct search_error : openmethod_error {
        //! Number of attempts to find hash factors
        std::size_t attempts;
        //! Number of buckets used in the last attempt
        std::size_t buckets;

        //! Write a short description to an output stream
        //! @param os The output stream
        //! @tparam Registry The registry
        //! @tparam Stream A @ref LightweightOutputStream
        template<class Registry, class Stream>
        auto write(Stream& os) const -> void;
    };

    using errors = std::variant<search_error>;

    //! `state` layout when runtime checks are disabled.
    struct no_checks {
        detail::hash_fn fn;
    };

    //! `state` layout when runtime checks are enabled: adds the table of
    //! registered type ids used to validate hashed types.
    struct with_checks : no_checks {
        std::vector<type_id> control;
    };

    //! A TypeHashFn metafunction.
    //!
    //! @tparam Registry The registry containing this policy
    template<class Registry>
    class fn {
      public:
        //! The policy's state: the hash factors (@ref no_checks), plus the
        //! control table if the registry has runtime checks enabled (@ref
        //! with_checks). Held in the registry's shared state (see @ref
        //! registry_state).
        using state = std::conditional_t<
            Registry::has_runtime_checks, with_checks, no_checks>;

      private:
        static auto& st() {
            return Registry::template state<fast_perfect_hash>();
        }

        static void check(std::size_t index, type_id type);

        template<class InitializeContext, class... Options>
        static void initialize_aux(
            const InitializeContext& ctx, std::vector<type_id>& buckets,
            const std::tuple<Options...>& options);

      public:
        //! Finds the hash factors
        //!
        //! Attempts to find suitable values for the multiplication factor `M`
        //! and the shift amount `S` to that do not result in collisions for the
        //! specified input values.
        //!
        //! If no suitable values are found, calls the error handler with
        //! a @ref search_error object then calls `abort`.
        //!
        //! @tparam Context An @ref InitializeContext.
        //! @param ctx A Context object.
        //! @param options A tuple of option objects.
        //!
        //! Use @ref hash_range to retrieve the minimum and maximum hash
        //! values after calling this function.
        template<class Context, class... Options>
        static auto initialize(
            const Context& ctx, const std::tuple<Options...>& options) -> void {
            if constexpr (Registry::has_runtime_checks) {
                initialize_aux(ctx, st().control, options);
            } else {
                std::vector<type_id> buckets;
                initialize_aux(ctx, buckets, options);
            }
        }

        //! Returns the hash range
        //!
        //! @return A pair containing the minimum and maximum hash values.
        static auto hash_range() -> std::pair<std::size_t, std::size_t> {
            return std::pair{st().fn.min_value, st().fn.max_value};
        }

        //! Hash a type id
        //!
        //! Hash a type id.
        //!
        //! If `Registry` contains the @ref runtime_checks policy, checks that
        //! the type id is valid, i.e. if it was present in the set passed to
        //! @ref initialize. Its absence indicates that a class involved in a
        //! method definition, method overrider, or method call was not
        //! registered. In this case, signal a @ref missing_class using
        //! the registry's @ref error_handler if present; then calls `abort`.
        //!
        //! @param type The type_id to hash
        //! @return The hash value
        BOOST_FORCEINLINE
        static auto hash(type_id type) -> std::size_t {
            auto index = st().fn(type);

            if constexpr (Registry::has_runtime_checks) {
                check(index, type);
            }

            return index;
        }

        //! Releases the memory allocated by `initialize`.
        //!
        //! @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(const std::tuple<Options...>&) -> void {
            if constexpr (Registry::has_runtime_checks) {
                st().control.clear();
            }
        }
    };
};

template<class Registry>
template<class InitializeContext, class... Options>
void fast_perfect_hash::fn<Registry>::initialize_aux(
    const InitializeContext& ctx, std::vector<type_id>& buckets,
    const std::tuple<Options...>& options) {
    (void)options;

    const auto N = std::distance(ctx.classes_begin(), ctx.classes_end());

    if constexpr (mp11::mp_contains<mp11::mp_list<Options...>, trace>::value) {
        if (std::get<trace>(options).on) {
            Registry::output::stream()
                << "Finding hash factor for " << N << " types\n";
        }
    }

    std::default_random_engine rnd(13081963);
    std::size_t total_attempts = 0;
    std::size_t M = 1;

    for (auto size = N * 5 / 4; size >>= 1;) {
        ++M;
    }

    std::uniform_int_distribution<std::size_t> uniform_dist;

    for (std::size_t pass = 0; pass < 5; ++pass, ++M) {
        st().fn.shift = 8 * sizeof(type_id) - M;
        auto hash_size = 1 << M;

        if constexpr (InitializeContext::template has_option<trace>) {
            ctx.tr << "  trying with M = " << M << ", " << hash_size
                   << " buckets\n";
        }

        std::size_t attempts = 0;
        buckets.resize(hash_size);

        while (attempts < 100'000) {
            std::fill(
                buckets.begin(), buckets.end(), type_id(detail::uintptr_max));
            ++attempts;
            ++total_attempts;
            st().fn.mult = uniform_dist(rnd) | 1;
            // Reset per attempt, not just per pass: a failed attempt (a
            // collision partway through, below) has already narrowed these
            // for the type ids it did place, and a fresh `mult` produces an
            // unrelated distribution - carrying over stale min/max would
            // contaminate hash_range() and oversize the vptr vector
            // (vptr_vector sizes itself off max_value).
            st().fn.min_value = (std::numeric_limits<std::size_t>::max)();
            st().fn.max_value = (std::numeric_limits<std::size_t>::min)();

            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;
                    auto index = st().fn(type);

                    // A class can now have more than one class_info entry
                    // for the same type_id (see augment_classes() in
                    // initialize.hpp: one per module, kept distinct because
                    // their static_vptr differs under hidden visibility).
                    // Re-inserting the *same* type_id into the *same*
                    // (deterministic) slot is not a real collision - only a
                    // different type_id landing on an already-occupied slot
                    // is.
                    if (detail::uintptr(buckets[index]) !=
                            detail::uintptr_max &&
                        buckets[index] != type) {
                        goto collision;
                    }

                    st().fn.min_value = (std::min)(st().fn.min_value, index);
                    st().fn.max_value = (std::max)(st().fn.max_value, index);
                    buckets[index] = type;
                }
            }

            if constexpr (InitializeContext::template has_option<trace>) {
                ctx.tr << "  found " << st().fn.mult << " after "
                       << total_attempts << " attempts; span = ["
                       << st().fn.min_value << ", " << st().fn.max_value
                       << "]\n";
            }

            return;

        collision: {}
        }
    }

    search_error error;
    error.attempts = total_attempts;
    error.buckets = std::size_t(1) << M;

    if constexpr (Registry::has_error_handler) {
        Registry::error_handler::error(error);
    }

    abort();
}

template<class Registry>
void fast_perfect_hash::fn<Registry>::check(std::size_t index, type_id type) {
    if (index < st().fn.min_value || index > st().fn.max_value ||
        st().control[index] != type) {

        if constexpr (Registry::has_error_handler) {
            missing_class error;
            error.type = type;
            Registry::error_handler::error(error);
        }

        abort();
    }
}

template<class Registry, class Stream>
auto fast_perfect_hash::search_error::write(Stream& os) const -> void {
    os << "could not find hash factors after " << attempts
       << " attempts using up to " << buckets << " buckets\n";
}

} // namespace policies
} // namespace boost::openmethod

#endif


// 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_POLICY_VECTORED_ERROR_HPP
#define BOOST_OPENMETHOD_POLICY_VECTORED_ERROR_HPP




#include <functional>
#include <variant>

namespace boost::openmethod {

namespace policies {

//! Calls a std::function with the error.
//!
//! Wraps the error in a `std::variant`, and calls a `std::function` with it.
//! The function object is initialized to a function (@ref default_handler) that
//! writes a description of the error, using the @ref output policy, if it is
//! available in the registry.
//!
//! This is the error handler used by the default registry. In debug variants,
//! it writes an error message to `stderr`, then returns. In release variants,
//! no message is emitted. Any call by the library to the error policy is
//! immediately followed by a call to `abort`.
//!
//! By default, the library is exception-agnostic: it is exception-safe, but it
//! does not throw exceptions by itself. The program may replace the default
//! handler with a function that throws an exception, possibly preventing
//! program termination. The @ref throw_error_handler policy can also be used to
//! enable exception throwing on a registry basis.
//!
//! @par Example
//! include:policies.cpp#default_error_handler_registry;default_error_handler_set
//!
//! @see [Error Handling](xref:ROOT:error_handling.adoc)

struct default_error_handler : error_handler {
    //! A ErrorHandlerFn metafunction.
    //!
    //! @tparam Registry The registry containing this policy.
    template<class Registry>
    class fn {
        template<typename, typename, typename>
        struct error_variant_aux;

        template<
            typename T, class... Errors, class Policy, class... MorePolicies>
        struct error_variant_aux<
            T, std::variant<Errors...>,
            mp11::mp_list<Policy, MorePolicies...>> :
            error_variant_aux<
                void, std::variant<Errors...>, mp11::mp_list<MorePolicies...>> {
        };

        template<class... Errors, class Policy, class... MorePolicies>
        struct error_variant_aux<
            std::void_t<typename Policy::errors>, std::variant<Errors...>,
            mp11::mp_list<Policy, MorePolicies...>> :
            error_variant_aux<
                void,
                mp11::mp_append<
                    std::variant<Errors...>, typename Policy::errors>,
                mp11::mp_list<MorePolicies...>> {};

        template<class... Errors>
        struct error_variant_aux<
            void, std::variant<Errors...>, mp11::mp_list<>> {
            using type = std::variant<Errors...>;
        };

      public:
        //! A `std::variant` containing an instance of a subclass of @ref
        //! openmethod_error.
        using error_variant = typename error_variant_aux<
            void,
            std::variant<
                not_initialized, no_overrider, ambiguous_call, missing_class,
                missing_base, odr_violation, final_error>,
            typename Registry::policy_list>::type;

        //! The type of the error handler function object.
        using function_type = std::function<void(const error_variant& error)>;

        //! The policy's state: the error handler function object. Held in
        //! the registry's shared state (see @ref registry_state).
        struct state {
            function_type handler;
        };

      private:
        static auto& st() {
            return Registry::template state<default_error_handler>();
        }

      public:
        //! Calls a function with the error object, wrapped in an @ref
        //! error_variant.
        //!
        //! @tparam Error A subclass of @ref openmethod_error.
        //! @param error The error object.
        template<class Error>
        static auto error(const Error& error) -> void {
            auto handler = st().handler ? st().handler : default_handler;
            handler(error_variant(error));
        }

        //! Sets the function to be called to handle errors.
        //!
        //! Sets the error handler function to a new value, and returns the
        //! previous function.
        //!
        //! @param new_handler the new function.
        //! @return The previous function.
        // coverity[auto_causes_copy]
        static auto set(function_type new_handler) -> function_type {
            auto prev = std::exchange(st().handler, std::move(new_handler));
            return prev ? prev : default_handler;
        }

        //! The default error handler function.
        //!
        //! @param error A variant containing the error.
        //!
        //! If `Registry` contains an @ref output policy, writes a description
        //! of the error; otherwise, does nothing.
        static auto default_handler(const error_variant& error) -> void {
            if constexpr (Registry::has_output) {
                std::visit(
                    [](auto&& error) {
                        error.template write<Registry>(
                            Registry::output::stream());
                    },
                    error);
                Registry::output::stream() << "\n";
            }
        }
    };
};

} // namespace policies
} // namespace boost::openmethod

#endif


namespace boost::openmethod {

//! Default registry.
//!
//! `default_registry` is a predefined @ref registry, and the default value of
//! @ref BOOST_OPENMETHOD_DEFAULT_REGISTRY.
//! It contains the following policies:
//! @li @ref policies::std_rtti: Use standard RTTI.
//! @li @ref policies::fast_perfect_hash: Use a fast perfect hash function to
//!   map type ids to indices.
//! @li @ref policies::vptr_vector: Store v-table pointers in a @c std::vector.
//! @li @ref policies::default_error_handler: Write short diagnostic messages.
//! @li @ref policies::stderr_output: Write messages to @c stderr.
//!
//! If @ref BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
//! is defined, `default_registry` also includes the @ref runtime_checks policy.
//!
//! @note Use `BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS` with caution, as
//! inconsistent use of the macro can cause ODR violations. If defined, it must
//! be in all the translation units in the program that use `default_registry`,
//! including those pulled from libraries.
//!
//! For a program and its shared libraries to contribute to the same
//! `default_registry`, its state must be shared across the modules, with
//! @ref BOOST_OPENMETHOD_IMPORT_REGISTRY, @ref BOOST_OPENMETHOD_EXPORT_REGISTRY
//! and @ref BOOST_OPENMETHOD_INSTANTIATE_REGISTRY:
//! @code
//! // header, every translation unit of a client module:
//! BOOST_OPENMETHOD_IMPORT_REGISTRY(boost::openmethod::default_registry);
//! // header, every translation unit of the owning module:
//! BOOST_OPENMETHOD_EXPORT_REGISTRY(boost::openmethod::default_registry);
//! // exactly one .cpp of the owning module:
//! BOOST_OPENMETHOD_INSTANTIATE_REGISTRY(boost::openmethod::default_registry);
//! @endcode
struct default_registry :
    registry<
        policies::std_rtti, policies::fast_perfect_hash, policies::vptr_vector,
        policies::default_error_handler, policies::stderr_output
#ifdef BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
        ,
        policies::runtime_checks
#endif
        > {
};

namespace detail {

static odr_check<default_registry> default_registry_odr_check_instance;

}

//! Indirect registry.
//!
//! `indirect_registry` is a predefined @ref registry that uses the same
//! policies as @ref default_registry, plus the @ref indirect_vptr policy.
//!
//! `indirect_registry` has its own state, separate from `default_registry`'s.
//! Share it across shared libraries exactly as for @ref default_registry,
//! naming `indirect_registry` in the macros.
//!
//! @see @ref policies::indirect_vptr
struct indirect_registry : default_registry::with<policies::indirect_vptr> {};

} // namespace boost::openmethod

// The library only tests BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS, it never
// defines it - that is up to the program. MrDocs extracts macros from
// `#define` directives, so give it one to extract. It is placed after
// `default_registry`, whose definition tests the macro, so that documenting it
// cannot change what is documented.
#ifdef __MRDOCS__
#ifndef BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
//! Enable runtime checks in @ref boost::openmethod::default_registry.
//!
//! May be defined by a program before including
//! `<boost/openmethod/default_registry.hpp>` (or any header that includes it,
//! like `<boost/openmethod.hpp>`) to enable runtime checks. See
//! @ref boost::openmethod::default_registry for details.
//!
//! @par Example
//!
//! Define the symbol before including the library, or on the compiler command
//! line:
//!
//! @code
//! #define BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
//! #include <boost/openmethod.hpp>
//! @endcode
//!
//! @note The error goes to the registry's
//! @ref boost::openmethod::policies::error_handler policy, which writes the
//! description shown in the comments; the program is then terminated. A
//! handler may throw instead, to keep the program running.
//!
//! The checks catch what @ref boost::openmethod::initialize cannot. Below,
//! `Bulldog` is never registered; nothing is amiss until a call passes one,
//! and only then is @ref boost::openmethod::missing_class reported:
//!
//! include:errors_missing_class_call.cpp#classes;use
//!
//! Without the checks the same call proceeds on a v-table pointer that was
//! never set up, and the behavior is undefined.
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
#define BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS
#endif
#endif

#endif




#ifndef BOOST_OPENMETHOD_DEFAULT_REGISTRY
//! Default value for `Registry`.
//!
//! The name of the default registry.
//!
//! `BOOST_OPENMETHOD_DEFAULT_REGISTRY` is the default value for the `Registry`
//! template parameter of @ref boost::openmethod::method,
//! @ref boost::openmethod::use_classes, @ref boost::openmethod::virtual_ptr,
//! and all the constructs that take a registry as a template argument.
//!
//! `BOOST_OPENMETHOD_DEFAULT_REGISTRY` can be defined by a program to change
//! the default registry globally, *before* including
//! `<boost/openmethod/core.hpp>` (or any header that includes it, like
//! `<boost/openmethod.hpp>`). After that, changing its value has no effect,
//! even on other macros.
//!
//! To use a registry that the library provides, name it in the macro:
//!
//! @code
//! #define BOOST_OPENMETHOD_DEFAULT_REGISTRY boost::openmethod::indirect_registry
//! #include <boost/openmethod.hpp>
//! @endcode
//!
//! To use a registry of your own, *declare* the class before the include, and
//! *define* it after:
//!
//! @code
//! struct my_registry;
//! #define BOOST_OPENMETHOD_DEFAULT_REGISTRY my_registry
//!
//! #include <boost/openmethod.hpp>
//! // plus any policies/ and interop/ headers needed, in any order
//!
//! struct my_registry
//!     : boost::openmethod::default_registry::with<my_policy> {};
//! @endcode
//!
//! Only the declaration has to precede the include. Deferring the definition is
//! what makes it possible to build the registry from the policies that
//! `<boost/openmethod.hpp>` brings in - and from policies of your own, written
//! against them.
//!
//! The registry must be complete before the first construct that instantiates
//! it: a `BOOST_OPENMETHOD*` macro, @ref boost::openmethod::use_classes,
//! @ref boost::openmethod::method, @ref boost::openmethod::virtual_ptr, or
//! @ref BOOST_OPENMETHOD_IMPORT_REGISTRY and its companions.
//!
//! @note The value must name a class, not a typedef or an alias template, and
//! the declaration must use the same class-key as the definition. Qualify the
//! name (`::my_registry`, `myapp::my_registry`) if it could also be found in
//! namespace `boost::openmethod` - `registry` in particular.
//!
//! @note Use this feature with caution, as it will cause ODR violations if
//! different translation units define different default registries.
//!
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
#define BOOST_OPENMETHOD_DEFAULT_REGISTRY ::boost::openmethod::default_registry
#endif

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4100)
#pragma warning(disable : 4646)
#pragma warning(disable : 4702) // unreachable code
#endif

//! Top namespace of the library.
namespace boost::openmethod {

// Hide the `detail::` qualification of the exposition-only traits from MrDocs,
// which documents them as members of `boost::openmethod`.
//
// MrDocs renders the condition of an `enable_if_t` used as a defaulted template
// argument as a C++20 requires-clause, by copying the *raw source text* spanning
// the condition expression - it does not walk the expression tree
// (cppalliance/mrdocs#1016). A macro is thus elided only when it sits *before*
// the first token of the condition, where it expands to nothing under
// `__MRDOCS__` and so falls outside the copied range; anywhere inside the
// condition - after a `!`, after a `&&`, inside parentheses - its name is
// printed verbatim. That holds whatever shape the macro has - the function-like
// form below, an object-like one, or a bare `#ifndef __MRDOCS__` around
// `detail::`.
//
// Hence the rule for a condition mentioning an exposition-only trait:
//
// - `BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)` must be the first thing in the
//   condition. Spell a negation as `Trait<T> == false`, not `!Trait<T>`.
// - One trait per condition. When a constraint needs several, give each its own
//   defaulted template parameter - MrDocs joins them with `&&`, in order, so the
//   rendered clause is unchanged, and substitution short-circuits at the first
//   failure.
//
// Only expressions are affected. Types are printed from the AST, so the macro
// may appear anywhere in one (see `method::operator()`).
#ifdef __MRDOCS__
#define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
#define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
#define BOOST_OPENMETHOD_UNLESS_MRDOCS(...)
#else
#define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS namespace detail {
#define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS }
#define BOOST_OPENMETHOD_UNLESS_MRDOCS(...) __VA_ARGS__
#endif

namespace detail {
using sfinae = void;
}

template<
    class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY,
    typename = detail::sfinae>
class virtual_ptr;

// =============================================================================
// Helpers

namespace detail {

using macro_default_registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY;

template<typename...>
struct extract_registry;

template<>
struct extract_registry<> {
    using registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY;
    using others = mp11::mp_list<>;
};

template<typename Type>
struct extract_registry<Type> {
    using registry = std::conditional_t<
        is_registry<Type>, Type, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
    using others = std::conditional_t<
        is_registry<Type>, mp11::mp_list<>, mp11::mp_list<Type>>;
};

template<typename Type1, typename Type2, typename... MoreTypes>
struct extract_registry<Type1, Type2, MoreTypes...> {
    static_assert(!is_registry<Type1>, "policy must be the last in the list");
    using registry = typename extract_registry<Type2, MoreTypes...>::registry;
    using others = mp11::mp_push_front<
        typename extract_registry<Type2, MoreTypes...>::others, Type1>;
};

template<class Registry, class... Class>
struct init_type_ids;

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 Base, class Derived>
struct is_unambiguous_accessible_base_of : std::is_base_of<Base, Derived> {
    static_assert(
        std::is_base_of_v<Base, Derived> ==
            std::is_convertible_v<Derived&, Base&>,
        "class must be an accessible unambiguous base, repeated inheritance is "
        "not "
        "supported");
};

// Collect the base classes of a list of classes. The result is a mp11 map that
// associates each class to a list starting with the class itself, followed by
// all its bases, as per std::is_base_of. Thus the list includes the class
// itself at least twice: at the front, and down the list, as its own improper
// base. The direct and indirect bases are all included. The runtime will
// extract the direct proper bases.
template<typename... Cs>
using inheritance_map = mp11::mp_list<boost::mp11::mp_push_front<
    boost::mp11::mp_filter_q<
        boost::mp11::mp_bind_back<is_unambiguous_accessible_base_of, Cs>,
        mp11::mp_list<Cs...>>,
    Cs>...>;

// =============================================================================
// optimal_cast

template<typename B, typename D, typename = void>
struct requires_dynamic_cast_ref_aux : std::true_type {};

template<typename B, typename D>
struct requires_dynamic_cast_ref_aux<
    B, D, std::void_t<decltype(static_cast<D>(std::declval<B>()))>> :
    std::false_type {};

template<class B, class D>
constexpr bool requires_dynamic_cast =
    detail::requires_dynamic_cast_ref_aux<B, D>::value;

template<class Registry, class D, class B>
auto optimal_cast(B&& obj) -> decltype(auto) {
    if constexpr (requires_dynamic_cast<B, D>) {
        return Registry::rtti::template dynamic_cast_ref<D>(
            std::forward<B>(obj));
    } else {
        return static_cast<D>(obj);
    }
}

// =============================================================================
// Common details

template<typename T>
struct is_virtual : std::false_type {};

template<typename T>
struct is_virtual<virtual_<T>> : std::true_type {};

template<typename T>
struct remove_virtual_aux {
    using type = T;
};

template<typename T>
struct remove_virtual_aux<virtual_<T>> {
    using type = T;
};

template<typename T>
using remove_virtual_ = typename remove_virtual_aux<T>::type;

template<typename T, class Registry, typename = void>
struct virtual_type_aux {
    using type = void;
};

template<typename T, class Registry>
struct virtual_type_aux<
    T, Registry,
    std::void_t<typename virtual_traits<T, Registry>::virtual_type>> {
    using type = typename virtual_traits<T, Registry>::virtual_type;
};

template<typename T, class Registry>
using virtual_type = typename virtual_type_aux<T, Registry>::type;

template<typename MethodArgList>
using virtual_types = boost::mp11::mp_transform<
    remove_virtual_, boost::mp11::mp_filter<detail::is_virtual, MethodArgList>>;

} // namespace detail

BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS

//! Removes the virtual_<> decorator, if present (exposition only).
//!
//! Provides a nested `type` equal to `T`. The template is specialized for
//! `virtual_<T>`.
//!
//! @tparam T A type.
template<typename T>
struct StripVirtualDecorator {
    //! Same as `T`
    using type = T;
};

//! Removes the virtual_<> decorator (exposition only).
//!
//! Provides a nested `type` equal to `T`.
//!
//! @tparam T A type.
template<typename T>
struct StripVirtualDecorator<virtual_<T>> {
    //! Same as `T`.
    using type = T;
};

BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS

// =============================================================================
// virtual_traits

//! Traits for types used as virtual parameters.
//!
//! `virtual_traits` must be specialized for each type that can be used as a
//! virtual parameters. It enables methods to:
//! @li find the type of the object the argument refers to (e.g. @c Node from
//! @c Node&)
//! @li obtain a non-modifiable reference to that object (e.g. a @c const
//! @c Node& from @c Node&)
//! @li cast the argument to another type (e.g. cast a @c Node& to a @c Plus&)
//!
//! @par Requirements
//!
//! Specializations of `virtual_traits` must provide the members described to
//! the @ref VirtualTraits blueprint.
//!
//! @tparam T A type referring (in the broad sense) to an instance of a class.
//! @tparam Registry A @ref registry.
template<typename T, class Registry>
struct virtual_traits;

//! Specialize virtual_traits for lvalue reference types.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @tparam Registry A @ref registry.
template<class Class, class Registry>
struct virtual_traits<Class&, Registry> {
    //! `Class`, stripped from cv-qualifiers.
    using virtual_type = std::remove_cv_t<Class>;

    //! Return a reference to a non-modifiable `Class` object.
    //! @param arg A reference to a non-modifiable `Class` object.
    //! @return A reference to the same object.
    static auto peek(const Class& arg) -> const Class& {
        return arg;
    }

    //! Cast to another type.
    //!
    //! Cast an object to another type. If possible, use `static_cast`.
    //! Otherwise, use `Registry::rtti::dynamic_cast_ref`.
    //!
    //! @tparam Derived A lvalue reference type.
    //! @param obj A reference to a `Class` object.
    //! @return A reference to the same object, cast to `Derived`.
    template<typename Derived>
    static auto cast(Class& obj) -> Derived {
        static_assert(std::is_lvalue_reference_v<Derived>);
        return detail::optimal_cast<Registry, Derived>(obj);
    }
};

//! Specialize virtual_traits for xvalue reference types.
//!
//! @tparam T A xvalue reference type.
//! @tparam Registry A @ref registry.
template<class Class, class Registry>
struct virtual_traits<Class&&, Registry> {
    //! Same as `Class`.
    using virtual_type = Class;

    //! Return a reference to a non-modifiable `Class` object.
    //! @param arg A reference to a non-modifiable `Class` object.
    //! @return A reference to the same object.
    static auto peek(const Class& arg) -> const Class& {
        return arg;
    }

    //! Cast to another type.
    //!
    //! Cast an object to another type. If possible, use `static_cast`.
    //! Otherwise, use `Registry::rtti::dynamic_cast_ref`.
    //!
    //! @tparam Derived A rvalue reference type.
    //! @param obj A reference to a `Class` object.
    //! @return A reference to the same object, cast to `Derived`.
    template<typename Derived>
    static auto cast(Class&& obj) -> Derived {
        static_assert(std::is_rvalue_reference_v<Derived>);
        return detail::optimal_cast<Registry, Derived>(obj);
    }
};

//! Specialize virtual_traits for pointer types.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @tparam Registry A @ref registry.
template<class Class, class Registry>
struct virtual_traits<Class*, Registry> {
    //! `Class`, stripped from cv-qualifiers.
    using virtual_type = std::remove_cv_t<Class>;

    //! Return a reference to a non-modifiable `Class` object.
    //! @param arg A pointer to a non-modifiable `Class` object.
    //! @return A const reference to the same object.
    static auto peek(const Class* arg) -> const Class& {
        return *arg;
    }

    //! Cast to another type.
    //!
    //! Cast an object to another type. If possible, use `static_cast`.
    //! Otherwise, use `Registry::rtti::dynamic_cast_ref`.
    //!
    //! @tparam Derived A pointer type.
    //! @param obj A pointer to a `Class` object.
    //! @return A pointer to the same object, cast to `Derived`.
    template<typename Derived>
    static auto cast(Class* ptr) -> Derived {
        static_assert(std::is_pointer_v<Derived>);

        if constexpr (detail::requires_dynamic_cast<Class*, Derived>) {
            return dynamic_cast<Derived>(ptr);
        } else {
            return static_cast<Derived>(ptr);
        }
    }
};

namespace detail {

template<class...>
struct use_class_aux;

template<class Registry, class Class, typename... Bases>
struct use_class_aux<Registry, mp11::mp_list<Class, Bases...>> :
    std::conditional_t<
        Registry::has_deferred_static_rtti, detail::deferred_class_info,
        detail::class_info> {
    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::has_deferred_static_rtti) {
            resolve_type_ids();
        }

        // coverity[uninit] - zero-initialized static storage
        Registry::static_::st.classes.push_back(*this);
    }

    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::static_::st.classes.remove(*this);
    }
};

template<class Registry, class Class, typename... Bases>
type_id use_class_aux<
    Registry, mp11::mp_list<Class, Bases...>>::bases[sizeof...(Bases)];

template<class... Classes>
using use_classes_tuple_type = boost::mp11::mp_apply<
    detail::tuple,
    boost::mp11::mp_transform_q<
        boost::mp11::mp_bind_front<
            detail::use_class_aux,
            typename detail::extract_registry<Classes...>::registry>,
        boost::mp11::mp_apply<
            detail::inheritance_map,
            boost::mp11::mp_unique<
                typename detail::extract_registry<Classes...>::others>>>>;

} // namespace detail

//! Add classes to a registry
//!
//! `use_classes` is a registrar class that adds one or more classes to a
//! registry.
//!
//! Classes potentially involved in a method definition, an overrider, or a
//! method call must be registered via `use_classes`. A class may be registered
//! multiple times. A class and its direct bases must be listed together in one
//! or more instantiations of `use_classes`.
//!
//! If a class is identified by different type ids in different translation
//! units, it must be registered in as many translation units as necessary for
//! `use_classes` to register all the type ids. This situation can occur when
//! using standard RTTI, because the address of the `type_info` objects are used
//! as type ids, and the standard does not guarantee that there is exactly one
//! such object per class. The only such case known to the author is when using
//! Windows DLLs.
//!
//! Virtual and multiple inheritance are supported, with the exclusion of
//! repeated inheritance.
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
//! @see [Registries and Policies](xref:ROOT:registries_and_policies.adoc)
template<class... Classes>
class use_classes {
    detail::use_classes_tuple_type<Classes...> tuple;
};

// -----------------------------------------------------------------------------
// reflection-based class registration

namespace detail {

#if BOOST_OPENMETHOD_HAS_REFLECTION

// One registrar per entry, for the whole program - not per translation unit, as
// `BOOST_OPENMETHOD_CLASSES` produces. Same mechanism as
// `inplace_vptr_use_classes`: an `inline` variable template, instantiated by
// odr-use. Keyed on the entry rather than on the class, because a class' base
// list depends on what else was registered alongside it.
template<class Registry, class Entry>
inline use_class_aux<Registry, Entry> reflected_class_registrar;

// Register every class the scan selected, each with its direct bases, as
// `reflected_registered_classes` computed them.
template<class Registry, class... Entries>
BOOST_FORCEINLINE auto use_reflected_classes(mp11::mp_list<Entries...>*)
    -> void {
    (..., (void)&reflected_class_registrar<Registry, Entries>);
}

#endif

} // namespace detail

// =============================================================================
// virtual_ptr

//! Return the v-table pointer of an object (ADL customization point).
//!
//! This declaration is a catch-all that matches any argument list and returns
//! `void`, denoting the absence of customization. If an overload beats it for a
//! given argument type and registry, that overload is used to acquire a v-table
//! pointer instead of the registry's @ref policies::vptr policy.
//!
//! The library uses `boost_openmethod_vptr`, if found, when dispatching via a
//! @ref virtual_ parameter. It is not used by @ref virtual_ptr; wrapping an
//! object that has an overload in a @ref virtual_ptr is rejected at compile
//! time.
//!
//! @par Requirements
//!
//! The library uses argument-dependent lookup to find an overload that
//! satisfies the following requirements:
//!
//! @li The first parameter is a `const` lvalue reference to the virtual
//! argument.
//!
//! @li The second parameter is a pointer to a registry. Its role is to pass the
//! registry's *class* to the overload. It must not be dereferenced - its value
//! is `nullptr`. It may instead be `void*` if the overload does not need the
//! registry.
//!
//! @li The return type is @ref vptr_type.
//!
//! @par Example
//!
//! `Animal` carries its v-table pointer, and is registry-agnostic:
//!
//! include:../examples/virtual_.cpp#virtual_intrusive
//!
//! @see @ref inplace_vptr_base
//! @see [Virtual Pointer Alternatives](xref:ROOT:virtual_ptr_alt.adoc)
//! @see [Custom RTTI](xref:ROOT:custom_rtti.adoc)
void boost_openmethod_vptr(...);

namespace detail {

template<typename, class, typename = void>
struct is_smart_ptr_aux : std::false_type {};

template<typename Class, class Registry>
struct is_smart_ptr_aux<
    Class, Registry,
    std::void_t<
        typename virtual_traits<Class, Registry>::template rebind<Class>>> :
    std::true_type {};

template<class Class, class Other, class Registry, typename = void>
struct same_smart_ptr_aux : std::false_type {};

template<class Class, class Other, class Registry>
struct same_smart_ptr_aux<
    Class, Other, Registry,
    std::void_t<typename virtual_traits<Class, Registry>::template rebind<
        typename Other::element_type>>> :
    std::is_same<
        Other,
        typename virtual_traits<Class, Registry>::template rebind<
            typename Other::element_type>> {};

} // namespace detail

BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS

//! Test if argument is polymorphic (exposition only)
//!
//! Evaluates to `true` if `Class` is a polymorphic type, according to the
//! `rtti` policy of `Registry`.
//!
//! If Registry's `rtti` policy is std_rtti`, this is the same as
//! `std::is_polymorphic`. However, other `rtti` policies may have a different
//! view of what is polymorphic.
//!
//! @tparam Class A class type.
//! @tparam Registry A registry.
template<class Class, class Registry>
constexpr bool IsPolymorphic = Registry::rtti::template is_polymorphic<Class>;

//! Test if argument is a smart pointer (exposition only)
//!
//! Evaluates to `true` if `Class` is a smart pointer type, and false otherwise.
//! `Class` is considered a smart pointer if `virtual_traits<Class, Registry>`
//! exists and it defines a nested template `rebind<T>` that can be instantiated
//! with `Class`.
//!
//! @tparam Class A class type.
//! @tparam Registry A registry.
template<typename Class, class Registry>
constexpr bool IsSmartPtr = detail::is_smart_ptr_aux<Class, Registry>::value;

//! Test if arguments are same kind of smart pointers (exposition only)
//!
//! Evaluates to `true` if `Class` and `Other` are both smart pointers of the
//! same type.
//!
//! @tparam Class A class type.
//! @tparam Other Another class type.
//! @tparam Registry A registry.
template<class Class, class Other, class Registry>
constexpr bool SameSmartPtr =
    detail::same_smart_ptr_aux<Class, Other, Registry>::value;

BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS

template<class Registry, typename Arg>
inline auto final_virtual_ptr(Arg&& obj);

namespace detail {

template<class Class, class Registry>
struct is_virtual<virtual_ptr<Class, Registry, void>> : std::true_type {};

template<class Class, class Registry>
struct is_virtual<virtual_ptr<Class, Registry, void>&> : std::true_type {};

template<class Class, class Registry>
struct is_virtual<const virtual_ptr<Class, Registry, void>&> :
    std::true_type {};

template<typename>
struct is_virtual_ptr_aux : std::false_type {};

template<class Class, class Registry>
struct is_virtual_ptr_aux<virtual_ptr<Class, Registry, void>> :
    std::true_type {};

template<class Class, class Registry>
struct is_virtual_ptr_aux<const virtual_ptr<Class, Registry, void>&> :
    std::true_type {};

template<typename T>
constexpr bool is_virtual_ptr = detail::is_virtual_ptr_aux<T>::value;

template<class Class, class Registry>
constexpr bool has_vptr_fn = std::is_same_v<
    decltype(boost_openmethod_vptr(
        std::declval<const Class&>(), std::declval<Registry*>())),
    vptr_type>;

BOOST_OPENMETHOD_DETAIL_HAS_STATIC_FN(vptr);

template<class Registry, class ArgType>
decltype(auto) acquire_vptr(const ArgType& arg) {
    // A class with a boost_openmethod_vptr overload does not need to be
    // wrapped: virtual_ptr and the hook fill the same goal, fast access
    // to the v-table pointer. The hook also returns the vptr by value,
    // which indirect registries cannot store (see box_vptr).
    static_assert(
        !has_vptr_fn<ArgType, Registry>,
        "do not wrap an object that has a boost_openmethod_vptr overload "
        "in a virtual_ptr; call methods directly on the object");

    Registry::require_initialized();

    if constexpr (has_vptr<
                      virtual_traits<const ArgType&, Registry>,
                      const ArgType&>) {
        return virtual_traits<const ArgType&, Registry>::vptr(arg);
    } else {
        return Registry::template policy<policies::vptr>::dynamic_vptr(arg);
    }
}

template<bool Indirect>
inline auto box_vptr(const vptr_type& vp) {
    if constexpr (Indirect) {
        return &vp;
    } else {
        return vp;
    }
}

inline auto unbox_vptr(vptr_type vp) {
    return vp;
}

inline auto unbox_vptr(const vptr_type* vpp) {
    return *vpp;
}

inline vptr_type null_vptr = nullptr;

} // namespace detail

//! Create a `virtual_ptr` for an object of a known exact class.
//!
//! Creates a @ref virtual_ptr to an object, setting its v-table pointer
//! according to the declared type of its argument. Assumes that the static and
//! dynamic types are the same. Sets the v-table pointer to the
//! @ref registry::static_vptr for the class.
//!
//! `Class` is _not_ required to be polymorphic.
//!
//! Nothing is looked up at runtime. Constructing a `virtual_ptr` from a
//! reference or a pointer reads the object's dynamic type through the
//! registry's `rtti` policy, then finds the v-table through its `vptr` policy;
//! here the v-table pointer is a static variable, read directly. It is also
//! the only way to create a `virtual_ptr` in a registry that uses
//! @ref policies::static_rtti, which has no dynamic type to consult and
//! disables the constructors that would need one.
//!
//! If runtime checks are enabled, and the argument is polymorphic, checks if
//! the static and dynamic types are the same. If not, calls the error handler
//! with a @ref final_error value, then terminates the program with `abort`.
//!
//! @par Errors
//!
//! @li @ref final_error The static and dynamic types of the object are
//! different.
//!
//! @par Example
//!
//! include:virtual_ptr.cpp#non_polymorphic_classes;final_virtual_ptr
//!
//! @tparam Registry A @ref registry.
//! @tparam Arg The type of the argument.
//! @param obj A reference to an object.
//! @return A `virtual_ptr<Class, Registry>` pointing to `obj`.
template<class Registry, typename Arg>
inline auto final_virtual_ptr(Arg&& obj) {
    using namespace detail;
    using VirtualPtr = virtual_ptr<std::remove_reference_t<Arg>, Registry>;
    using Traits = virtual_traits<Arg, Registry>;
    using Class = typename Traits::virtual_type;

    static_assert(!std::is_const_v<Class>);
    static_assert(!std::is_volatile_v<Class>);
    static_assert(!std::is_reference_v<Class>);
    static_assert(!std::is_pointer_v<Class>);

    Registry::require_initialized();

    if constexpr (
        Registry::has_runtime_checks &&
        Registry::rtti::template is_polymorphic<Class>) {

        // check that dynamic type == static type
        auto static_type = Registry::rtti::template static_type<Class>();
        auto dynamic_type = Registry::rtti::dynamic_type(Traits::peek(obj));

        // Equal type_ids settle it, and that is the common case: both come
        // from this module. Only when they differ is type_index needed, because
        // the same class has one type_id per module, so an object created
        // elsewhere yields a different type_id than this module's static_type
        // and a raw comparison alone would abort on a valid pointer. Ordering
        // the test this way keeps the check a pointer comparison except in the
        // cross-module case, where type_index may be as costly as comparing
        // names (see std_rtti::type_index).
        if (dynamic_type != static_type &&
            Registry::rtti::type_index(dynamic_type) !=
                Registry::rtti::type_index(static_type)) {
            if constexpr (is_not_void<typename Registry::error_handler>) {
                final_error error;
                error.static_type = static_type;
                error.dynamic_type = dynamic_type;
                Registry::error_handler::error(error);
            }

            abort();
        }
    }

    const vptr_type& vptr = Registry::template static_vptr<Class>;
    BOOST_ASSERT(vptr);

    return VirtualPtr(
        std::forward<Arg>(obj),
        detail::box_vptr<VirtualPtr::use_indirect_vptrs>(vptr));
}

//! Create a `virtual_ptr` for an object of a known exact class.
//!
//! This is an overload of `final_virtual_ptr` that uses the default
//! registry as the `Registry` template parameter.
//!
//! @par Example
//!
//! include:virtual_ptr.cpp#non_polymorphic_classes;final_virtual_ptr
//!
//! @see @ref final_virtual_ptr
// We could give a default value to Registry in the main template, but gcc
// doesn't like it.
template<class Arg>
inline auto final_virtual_ptr(Arg&& obj) {
    return final_virtual_ptr<BOOST_OPENMETHOD_DEFAULT_REGISTRY, Arg>(
        std::forward<Arg>(obj));
}
//! Wide pointer combining pointers to an object and its v-table
//!
//! A `virtual_ptr` is a wide pointer that combines pointers to an object and
//! its v-table. Calls to methods via `virtual_ptr` are as fast as ordinary
//! virtual function calls (typically two instructions).
//!
//! A `virtual_ptr` can be implicitly constructed from a reference, a pointer,
//! or another `virtual_ptr`, provided that they are type-compatible.
//!
//! `virtual_ptr` has specializations that use a `std::shared_ptr` or a
//! `std::unique_ptr` as the pointer to the object. The mechanism can be
//! extended to other smart pointers by specializing @ref virtual_traits. A
//! "plain" `virtual_ptr` can be constructed from a smart `virtual_ptr`, but not
//! the other way around.
//!
//! The default value for `Registry` can be customized by defining the
//! @ref BOOST_OPENMETHOD_DEFAULT_REGISTRY
//! preprocessor symbol.
//!
//! @par Requirements
//!
//! @li @ref virtual_traits must be specialized for @c Class&.
//! @li @c Class must be a class type, possibly cv-qualified, registered in
//! @c Registry.
//!
//! @tparam Class The class of the object, possibly cv-qualified
//! @tparam Registry The registry in which `Class` is registered
//! @tparam unnamed Implementation defined, use default
//!
//! @see [Smart Pointers](xref:ROOT:smart_pointers.adoc)
//! @see [Virtual Pointer Alternatives](xref:ROOT:virtual_ptr_alt.adoc)
//! @see [Performance](xref:ROOT:performance.adoc)
template<class Class, class Registry, typename>
class virtual_ptr {

    using traits = virtual_traits<Class&, Registry>;

#ifndef __MRDOCS__
    template<class, class, typename>
    friend class virtual_ptr;
    template<class, typename Arg>
    friend auto final_virtual_ptr(Arg&& obj);
#endif

    static constexpr bool is_smart_ptr = false;
    static constexpr bool use_indirect_vptrs = Registry::has_indirect_vptr;

    std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type> vp;
    Class* obj;

    template<
        class Other,
        typename = std::enable_if_t<std::is_constructible_v<Class*, Other*>>>
    virtual_ptr(Other& other, decltype(vp) vp) : vp(vp), obj(&other) {
    }

  public:
    //! Class
    //!
    //! This is the same as `Class`.
    using element_type = Class;

    //! Default constructor
    //!
    //! @note This constructor does nothing. The state of the two pointers
    //! inside the object is as specified for uninitialized variables by C++.
    virtual_ptr() = default;

    //! Construct from `nullptr`
    //!
    //! Set both object and v-table pointers to `nullptr`.
    //!
    //! @param value A `nullptr`.
    //!
    //! @par Example
    //!
    //! include:virtual_ptr.cpp#ctor_nullptr
    //!
    //! @param value A `nullptr`.
    explicit virtual_ptr(std::nullptr_t) :
        vp(detail::box_vptr<use_indirect_vptrs>(detail::null_vptr)),
        obj(nullptr) {
    }

    //! Construct a `virtual_ptr` from a reference to an object
    //!
    //! The pointer to the v-table is obtained from @ref virtual_traits,
    //! if it provides a `vptr` function, or from the
    //! @ref policies::VptrFn::dynamic_vptr of the registry's `vptr`
    //! policy otherwise. An object with a @ref boost_openmethod_vptr
    //! overload is rejected at compile time: it carries its own v-table
    //! pointer, and does not need to be wrapped in a `virtual_ptr`.
    //!
    //! @param other A reference to a polymorphic object
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#ctor_ref
    //!
    //! @par Requirements
    //! @li @c Other must be a polymorphic class, according to the @c rtti
    //! policy of @c Registry.
    //! @li @c Other* must be constructible from @c Class*.
    //!
    //! @par Errors
    //!
    //! The following errors may occur, depending on the policies selected in
    //! `Registry`:
    //!
    //! @li @ref missing_class
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                IsPolymorphic<Other, Registry> &&
            std::is_constructible_v<Class*, Other*>>>
    virtual_ptr(Other& other) :
        vp(detail::box_vptr<use_indirect_vptrs>(
            detail::acquire_vptr<Registry>(other))),
        obj(&other) {
    }

    //! Construct a `virtual_ptr` from a pointer to an object
    //!
    //! The pointer to the v-table is obtained from @ref virtual_traits,
    //! if it provides a `vptr` function, or from the
    //! @ref policies::VptrFn::dynamic_vptr of the registry's `vptr`
    //! policy otherwise. An object with a @ref boost_openmethod_vptr
    //! overload is rejected at compile time: it carries its own v-table
    //! pointer, and does not need to be wrapped in a `virtual_ptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#ctor_pointer
    //!
    //! @param other A pointer to a polymorphic object
    //!
    //! @par Requirements
    //!
    //! @li @c Other must be a polymorphic class, according to the @c rtti
    //! policy of @c Registry.
    //!
    //! @li @c Other* must be constructible from @c Class*.
    //!
    //! @par Errors
    //!
    //! The following errors may occur, depending on the policies selected in
    //! `Registry`:
    //!
    //! @li @ref missing_class
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                IsPolymorphic<Class, Registry> &&
            std::is_constructible_v<Class*, Other*>>>
    virtual_ptr(Other* other) :
        vp(detail::box_vptr<use_indirect_vptrs>(
            detail::acquire_vptr<Registry>(*other))),
        obj(other) {
    }

    //! Construct a `virtual_ptr` from another `virtual_ptr`
    //!
    //! Copy the object and v-table pointers from `other` to `this.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Examples
    //!
    //! Constructing from a plain `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#non_polymorphic_classes;ctor_vptr
    //!
    //! Constructing from a smart `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#ctor_shared_vptr
    //!
    //! No construction of a smart `virtual_ptr` from a plain `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#ctor_shared_from_plain_rejected
    //!
    //! @param other A virtual_ptr to a type-compatible object
    //!
    //! @par Requirements
    //! @li @c Other's object pointer must be assignable to a @c Class*.
    template<
        class Other,
        typename = std::enable_if_t<std::is_constructible_v<
            Class*, typename virtual_ptr<Other, Registry>::element_type*>>>
    virtual_ptr(const virtual_ptr<Other, Registry>& other) :
        vp(other.vp), obj(other.get()) {
    }

    //! Assign a `virtual_ptr` from a reference to an object
    //!
    //! The pointer to the v-table is obtained from @ref virtual_traits,
    //! if it provides a `vptr` function, or from the
    //! @ref policies::VptrFn::dynamic_vptr of the registry's `vptr`
    //! policy otherwise. An object with a @ref boost_openmethod_vptr
    //! overload is rejected at compile time: it carries its own v-table
    //! pointer, and does not need to be wrapped in a `virtual_ptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#assign_ref
    //!
    //! @param other A reference to a polymorphic object
    //!
    //! @par Requirements
    //!
    //! @li @c Other must be a polymorphic class, according to the @c rtti
    //! policy of @c Registry.
    //!
    //! @li @c Other* must be constructible from @c Class*.
    //!
    //! @par Errors
    //!
    //! The following errors may occur, depending on the policies selected in
    //! `Registry`:
    //!
    //! @li @ref missing_class
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                IsPolymorphic<Class, Registry> &&
            std::is_assignable_v<Class*&, Other*>>>
    virtual_ptr& operator=(Other& other) {
        obj = &other;
        vp = detail::box_vptr<use_indirect_vptrs>(
            detail::acquire_vptr<Registry>(other));
        return *this;
    }

    //! Assign a `virtual_ptr` from a pointer to an object
    //!
    //! The pointer to the v-table is obtained from @ref virtual_traits,
    //! if it provides a `vptr` function, or from the
    //! @ref policies::VptrFn::dynamic_vptr of the registry's `vptr`
    //! policy otherwise. An object with a @ref boost_openmethod_vptr
    //! overload is rejected at compile time: it carries its own v-table
    //! pointer, and does not need to be wrapped in a `virtual_ptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#assign_pointer
    //!
    //! @param other A pointer to a polymorphic object
    //!
    //! @par Requirements
    //! @li @c Other must be a polymorphic class, according to the @c rtti
    //! policy of @c Registry.
    //! @li @c Other* must be constructible from @c Class*.
    //!
    //! @par Errors
    //!
    //! The following errors may occur, depending on the policies selected in
    //! `Registry`:
    //!
    //! @li @ref missing_class
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                IsPolymorphic<Class, Registry> &&
            std::is_assignable_v<Class*&, Other*>>>
    virtual_ptr& operator=(Other* other) {
        obj = other;
        vp = detail::box_vptr<use_indirect_vptrs>(
            detail::acquire_vptr<Registry>(*other));
        return *this;
    }

    //! Assign a `virtual_ptr` from another `virtual_ptr`
    //!
    //! Copy the object and v-table pointers from `other` to `this.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Examples
    //!
    //! Assigning from a plain `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#non_polymorphic_classes;assign_vptr
    //!
    //! Assigning from a smart `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#assign_shared_vptr
    //!
    //! No assignment from a plain `virtual_ptr` to a smart `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#assign_shared_from_plain_rejected
    //!
    //! @param other A virtual_ptr to a type-compatible object
    //!
    //! @par Requirements
    //! @li @c Other's object pointer must be assignable to a @c Class*.
    template<
        class Other,
        typename = std::enable_if_t<std::is_assignable_v<
            Class*&, typename virtual_ptr<Other, Registry>::element_type*>>>
    virtual_ptr& operator=(const virtual_ptr<Other, Registry>& other) {
        obj = other.get();
        vp = other.vp;
        return *this;
    }

    //! Set a `virtual_ptr` to `nullptr`
    //!
    //! Set both object and v-table pointers to `nullptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#assign_nullptr
    virtual_ptr& operator=(std::nullptr_t) {
        obj = nullptr;
        vp = detail::box_vptr<use_indirect_vptrs>(detail::null_vptr);
        return *this;
    }

    //! Get a pointer to the object
    //!
    //! @return A pointer to the object
    auto get() const -> Class* {
        return obj;
    }

    //! Get a pointer to the object
    //!
    //! @return A pointer to the object
    auto operator->() const {
        return get();
    }

    //! Get a reference to the object
    //!
    //! @return A reference to the object
    auto operator*() const -> element_type& {
        return *get();
    }

    //! Get a pointer to the object
    //!
    //! @return A pointer to the object
    auto pointer() const -> element_type* {
        return obj;
    }

    //! Cast to another `virtual_ptr` type
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#cast
    //!
    //! @tparam Other The target class of the cast
    //! @return A `virtual_ptr<Other, Registry>` pointing to the same object
    //! @par Requirements
    //! @li @c Other must be a base or derived class of @c Class.
    template<
        class Other,
        typename = std::enable_if_t<
            std::is_base_of_v<element_type, Other> ||
            std::is_base_of_v<Other, element_type>>>
    auto cast() const -> decltype(auto) {
        return virtual_ptr<Other, Registry>(
            traits::template cast<Other&>(*obj), vp);
    }

    //! Construct a `virtual_ptr` for an object of a known exact class
    //!
    //! This function forwards to @ref final_virtual_ptr.
    //!
    //! @tparam Other The type of the argument
    //! @param obj A reference to an object
    //! @return A `virtual_ptr<Class, Registry>` pointing to `obj`
    template<class Other>
    static auto final(Other&& obj) {
        return final_virtual_ptr<Registry>(std::forward<Other>(obj));
    }

    //! Get the v-table pointer
    //! @return The v-table pointer
    auto vptr() const {
        return detail::unbox_vptr(this->vp);
    }
};

//! Wide pointer combining a smart pointer to an object and a pointer to its
//! v-table
//!
//! This specialization of `virtual_ptr` uses a smart pointer to track the
//! object, instead of a plain pointer.
//!
//! @tparam SmartPtr A smart pointer type
//! @tparam Registry The registry in which the underlying class is registered
//!
//! @see [Smart Pointers](xref:ROOT:smart_pointers.adoc)
template<class SmartPtr, class Registry>
class virtual_ptr<
    SmartPtr, Registry,
    std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                         IsSmartPtr<SmartPtr, Registry>>> {

#ifndef __MRDOCS__
    template<class, class, typename>
    friend class virtual_ptr;
    template<class, typename Arg>
    friend auto final_virtual_ptr(Arg&& obj);
#endif

    static constexpr bool is_smart_ptr = true;
    static constexpr bool use_indirect_vptrs = Registry::has_indirect_vptr;

    using traits = virtual_traits<SmartPtr, Registry>;

    std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type> vp;
    SmartPtr obj;

    template<
        class Other,
        typename = std::enable_if_t<std::is_constructible_v<SmartPtr*, Other*>>>
    virtual_ptr(Other& other, decltype(vp) vp) : vp(vp), obj(&other) {
    }

    template<typename Arg>
    virtual_ptr(Arg&& obj, decltype(vp) vp) :
        vp(vp), obj(std::forward<Arg>(obj)) {
    }

  public:
    //! Class pointed to by SmartPtr
    using element_type = typename SmartPtr::element_type;

    //! Default constructor
    //!
    //! Construct the object pointer using its default constructor. Set the
    //! v-table pointer to `nullptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_ctor_default
    virtual_ptr() :
        vp(detail::box_vptr<use_indirect_vptrs>(detail::null_vptr)) {
    }

    //! Construct from `nullptr`
    //!
    //! Construct the object pointer using its default constructor. Set the
    //! v-table pointer to `nullptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_ctor_nullptr
    //!
    //! @param value A `nullptr`.
    explicit virtual_ptr(std::nullptr_t) :
        vp(detail::box_vptr<use_indirect_vptrs>(detail::null_vptr)) {
    }

    virtual_ptr(const virtual_ptr& other) = default;

    virtual_ptr(virtual_ptr&& other) :
        vp(std::exchange(
            other.vp, detail::box_vptr<use_indirect_vptrs>(detail::null_vptr))),
        obj(std::move(other.obj)) {
    }

    //! Construct from a (const) smart pointer to a derived class
    //!
    //! Set the object pointer with a copy of `other`. Set the v-table pointer
    //! according to the dynamic type of `*other`.
    //!
    //! @par Examples
    //!
    //! Constructing from a `std::shared_ptr`:
    //!
    //! include:virtual_ptr.cpp#shared_ctor_const_smart_ptr
    //!
    //! A move-only smart pointer cannot be copied from. Use the move
    //! constructor instead:
    //!
    //! include:virtual_ptr.cpp#unique_copy_rejected
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a polymorphic class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c const @c Other&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_constructible_v<SmartPtr, const Other&>>,
        typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
            detail::) IsPolymorphic<typename Other::element_type, Registry>>>
    virtual_ptr(const Other& other) :
        vp(detail::box_vptr<use_indirect_vptrs>(
            other ? detail::acquire_vptr<Registry>(*other)
                  : detail::null_vptr)),
        obj(other) {
    }

    //! Construct from a smart pointer to a derived class
    //!
    //! Copy object pointer from `other` to `this`. Set the v-table pointer
    //! according to the dynamic type of `*other`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#shared_ctor_smart_ptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a polymorphic class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_constructible_v<SmartPtr, Other&>>,
        typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
            detail::) IsPolymorphic<typename Other::element_type, Registry>>>
    virtual_ptr(Other& other) :
        vp(detail::box_vptr<use_indirect_vptrs>(
            other ? detail::acquire_vptr<Registry>(*other)
                  : detail::null_vptr)),
        obj(other) {
    }

    //! Move-construct from a smart pointer to a derived class
    //!
    //! Move object pointer from `other` to `this`. Set the v-table pointer
    //! according to the dynamic type of `*other`.
    //!
    //! @par Examples
    //!
    //! Move-constructing from a `std::shared_ptr`:
    //!
    //! include:virtual_ptr.cpp#shared_ctor_move_smart_ptr
    //!
    //! Move-constructing from a `std::unique_ptr`:
    //!
    //! include:virtual_ptr.cpp#unique_ctor_move_smart_ptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a polymorphic class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_constructible_v<SmartPtr, Other&&>>,
        typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
            detail::) IsPolymorphic<typename Other::element_type, Registry>>>
    virtual_ptr(Other&& other) :
        vp(detail::box_vptr<use_indirect_vptrs>(
            other ? detail::acquire_vptr<Registry>(*other)
                  : detail::null_vptr)),
        obj(std::move(other)) {
    }

    //! Construct from a smart virtual (const) pointer to a derived class
    //!
    //! Copy the object and v-table pointers from `other`.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_ctor_const_vptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a virtual pointer to a class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_constructible_v<SmartPtr, const Other&>>>
    virtual_ptr(const virtual_ptr<Other, Registry>& other) :
        vp(other.vp), obj(other.obj) {
    }

    //! Construct-move from a virtual pointer to a derived class
    //!
    //! Move the object pointer from `other` to `this`. Copy the v-table pointer
    //! from `other`.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Examples
    //!
    //! Move-constructing from a shared `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_ctor_move_vptr
    //!
    //! Move-constructing from a unique `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#unique_ctor_move_vptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_constructible_v<SmartPtr, Other&&>>>
    virtual_ptr(virtual_ptr<Other, Registry>&& other) :
        vp(std::exchange(
            other.vp, detail::box_vptr<use_indirect_vptrs>(detail::null_vptr))),
        obj(std::move(other.obj)) {
    }

    //! Assign from `nullptr`
    //!
    //! Reset the object pointer using its default constructor. Set the
    //! v-table pointer to `nullptr`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_assign_nullptr
    //!
    //! @param value A `nullptr`.
    virtual_ptr& operator=(std::nullptr_t) {
        obj = SmartPtr();
        vp = detail::box_vptr<use_indirect_vptrs>(detail::null_vptr);
        return *this;
    }

    //! Assign from a (const) smart pointer to a derived class
    //!
    //! Copy the object pointer from `other` to `this`. Set the v-table pointer
    //! according to the dynamic type of `*other`.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#shared_assign_smart_ptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a polymorphic class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c const @c Other&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_assignable_v<SmartPtr, const Other&>>,
        typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
            detail::) IsPolymorphic<typename Other::element_type, Registry>>>
    virtual_ptr& operator=(const Other& other) {
        obj = other;
        vp = detail::box_vptr<use_indirect_vptrs>(
            detail::acquire_vptr<Registry>(*other));
        return *this;
    }

    //! Move-assign from a smart pointer to a derived class
    //!
    //! Move object pointer from `other` to `this`. Set the v-table pointer
    //! according to the dynamic type of `*other`.
    //!
    //! @par Examples
    //!
    //! Move-assigning from a `std::shared_ptr`:
    //!
    //! include:virtual_ptr.cpp#shared_assign_move_smart_ptr
    //!
    //! Move-assigning from a `std::unique_ptr`:
    //!
    //! include:virtual_ptr.cpp#unique_assign_move_smart_ptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a polymorphic class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_assignable_v<SmartPtr, Other&&>>,
        typename = std::enable_if_t<BOOST_OPENMETHOD_UNLESS_MRDOCS(
            detail::) IsPolymorphic<typename Other::element_type, Registry>>>
    virtual_ptr& operator=(Other&& other) {
        vp = detail::box_vptr<use_indirect_vptrs>(
            other ? detail::acquire_vptr<Registry>(*other) : detail::null_vptr);
        obj = std::move(other);
        return *this;
    }

    //! Assign from a smart virtual pointer to a derived class
    //!
    //! Copy the object and v-table pointers from `other` to `this`.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_assign_vptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a virtual pointer to a class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_assignable_v<SmartPtr, Other&>>>
    virtual_ptr& operator=(virtual_ptr<Other, Registry>& other) {
        obj = other.obj;
        vp = other.vp;
        return *this;
    }

    virtual_ptr& operator=(const virtual_ptr& other) = default;

    //! Assign from a smart virtual const pointer to a derived class
    //!
    //! Copy the object and v-table pointers from `other` to `this`.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Example
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_assign_const_vptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a virtual pointer to a class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_assignable_v<SmartPtr, const Other&>>>
    virtual_ptr& operator=(const virtual_ptr<Other, Registry>& other) {
        obj = other.obj;
        vp = other.vp;
        return *this;
    }

    //! Move from a virtual pointer to a derived class
    //!
    //! Move the object pointer from `other` to `this`. Copy the v-table pointer
    //! from `other`.
    //!
    //! `Other` is _not_ required to be a pointer to a polymorphic class.
    //!
    //! @par Examples
    //!
    //! Move-assigning from a shared `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#non_polymorphic_classes;shared_assign_move_vptr
    //!
    //! Move-assigning from a unique `virtual_ptr`:
    //!
    //! include:virtual_ptr.cpp#unique_assign_move_vptr
    //!
    //! @par Requirements
    //! @li @c SmartPtr and @c Other must be instantiated from the same template -
    //! e.g. both @c std::shared_ptr or both @c std::unique_ptr.
    //! @li @c Other must be a smart pointer to a class derived from
    //! @c element_type.
    //! @li @c SmartPtr must be constructible from @c Other&&.
    template<
        class Other,
        typename = std::enable_if_t<
            BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                SameSmartPtr<SmartPtr, Other, Registry> &&
            std::is_assignable_v<SmartPtr, Other&&>>>
    virtual_ptr& operator=(virtual_ptr<Other, Registry>&& other) {
        vp = std::exchange(
            other.vp, detail::box_vptr<use_indirect_vptrs>(detail::null_vptr));
        obj = std::move(other.obj);

        return *this;
    }

    //! Get a pointer to the object
    //!
    //! @return A *plain* pointer to the object
    auto get() const -> element_type* {
        return obj.get();
    }

    //! Get a pointer to the object
    //!
    //! @return A *plain* pointer to the object
    auto operator->() const -> element_type* {
        return get();
    }

    //! Get a reference to the object
    //!
    //! @return A reference to the object
    auto operator*() const -> element_type& {
        return *get();
    }

    //! Get a smart pointer to the object
    //!
    //! @return A const reference to the object pointer
    auto pointer() const -> const SmartPtr& {
        return obj;
    }

    //! Cast to another `virtual_ptr` type
    //! @tparam Other The target class of the cast
    //! @return A `virtual_ptr<Other, Registry>` pointing to the same object
    //! @par Requirements
    //! @li @c Other must be a base or a derived class of @c Class.
    template<
        class Other,
        typename = std::enable_if_t<
            std::is_base_of_v<element_type, Other> ||
            std::is_base_of_v<Other, element_type>>>
    auto cast() & -> decltype(auto) {
        using other_smart_ptr = typename traits::template rebind<Other>;

        return virtual_ptr<other_smart_ptr, Registry>(
            traits::template cast<other_smart_ptr>(obj), vp);
    }

    template<
        class Other,
        typename = std::enable_if_t<
            std::is_base_of_v<element_type, Other> ||
            std::is_base_of_v<Other, element_type>>>
    auto cast() const& -> decltype(auto) {
        using other_smart_ptr = typename traits::template rebind<Other>;

        return virtual_ptr<other_smart_ptr, Registry>(
            traits::template cast<other_smart_ptr>(obj), vp);
    }

    template<class Other>
    auto cast() && -> decltype(auto) {
        static_assert(
            std::is_base_of_v<element_type, Other> ||
            std::is_base_of_v<Other, element_type>);

        using other_smart_ptr = typename traits::template rebind<Other>;

        return virtual_ptr<other_smart_ptr, Registry>(
            traits::template cast<other_smart_ptr>(std::move(obj)), vp);
    }

    //! Construct a `virtual_ptr` from a smart pointer to an object of a known
    //! exact class
    //!
    //! This function forwards to @ref final_virtual_ptr.
    //!
    //! @tparam Other The type of the argument
    //! @param obj A reference to an object
    //! @return A `virtual_ptr<Class, Registry>` pointing to `obj`
    template<class Other>
    static auto final(Other&& obj) {
        return final_virtual_ptr<Registry>(std::forward<Other>(obj));
    }

    //! Get the v-table pointer
    //! @return The v-table pointer
    auto vptr() const {
        return detail::unbox_vptr(this->vp);
    }
};

//! Construct a `virtual_ptr` from a lvalue reference.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @param obj A lvalue reference to an object.
//! @return A `virtual_ptr<Class>`.
template<class Class>
virtual_ptr(Class& obj)
    -> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;

//! Construct a `virtual_ptr` from a xvalue reference.
//!
//! @tparam Class A class type.
//! @param obj A xvalue reference to an object.
//! @return A `virtual_ptr<Class>`.
template<class Class>
virtual_ptr(Class&& obj)
    -> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;

// Alas this is not allowed:
// template<class Registry, class Class>
// virtual_ptr<Registry>(Class&) -> virtual_ptr<Class, Registry>;

//! Compare two `virtual_ptr`s for equality.
//!
//! Compare the underlying object pointers for equality. The v-table pointers
//! are not compared.
//!!
//! @tparam Left The type of the left-hand side argument.
//! @tparam Right The type of the right-hand side argument.
//! @tparam Registry A @ref registry.
//! @param left A reference to a `virtual_ptr`.
//! @param right A reference to a `virtual_ptr`.
//! @return `true` if both `virtual_ptr`s point to the same object or both
//! are `nullptr`, `false` otherwise.
template<class Left, class Right, class Registry>
auto operator==(
    const virtual_ptr<Left, Registry>& left,
    const virtual_ptr<Right, Registry>& right) -> bool {
    return left.pointer() == right.pointer();
}

//! Compare two `virtual_ptr`s for inequality.
//!
//! Compare the underlying object pointers for inequality. The v-table pointers
//! are not compared.
//!! @tparam Left The type of the left-hand side argument.
//! @tparam Right The type of the right-hand side argument.
//! @tparam Registry A @ref registry.
//! @param left A reference to a `virtual_ptr`.
//! @param right A reference to a `virtual_ptr`.
//! @return `true` if both `virtual_ptr`s point to different objects, or one
//! is `nullptr` and the other is not, `false` otherwise.
template<class Left, class Right, class Registry>
auto operator!=(
    const virtual_ptr<Left, Registry>& left,
    const virtual_ptr<Right, Registry>& right) -> bool {
    return !(left == right);
}

//! Specialize virtual_traits for `virtual_ptr`.
//!
//! Specialize virtual_traits for `virtual_ptr`\'s passed by value.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @tparam Registry A @ref registry.
template<class Class, class Registry>
struct virtual_traits<virtual_ptr<Class, Registry>, Registry> {
    //! `Class`, stripped from cv-qualifiers.
    using virtual_type =
        std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;

    //! Return a reference to a non-modifiable `Class` object.
    //! @param arg A reference to a non-modifiable `Class` object.
    //! @return A reference to the same object.
    static auto peek(const virtual_ptr<Class, Registry>& ptr)
        -> const virtual_ptr<Class, Registry>& {
        return ptr;
    }

    //! Cast to another type.
    //!
    //! Cast a `virtual_ptr` to another type, using its `cast` member function.
    //!
    //! @param obj A lvalue reference to a `virtual_ptr`.
    //! @return A lvalue reference to a `virtual_ptr` to the same object, cast
    //! to `Derived::element_type`.
    template<typename Derived>
    static auto cast(const virtual_ptr<Class, Registry>& ptr)
        -> decltype(auto) {
        return ptr.template cast<typename Derived::element_type>();
    }

    //! Cast to another type.
    //!
    //! Cast a `virtual_ptr` to another type, using its `cast` member function.
    //!
    //! @param obj A xvalue reference to a `virtual_ptr`.
    //! @return A xvalue reference to a `virtual_ptr` to the same object, cast
    //! to `Derived::element_type`.
    template<typename Derived>
    static auto cast(virtual_ptr<Class, Registry>&& ptr) -> decltype(auto) {
        return std::move(ptr).template cast<typename Derived::element_type>();
    }
};

//! Specialize virtual_traits for `virtual_ptr`.
//!
//! Specialize virtual_traits for `virtual_ptr`\'s passed by const reference.
//!
//! @tparam Class A class type, possibly cv-qualified.
//! @tparam Registry A @ref registry.
template<class Class, class Registry>
struct virtual_traits<const virtual_ptr<Class, Registry>&, Registry> {
    //! `Class`, stripped from cv-qualifiers.
    using virtual_type =
        std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;

    //! Return a reference to a non-modifiable `Class` object.
    //! @param arg A reference to a non-modifiable `Class` object.
    //! @return A reference to the same object.
    static auto peek(const virtual_ptr<Class, Registry>& ptr)
        -> const virtual_ptr<Class, Registry>& {
        return ptr;
    }

    //! Cast to another type.
    //!
    //! Cast a `virtual_ptr` to another type, using its `cast` member function.
    //!
    //! @param obj A lvalue reference to a `virtual_ptr`.
    //! @return A lvalue reference to a `virtual_ptr` to the same object, cast
    //! to `Derived::element_type`.
    template<typename Derived>
    static auto cast(const virtual_ptr<Class, Registry>& ptr)
        -> decltype(auto) {
        return ptr.template cast<
            typename std::remove_reference_t<Derived>::element_type>();
    }
};

// =============================================================================
// Method

namespace detail {

template<typename P, typename Q, class Registry>
struct select_overrider_virtual_type_aux {
    using type = void;
};

template<typename P, typename Q, class Registry>
struct select_overrider_virtual_type_aux<virtual_<P>, Q, Registry> {
    using type = virtual_type<Q, Registry>;
};

template<typename P, typename Q, class Registry>
struct select_overrider_virtual_type_aux<
    virtual_ptr<P, Registry>, virtual_ptr<Q, Registry>, Registry> {
    using type = typename virtual_traits<
        virtual_ptr<Q, Registry>, Registry>::virtual_type;
};

template<typename P, typename Q, class Registry>
struct select_overrider_virtual_type_aux<
    const virtual_ptr<P, Registry>&, const virtual_ptr<Q, Registry>&,
    Registry> {
    using type = typename virtual_traits<
        const virtual_ptr<Q, Registry>&, Registry>::virtual_type;
};

template<typename P, typename Q, class Registry>
using select_overrider_virtual_type =
    typename select_overrider_virtual_type_aux<P, Q, Registry>::type;

template<
    typename MethodParameters, typename OverriderParameters, class Registry>
using overrider_virtual_types = boost::mp11::mp_remove<
    boost::mp11::mp_transform_q<
        boost::mp11::mp_bind_back<select_overrider_virtual_type, Registry>,
        MethodParameters, OverriderParameters>,
    void>;

template<class Method, class Rtti, 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) {
        if constexpr (Index == 0u) {
            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);
        } else {
            arg_type_id = Rtti::dynamic_type(arg);
        }

        error.types[Index] = arg_type_id;

        init_bad_call<Method, Rtti, 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> {
    static auto fn(bad_call&) {
    }
};

template<class Registry>
using method_base = std::conditional_t<
    Registry::has_deferred_static_rtti, deferred_method_info, method_info>;

template<typename T, class Registry>
struct parameter_traits {
    static auto peek(const T& value) -> const T& {
        return value;
    }

    template<typename>
    static auto cast(T value) -> T {
        return value;
    }
};

template<typename T, class Registry>
struct parameter_traits<virtual_<T>, Registry> : virtual_traits<T, Registry> {};

template<class Class, class Registry>
struct parameter_traits<virtual_ptr<Class, Registry, void>, Registry> :
    virtual_traits<virtual_ptr<Class, Registry, void>, Registry> {};

template<class Class, class Registry>
struct parameter_traits<const virtual_ptr<Class, Registry, void>&, Registry> :
    virtual_traits<const virtual_ptr<Class, Registry, void>&, Registry> {};

template<typename...>
constexpr bool false_t = false; // workaround before CWG2518/P2593R1

template<typename T, class Registry, typename = void>
struct validate_method_parameter : std::true_type {};

template<typename T, class Registry, typename U>
struct validate_method_parameter<virtual_<T>, Registry, U> : std::false_type {
    static_assert(false_t<T>, "virtual_traits not specialized for type");
};

template<typename T, class Registry>
struct validate_method_parameter<
    virtual_<T>, Registry,
    std::void_t<typename virtual_traits<T, Registry>::virtual_type>> :
    std::bool_constant<
        has_vptr_fn<virtual_type<T, Registry>, Registry> ||
        Registry::rtti::template is_polymorphic<virtual_type<T, Registry>>> {
    static_assert(
        validate_method_parameter::value,
        "virtual_<> parameter is not a polymorphic class and no "
        "boost_openmethod_vptr is applicable");
};

template<class Class, class Registry>
struct validate_method_parameter<virtual_ptr<Class, Registry>, Registry, void> :
    std::true_type {};

template<class Class, class Registry, class MethodRegistry>
struct validate_method_parameter<
    virtual_ptr<Class, Registry>, MethodRegistry, void> : std::false_type {
    static_assert(
        false_t<Class, Registry, MethodRegistry>, "registry mismatch");
};
} // namespace detail

//! Implement a method
//!
//! Methods are created by specializing the `method` class template with an
//! identifier, a function type and optionally a registry.
//!
//! `Id` is a type, typically an incomplete class declaration named after the
//! method's purpose. It is used to allow different methods with the same
//! signature.
//!
//! `Fn` is a function type, i.e. a type in the form `ReturnType(Parameters...)`.
//!
//! `Registry` is an instantiation of class template @ref registry. Methods may
//! use only classes that have been registered in the same registry as virtual
//! parameters and arguments. The registry also contains a set of policies that
//! influence several aspects of the dispatch mechanism - for example, how to
//! acquire a v-table pointer for an object, how to report errors, whether to
//! perform sanity checks, etc.
//!
//! The default value for `Registry` is @ref default_registry, but it can be
//! overridden by defining the preprocessor symbol
//! @ref BOOST_OPENMETHOD_DEFAULT_REGISTRY, *before* including
//! `<boost/openmethod/core.hpp>` (or any header that includes it, like
//! `<boost/openmethod.hpp>`). Setting the symbol afterwards has no effect.
//!
//! Specializations of `method` have a single instance: the static member `fn`,
//! which has an `operator()` that forwards to the appropriate overrider. It is
//! selected in the same way as overloaded function resolution:
//!
//! 1. Form the set of all applicable overriders. An overrider is applicable
//!    if it can be called with the arguments passed to the method.
//!
//! 2. If the set is empty, call the error handler (if present in the
//!    registry), then terminate the program with `abort`.
//!
//! 3. Remove the overriders that are dominated by other overriders in the set.
//!    Overrider A dominates overrider B if at least one of its virtual formal
//!    parameters is more specialized than B's, and if none of B's virtual
//!    parameters is more specialized than A's.
//!
//! 4. If the resulting set contains exactly one overrider, call it.
//!
//! If a single most specialized overrider does not exist, the program is
//! terminated via `abort`. If the registry contains an @ref error_handler
//! policy, its `error` function is called with an object that describes the
//! error, prior calling `abort`. `error` may prevent termination by throwing an
//! exception.
//!
//! For each virtual argument `arg`, the dispatch mechanism calls
//! `virtual_traits::peek(arg)` and deduces the v-table pointer from the
//! `result`, using the first of the following methods that applies:
//!
//! 1. If `result` is a `virtual_ptr`, get the pointer to the v-table from it.
//!
//! 2. If @ref boost_openmethod_vptr can be called with `result` and a
//!    `Registry*`, and it returns a `vptr_type`, call it.
//!
//! 3. If @ref virtual_traits provides a `vptr` function, call it.
//!
//! 4. Call the @ref policies::VptrFn::dynamic_vptr of the registry's `vptr`
//!    policy.
//!
//! @par N2216 Handling of Ambiguous Calls
//!
//! If `Registry` was initialized with the @ref n2216 option, ambiguous calls
//! are not an error. Instead, the following extra steps are taken to select an
//! overrider:
//!
//! 1. If the return type is a registered polymorphic type, remove all the
//!    overriders that return a less specific type than others.
//!
//! 2. If the resulting set contains only one overrider, call it.
//!
//! 3. Otherwise, call one of the remaining overriders. Which overrider is
//!    selected is not specified, but it is the same across calls with the
//!    same arguments types.
//!
//! @tparam Id A type
//! @tparam Fn A function type
//! @tparam Registry The registry in which the method is defined
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
template<
    typename Id, typename Fn,
    class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
class method;

//! Method with a specific id, signature and return type
//!
//! `method` implements an open-method that takes a parameter list -
//! `Parameters` - and returns a `ReturnType`.
//!
//! `Parameters` must contain at least one virtual parameter, i.e. a parameter
//! that has a type in the form `virtual_ptr<T, Registry>` or `virtual\_<T>`.
//! The dynamic types of the virtual arguments are taken into account to select
//! the overrider to call.
//!
//! @see method
//!
//! @tparam Id A type representing the method's name
//! @tparam ReturnType The return type of the method
//! @tparam Parameters The types of the parameters
//! @tparam Registry The registry of the method
template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
class method<Id, ReturnType(Parameters...), Registry> :
    public detail::method_base<Registry> {
    // Deliberately no default for Inline: giving it a default on only one of
    // this template's two forward declarations in this file (the other is
    // below, next to override_impl) is accepted by gcc but rejected by clang
    // ("too few template arguments"), so every use spells out all three
    // arguments explicitly instead.
    template<auto Function, typename FunctionType, bool Inline>
    struct override_aux;

    // Aliases used in implementation only. Everything extracted from template
    // arguments is capitalized like the arguments themselves.
    using RegistryType = Registry;
    using rtti = typename Registry::rtti;
    using DeclaredParameters = mp11::mp_list<Parameters...>;
    using CallParameters =
        boost::mp11::mp_transform<detail::remove_virtual_, DeclaredParameters>;
    using VirtualParameters =
        typename detail::virtual_types<DeclaredParameters>;
    using Signature = auto(Parameters...) -> ReturnType;
    using FunctionPointer = auto (*)(detail::remove_virtual_<Parameters>...)
        -> ReturnType;

  public:
    //! Method singleton
    //!
    //! The only instance of `method`. Its `operator()` is used to call
    //! the method.
    static method fn;

    //! Call the method
    //!
    //! Call the method with `args`. The types of the arguments are the same as
    //! the method `Parameters...`, stripped from any `virtual\_` decorators.
    //!
    //! @param args The arguments for the method call
    //!
    //! @par Errors
    //!
    //! If `Registry` contains an @ref error_handler policy, call its `error`
    //! function with an object of one of the following types:
    //!
    //! @li @ref no_overrider: No overrider is applicable.
    //! @li @ref ambiguous_call: More than one overrider is applicable, and
    //! none is more specialized than all the others.
    //!
    auto operator()(typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
                        StripVirtualDecorator<Parameters>::type... args) const
        -> ReturnType;

    //! Check if a next most specialized overrider exists
    //!
    //! Return `true` if a next most specialized overrider after _Fn_ exists,
    //! and @ref next can be called without causing a @ref bad_call.
    //!
    //! @par Requirements
    //!
    //! `Fn` must be a function that is an overrider of the method.
    //!
    //! @tparam Fn A function that is an overrider of the method.
    //! @return `true` if a next most specialized overrider exists
    template<auto Fn>
    static bool has_next();

    //! The next most specialized overrider
    //!
    //! A pointer to the next most specialized overrider after `Fn`, i.e. the
    //! overrider that would be called for the same tuple of virtual arguments
    //! if `Fn` was not present. Set to `nullptr` if no such overrider exists.
    //! @par Requirements
    //!
    //! `Fn` must be a function that is an overrider of the method.
    //!
    //! @tparam Fn A function that is an overrider of the method.

    // 'next' does not need any special treatment for Windows DLLs, because it
    // may be called only from within the overrider, registered with a registrar
    // in the same module.
    template<auto Fn>
    static FunctionPointer next;

    //! Add overriders to method
    //!
    //! `override`, instantiated as a static object, adds one or more overriders
    //! to an open-method.
    //!
    //! @par Requirements
    //!
    //! `Fn` must be a function that fulfills the following requirements:
    //!
    //! @li Have the same number of formal parameters as the method.
    //!
    //! @li Each @c virtual_ptr<T> in the method's parameter list must have a
    //! corresponding @c virtual_ptr<U> parameter in the same position in the
    //! overrider's parameter list, such that @c U is the same as @c T, or has
    //! @c T as an accessible unambiguous base.
    //!
    //! @li Each @c virtual_<T> in the method's parameter list must have a
    //! corresponding @c U parameter in the same position in the overrider's
    //! parameter list, such that @c U is the same as @c T, or has @c T as an
    //! accessible unambiguous base.
    //!
    //! @li All other formal parameters must have the same type as the method's
    //! corresponding parameters.
    //!
    //! @li The return type of the overrider must be the same as the method's
    //! return type or, if it is a polymorphic type, covariant with the method's
    //! return type.
    //!
    //! @tparam Fn One or more functions to the overrider list
    template<auto... Fn>
    class override {
        boost::mp11::mp_apply<
            detail::tuple,
            boost::mp11::mp_unique<
                boost::mp11::mp_list<override_aux<Fn, decltype(Fn), false>...>>>
            impl;
    };

    // Like override, but marks every overrider_info registered here as
    // inline_ = true (see overrider_info in preamble.hpp), making it
    // eligible for cross-module dedup during augment_methods()
    // consolidation - used by BOOST_OPENMETHOD_INLINE_OVERRIDE. Only an
    // overrider defined `inline` can legally have an identical definition
    // appear in more than one translation unit/module, which is why plain
    // `override` (above) never sets this.
    //
    // inline_ must be baked in as the `Inline` non-type template parameter
    // of override_aux/override_impl (below), not passed as a runtime
    // constructor argument to this class: override_aux::impl is a static
    // template data member whose dynamic initialization is not guaranteed to
    // happen before this class's own constructor body runs (taking its
    // address, `(void)&impl;` in override_aux's constructor, does not force
    // immediate construction) - a runtime assignment made here could run
    // *before* override_impl's constructor, which would then overwrite it
    // via its implicit default-construction of the overrider_info base.
    // Setting it inside override_impl's own constructor, alongside method,
    // pf, etc., is the only point guaranteed to run exactly once, at the
    // right time.
    template<auto... Fn>
    class inline_override {
        boost::mp11::mp_apply<
            detail::tuple,
            boost::mp11::mp_unique<
                boost::mp11::mp_list<override_aux<Fn, decltype(Fn), true>...>>>
            impl;
    };

  private:
    static constexpr auto Arity = boost::mp11::mp_count_if<
        mp11::mp_list<Parameters...>, detail::is_virtual>::value;

    // sanity checks
    static_assert((
        detail::validate_method_parameter<Parameters, Registry>::value && ...));
    static_assert(Arity > 0, "method has no virtual parameters");

    type_id vp_type_ids[Arity];

    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
    // contains a pointer to a function.
    // For multi-methods: the offset of the first virtual argument in the
    // method table, which contains a pointer to the corresponding cell in
    // 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 MethodArg, typename ArgType>
    auto vptr(const ArgType& arg) const -> vptr_type;

    template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
    auto resolve_uni(const ArgType& arg, const MoreArgTypes&... more_args) const
        -> detail::word;

    template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
    auto resolve_multi_first(
        const ArgType& arg,
        const MoreArgTypes&... more_args) const -> detail::word;

    template<
        std::size_t VirtualArg, typename MethodArgList, typename ArgType,
        typename... MoreArgTypes>
    auto resolve_multi_next(
        vptr_type dispatch, const ArgType& arg,
        const MoreArgTypes&... more_args) const -> detail::word;

    template<typename... ArgType>
    FunctionPointer resolve(const ArgType&... args) const;

    template<auto, typename>
    struct thunk;

    template<auto, typename>
    struct thunk;

    method();
    method(const method&) = delete;
    method(method&&) = delete;
    ~method();

    void resolve(); // virtual if Registry contains has_deferred_static_rtti

    static BOOST_NORETURN auto fn_not_implemented(
        detail::remove_virtual_<Parameters>... args) -> ReturnType;
    static BOOST_NORETURN auto fn_ambiguous(
        detail::remove_virtual_<Parameters>... args) -> ReturnType;

    template<
        auto Overrider, typename OverriderReturn,
        typename... OverriderParameters>
    struct thunk<Overrider, OverriderReturn (*)(OverriderParameters...)> {
        static auto fn(detail::remove_virtual_<Parameters>... arg)
            -> ReturnType;
        using OverriderVirtualParameters = detail::overrider_virtual_types<
            DeclaredParameters, mp11::mp_list<OverriderParameters...>,
            Registry>;
    };

    template<auto Function, typename FnReturnType, bool Inline = false>
    struct override_impl :
        std::conditional_t<
            Registry::has_deferred_static_rtti, detail::deferred_overrider_info,
            detail::overrider_info> {
        explicit override_impl(FunctionPointer* next = nullptr);
        void resolve_type_ids();

        static type_id vp_type_ids[Arity];
    };

    template<auto Function, typename FunctionType, bool Inline>
    struct override_aux;

    template<
        auto Function, typename FnReturnType, typename... FnParameters,
        bool Inline>
    struct override_aux<Function, FnReturnType (*)(FnParameters...), Inline> {
        override_aux() {
            (void)&impl;
        }

        static override_impl<Function, FnReturnType, Inline> impl;
    };
};

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<auto Fn>
typename method<Id, ReturnType(Parameters...), Registry>::FunctionPointer
    method<Id, ReturnType(Parameters...), Registry>::next;

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
method<Id, ReturnType(Parameters...), Registry>
    method<Id, ReturnType(Parameters...), Registry>::fn;

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<auto Function, typename FnReturnType, bool Inline>
type_id method<Id, ReturnType(Parameters...), Registry>::override_impl<
    Function, FnReturnType, Inline>::vp_type_ids[Arity];

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<
    auto Function, typename FnReturnType, typename... FnParameters, bool Inline>
typename method<Id, ReturnType(Parameters...), Registry>::
    template override_impl<Function, FnReturnType, Inline>
        method<Id, ReturnType(Parameters...), Registry>::override_aux<
            Function, FnReturnType (*)(FnParameters...), Inline>::impl;

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
method<Id, ReturnType(Parameters...), Registry>::method() {
    using namespace policies;

    this->slots_strides_ptr = slots_strides;

    if constexpr (!Registry::has_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 (*)()>(fn_not_implemented);
    this->ambiguous = reinterpret_cast<void (*)()>(fn_ambiguous);

    // zero-initalized static variable
    // coverity[uninit_use]
    Registry::static_::st.methods.push_back(*this);
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
void method<Id, ReturnType(Parameters...), Registry>::resolve_type_ids() {
    using namespace detail;
    this->method_type_id = rtti::template static_type<method>();
    this->return_type_id =
        rtti::template static_type<virtual_type<ReturnType, Registry>>();
    init_type_ids<
        Registry,
        mp11::mp_transform_q<
            mp11::mp_bind_back<virtual_type, Registry>,
            VirtualParameters>>::fn(this->vp_type_ids);
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
method<Id, ReturnType(Parameters...), Registry>::~method() {
    Registry::static_::st.methods.remove(*this);
}

// -----------------------------------------------------------------------------
// method dispatch

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
BOOST_FORCEINLINE auto
method<Id, ReturnType(Parameters...), Registry>::operator()(
    typename BOOST_OPENMETHOD_UNLESS_MRDOCS(detail::)
        StripVirtualDecorator<Parameters>::type... args) const -> ReturnType {
    using namespace detail;
    auto pf = resolve(args...);

    return pf(std::forward<typename StripVirtualDecorator<Parameters>::type>(
        args)...);
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<typename... ArgType>
BOOST_FORCEINLINE
    typename method<Id, ReturnType(Parameters...), Registry>::FunctionPointer
    method<Id, ReturnType(Parameters...), Registry>::resolve(
        const ArgType&... args) const {
    using namespace detail;

    Registry::require_initialized();

    void (*pf)();

    if constexpr (Arity == 1) {
        pf = resolve_uni<mp11::mp_list<Parameters...>, ArgType...>(args...).pf;
    } else {
        pf = resolve_multi_first<mp11::mp_list<Parameters...>, ArgType...>(
                 args...)
                 .pf;
    }

    return reinterpret_cast<FunctionPointer>(pf);
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<typename MethodArg, typename ArgType>
BOOST_FORCEINLINE auto method<Id, ReturnType(Parameters...), Registry>::vptr(
    const ArgType& arg) const -> vptr_type {
    if constexpr (detail::is_virtual_ptr<ArgType>) {
        return arg.vptr();
    } else {
        decltype(auto) obj = virtual_traits<MethodArg, Registry>::peek(arg);

        if constexpr (detail::has_vptr_fn<decltype(obj), Registry>) {
            return boost_openmethod_vptr(obj, static_cast<Registry*>(nullptr));
        } else if constexpr (detail::has_vptr<
                                 virtual_traits<MethodArg, Registry>,
                                 decltype(obj)>) {
            return virtual_traits<MethodArg, Registry>::vptr(obj);
        } else {
            return Registry::template policy<policies::vptr>::dynamic_vptr(obj);
        }
    }
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
BOOST_FORCEINLINE auto
method<Id, ReturnType(Parameters...), Registry>::resolve_uni(
    const ArgType& arg,
    const MoreArgTypes&... more_args) const -> detail::word {

    using namespace detail;
    using namespace policies;
    using namespace boost::mp11;

    if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
        vptr_type vtbl = vptr<remove_virtual_<mp_first<MethodArgList>>>(arg);
        return vtbl[this->slots_strides[0]];
    } else {
        return resolve_uni<mp_rest<MethodArgList>>(more_args...);
    }
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
BOOST_FORCEINLINE auto
method<Id, ReturnType(Parameters...), Registry>::resolve_multi_first(
    const ArgType& arg,
    const MoreArgTypes&... more_args) const -> detail::word {

    using namespace detail;
    using namespace boost::mp11;

    if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
        vptr_type vtbl = vptr<remove_virtual_<mp_first<MethodArgList>>>(arg);
        std::size_t slot = this->slots_strides[0];

        // The first virtual parameter is special.  Since its stride is
        // 1, there is no need to store it. Also, the method table
        // contains a pointer into the multi-dimensional dispatch table,
        // already resolved to the appropriate group.
        auto dispatch = vtbl[slot].pw;
        return resolve_multi_next<1, mp_rest<MethodArgList>, MoreArgTypes...>(
            dispatch, more_args...);
    } else {
        return resolve_multi_first<mp_rest<MethodArgList>, MoreArgTypes...>(
            more_args...);
    }
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<
    std::size_t VirtualArg, typename MethodArgList, typename ArgType,
    typename... MoreArgTypes>
BOOST_FORCEINLINE auto
method<Id, ReturnType(Parameters...), Registry>::resolve_multi_next(
    vptr_type dispatch, const ArgType& arg,
    const MoreArgTypes&... more_args) const -> detail::word {

    using namespace detail;
    using namespace boost::mp11;

    if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
        vptr_type vtbl = vptr<remove_virtual_<mp_first<MethodArgList>>>(arg);
        std::size_t slot = this->slots_strides[VirtualArg];
        std::size_t stride = this->slots_strides[Arity + VirtualArg - 1];
        dispatch = dispatch + vtbl[slot].i * stride;
    }

    if constexpr (VirtualArg + 1 == Arity) {
        return *dispatch;
    } else {
        return resolve_multi_next<
            VirtualArg + 1, mp_rest<MethodArgList>, MoreArgTypes...>(
            dispatch, more_args...);
    }
}

// -----------------------------------------------------------------------------
// Error handling

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<auto Fn>
inline auto method<Id, ReturnType(Parameters...), Registry>::has_next()
    -> bool {
    if (next<Fn> == fn_not_implemented) {
        return false;
    }

    if (next<Fn> == fn_ambiguous) {
        return false;
    }

    return true;
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
BOOST_NORETURN auto
method<Id, ReturnType(Parameters...), Registry>::fn_not_implemented(
    detail::remove_virtual_<Parameters>... args) -> ReturnType {
    using namespace policies;

    if constexpr (Registry::has_error_handler) {
        no_overrider error;
        detail::init_bad_call<method, rtti, 0u>::fn(
            error,
            detail::parameter_traits<Parameters, Registry>::peek(args)...);
        Registry::error_handler::error(error);
    }

    abort(); // in case user handler "forgets" to abort
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
BOOST_NORETURN auto
method<Id, ReturnType(Parameters...), Registry>::fn_ambiguous(
    detail::remove_virtual_<Parameters>... args) -> ReturnType {
    using namespace policies;

    if constexpr (Registry::has_error_handler) {
        ambiguous_call error;
        detail::init_bad_call<method, rtti, 0u>::fn(
            error,
            detail::parameter_traits<Parameters, Registry>::peek(args)...);
        Registry::error_handler::error(error);
    }

    abort(); // in case user handler "forgets" to abort
}

// -----------------------------------------------------------------------------
// overriders

namespace detail {

template<typename T, typename U>
struct same_reference_category {
    static constexpr bool value = (std::is_lvalue_reference<T>::value ==
                                   std::is_lvalue_reference<U>::value) &&
        (std::is_rvalue_reference<T>::value ==
         std::is_rvalue_reference<U>::value);
};
template<class T1, class T2, typename = void>
struct validate_overrider_parameter : std::false_type {
    static_assert(
        false_t<T1, T2>, "non-virtual parameter types must match exactly");
};

template<class T1, class T2>
struct validate_overrider_parameter<
    T1, T2,
    std::enable_if_t<
        is_virtual_ptr<T1> && is_virtual_ptr<T2> &&
        !same_reference_category<T1, T2>::value>> : std::false_type {
    static_assert(
        false_t<T1, T2>, "different virtual_ptr<> reference categories");
};

template<class T1, class T2>
struct validate_overrider_parameter<
    T1, T2, std::enable_if_t<is_virtual_ptr<T1> && !is_virtual_ptr<T2>>> :
    std::false_type {
    static_assert(
        false_t<T1, T2>,
        "virtual_ptr<> is required in overrider in same position as in "
        "method");
};

template<class T>
struct validate_overrider_parameter<T, T, void> : std::true_type {};

template<class T1, class T2>
struct validate_overrider_parameter<virtual_<T1>, T2, void> : std::true_type {};

template<class T1, class T2>
struct validate_overrider_parameter<virtual_<T1>, virtual_<T2>, void> :
    std::false_type {
    static_assert(false_t<T1, T2>, "virtual_<> is not allowed in overriders");
};

template<class T, class R>
struct validate_overrider_parameter<
    virtual_ptr<T, R>, virtual_ptr<T, R>, void> : std::true_type {};

template<class T1, class R, class T2, class R2>
struct validate_overrider_parameter<
    virtual_ptr<T1, R>, virtual_ptr<T2, R2>, void> : std::true_type {
    static_assert(std::is_same_v<R, R2>, "registry mismatch");
    using C1 = virtual_type<virtual_ptr<T1, R>, R>;
    using C2 = virtual_type<virtual_ptr<T2, R>, R>;
    static_assert(
        std::is_base_of_v<C1, C2> &&
            std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
        "method parameter must be an unambiguous accessible base "
        "of corresponding overrider parameter");
};

template<class T1, class R, class T2, class R2>
struct validate_overrider_parameter<
    const virtual_ptr<T1, R>&, const virtual_ptr<T2, R2>&, void> :
    std::true_type {
    static_assert(std::is_same_v<R, R2>, "registry mismatch");
    using C1 = virtual_type<const virtual_ptr<T1, R>&, R>;
    using C2 = virtual_type<const virtual_ptr<T2, R>&, R>;
    static_assert(
        std::is_base_of_v<C1, C2> &&
            std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
        "method parameter must be an unambiguous accessible base "
        "of corresponding overrider parameter");
};

template<class T1, class R, class T2, class R2>
struct validate_overrider_parameter<
    virtual_ptr<T1, R>&&, virtual_ptr<T2, R2>&&, void> : std::true_type {
    static_assert(std::is_same_v<R, R2>, "registry mismatch");
    using C1 = virtual_type<virtual_ptr<T1, R>&&, R>;
    using C2 = virtual_type<virtual_ptr<T2, R>&&, R>;
    static_assert(
        std::is_base_of_v<C1, C2> &&
            std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
        "method parameter must be an unambiguous accessible base "
        "of corresponding overrider parameter");
};

} // namespace detail

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<
    auto Overrider, typename OverriderReturn, typename... OverriderParameters>
auto method<Id, ReturnType(Parameters...), Registry>::
    thunk<Overrider, OverriderReturn (*)(OverriderParameters...)>::fn(
        detail::remove_virtual_<Parameters>... arg) -> ReturnType {
    using namespace detail;
    static_assert(
        (validate_overrider_parameter<Parameters, OverriderParameters>::value &&
         ...),
        "virtual_ptr category mismatch");
    return Overrider(
        detail::parameter_traits<Parameters, Registry>::template cast<
            OverriderParameters>(
            std::forward<detail::remove_virtual_<Parameters>>(arg))...);
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<auto Function, typename FnReturnType, bool Inline>
method<Id, ReturnType(Parameters...), Registry>::override_impl<
    Function, FnReturnType, Inline>::override_impl(FunctionPointer* p_next) {
    using namespace detail;

    // static variable this->method below is zero-initialized but gcc and clang
    // don't always see that.

#ifdef BOOST_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
#endif

#ifdef BOOST_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wuninitialized"
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif

    // zero-initalized static variable
    // coverity[uninit_use]
    if (overrider_info::method) {
        BOOST_ASSERT(overrider_info::method == &method::fn);
        return;
    }

#ifdef BOOST_CLANG
#pragma clang diagnostic pop
#endif

#ifdef BOOST_GCC
#pragma GCC diagnostic pop
#endif

    overrider_info::method = &method::fn;
    // Baked in as a template parameter (not a runtime constructor argument):
    // this static object's dynamic initialization is not guaranteed to
    // happen at any particular point relative to other code (see class
    // override/inline_override in this file), so Inline must be known here,
    // at the one place this object's fields are set exactly once.
    overrider_info::inline_ = Inline;

    if constexpr (!Registry::has_deferred_static_rtti) {
        resolve_type_ids();
    }

    this->next = reinterpret_cast<void (**)()>(
        p_next ? p_next : &method::next<Function>);

    using Thunk = thunk<Function, decltype(Function)>;
    this->pf = reinterpret_cast<void (*)()>(Thunk::fn);

    this->vp_begin = vp_type_ids;
    this->vp_end = vp_type_ids + Arity;

    method::fn.overriders.push_back(*this);
}

template<
    typename Id, typename... Parameters, typename ReturnType, class Registry>
template<auto Function, typename FnReturnType, bool Inline>
void method<Id, ReturnType(Parameters...), Registry>::override_impl<
    Function, FnReturnType, Inline>::resolve_type_ids() {
    using namespace detail;

    this->return_type = Registry::rtti::template static_type<
        virtual_type<FnReturnType, Registry>>();
    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);
}

// =============================================================================
// register_classes

namespace detail {

#if BOOST_OPENMETHOD_HAS_REFLECTION

template<class Method>
struct method_traits_aux;

template<
    typename Id, typename ReturnType, typename... Parameters, class Registry>
struct method_traits_aux<method<Id, ReturnType(Parameters...), Registry>> {
    // The classes the method dispatches on, plus its return type, which is
    // registered too when it is covariant. Same expression as
    // `method::resolve_type_ids`.
    using type = mp11::mp_push_back<
        mp11::mp_transform_q<
            mp11::mp_bind_back<virtual_type, Registry>,
            virtual_types<mp11::mp_list<Parameters...>>>,
        virtual_type<ReturnType, Registry>>;
};

// Read from reflection, by `substitute`-ing a method into it and taking the
// template arguments of the result.
template<class Method>
using method_classes = typename method_traits_aux<Method>::type;

// How `register_classes` interprets an argument group, and the items in it.
enum class register_classes_kind : unsigned char {
    invalid,    // not a reflection register_classes accepts, or a mixed group
    empty,      // `{}` - carries no kind, and takes no part in the ordering
    namespaces, // namespaces to scan
    classes,    // classes to register
    registries, // registries to add the classes to
};

consteval auto is_registry_type(std::meta::info type) -> bool {
    return std::meta::extract<bool>(std::meta::substitute(
        ^^is_registry,
        {
            type}));
}

// How `register_classes` interprets one item of one of its argument groups.
consteval auto register_classes_item_kind(std::meta::info item)
    -> register_classes_kind {
    auto entity = std::meta::dealias(item);

    if (std::meta::is_namespace(entity)) {
        return register_classes_kind::namespaces;
    }

    if (std::meta::is_class_type(entity)) {
        // A registry is always complete where classes are registered in it, so
        // an incomplete class cannot be one.
        if (std::meta::is_complete_type(entity) && is_registry_type(entity)) {
            return register_classes_kind::registries;
        }

        return register_classes_kind::classes;
    }

    return register_classes_kind::invalid;
}

// True if the group holds an item that is not a reflection `register_classes`
// accepts. The two checks below let such a group pass, so a mistake yields one
// diagnostic instead of three.
template<auto Group>
consteval auto register_classes_group_has_unknown() -> bool {
    for (auto index = 0u; index != Group.size; ++index) {
        if (register_classes_item_kind(Group.items[index]) ==
            register_classes_kind::invalid) {
            return true;
        }
    }

    return false;
}

// The kind of the items in `Group`; `invalid` if they are not all of the same
// kind, or if one of them is not a reflection `register_classes` accepts.
template<auto Group>
consteval auto register_classes_group_kind() -> register_classes_kind {
    auto kind = register_classes_kind::empty;

    for (auto index = 0u; index != Group.size; ++index) {
        auto item = register_classes_item_kind(Group.items[index]);

        if (item == register_classes_kind::invalid) {
            return register_classes_kind::invalid;
        }

        if (kind != register_classes_kind::empty && item != kind) {
            return register_classes_kind::invalid;
        }

        kind = item;
    }

    return kind;
}

template<auto... Groups>
consteval auto register_classes_groups_are_valid() -> bool {
    return (... && !register_classes_group_has_unknown<Groups>());
}

template<auto... Groups>
consteval auto register_classes_groups_are_homogeneous() -> bool {
    return (
        ... &&
        (register_classes_group_has_unknown<Groups>() ||
         register_classes_group_kind<Groups>() !=
             register_classes_kind::invalid));
}

// The groups must come in the order the `register_classes_kind` enumerators
// are declared in: namespaces, classes, registries.
template<auto... Groups>
consteval auto register_classes_groups_are_ordered() -> bool {
    // `register_classes<>` registers the classes of a scan of the global
    // namespace, and is the shape `BOOST_OPENMETHOD_REGISTER_CLASSES()`
    // expands to. It must be taken before the array below is formed: with no
    // group, that array has size zero, which is not standard C++ - GCC rejects
    // it outright under -Wpedantic, clang under -pedantic-errors.
    if constexpr (sizeof...(Groups) == 0) {
        return true;
    } else {
        register_classes_kind kinds[] = {
            register_classes_group_kind<Groups>()...};
        auto last = register_classes_kind::empty;

        for (auto kind : kinds) {
            // An empty group carries no kind, and a group the checks above
            // rejected has no meaningful one either.
            if (kind == register_classes_kind::empty ||
                kind == register_classes_kind::invalid) {
                continue;
            }

            if (kind < last) {
                return false;
            }

            last = kind;
        }

        return true;
    }
}

// The items of every group whose kind is `Kind`, in order, without repeats.
template<auto... Groups>
consteval auto register_classes_items(register_classes_kind kind)
    -> std::vector<std::meta::info> {
    std::vector<std::meta::info> items;

    // `Groups` is empty for `register_classes<>`, which names nothing and
    // scans the global namespace. The fold below then expands to nothing and
    // never reads `kind`, which GCC reports under -Wunused-but-set-parameter.
    static_cast<void>(kind);

    (..., [&] {
        for (auto index = 0u; index != Groups.size; ++index) {
            auto item = Groups.items[index];

            if (register_classes_item_kind(item) == kind) {
                push_unique(items, std::meta::dealias(item));
            }
        }
    }());

    return items;
}

template<auto... Groups>
consteval auto register_classes_registries_info() -> std::meta::info {
    return std::meta::substitute(
        ^^mp11::mp_list,
        register_classes_items<Groups...>(register_classes_kind::registries));
}

// `mp_list<Registry...>` for the registries the groups name.
//
// clang-format off: the formatter predates P2996 and eats the spaces around
// the splice, leaving `typename[:...:]`.
template<auto... Groups>
using register_classes_registries =
    typename [: register_classes_registries_info<Groups...>() :];
// clang-format on

// The classes to register for the argument groups `Groups`, each with its
// direct bases: the classes the methods of `Registry` dispatch on, the classes
// the groups list, and the ones a scan of the listed namespaces found that
// derive from them. Returns `mp_list<mp_list<Class, Class, Base...>, ...>` - the
// shape `use_class_aux` expects, with the class repeated as its own improper
// base, as `inheritance_map` produces.
//
// The work is done here, in reflection, and not with `mp11` over the lists the
// scan produces. A scan of the global namespace reaches every class in the
// program that is not in `std` or `boost`, and instantiating a trait once per
// pair of them costs far more than walking their base classes does.
template<class Registry, auto... Groups>
consteval auto reflected_registered_classes_info() -> std::meta::info {
    // Partition the groups. Registries take no part here: the caller calls
    // this function once per registry.
    auto namespaces =
        register_classes_items<Groups...>(register_classes_kind::namespaces);
    auto virtual_classes =
        register_classes_items<Groups...>(register_classes_kind::classes);

    for (auto& type : virtual_classes) {
        type = std::meta::remove_cv(type);
    }

    // With no namespace to start from, scan the global namespace. Listing
    // classes does not change that: they are extra roots for the scan, not a
    // way to turn it off.
    if (namespaces.empty()) {
        namespaces.push_back(^^::);
    }

    std::vector<std::meta::info> methods, classes;

    for (auto ns : namespaces) {
        scan_scope(ns, ^^method, methods, classes);
    }

    // Add the classes the methods dispatch on.

    for (auto found : methods) {
        // A method's third template argument is its registry.
        if (std::meta::template_arguments_of(found)[2] != ^^Registry) {
            continue;
        }

        auto list = std::meta::dealias(std::meta::substitute(
            ^^method_classes,
            {
                found}));

        for (auto type : std::meta::template_arguments_of(list)) {
            // A method's return type is `void` unless it is covariant, and a
            // virtual parameter may be a smart pointer rather than a class.
            if (std::meta::is_class_type(type)) {
                push_unique(virtual_classes, std::meta::remove_cv(type));
            }
        }
    }

    // Those, plus every class the scan found that derives from one of them. A
    // base class no method dispatches on is left out: no overrider could ever
    // be selected on it, and it would cost a lattice node, a hash slot and
    // dispatch table space.
    auto registered = virtual_classes;

    for (auto found : classes) {
        std::vector<std::meta::info> bases;
        collect_dispatchable_bases(found, bases);

        for (auto base : bases) {
            if (contains(virtual_classes, base)) {
                push_unique(registered, found);
                break;
            }
        }
    }

    // Which registered class inherits from which, as a square matrix indexed
    // by position in `registered`. Walking the base classes once per class and
    // answering from the matrix afterwards keeps this within the compiler's
    // budget for constant evaluation: the alternative, re-searching a class'
    // bases for every pair, is cubic in the number of classes times the depth
    // of the hierarchy, and exceeds GCC's default -fconstexpr-ops-limit on a
    // chain of a few dozen.
    auto count = registered.size();
    std::vector<char> inherits(count * count, char(0));

    for (auto index = 0u; index != count; ++index) {
        std::vector<std::meta::info> bases;
        collect_dispatchable_bases(registered[index], bases);

        for (auto base : bases) {
            if (base == registered[index]) {
                continue;
            }

            for (auto other = 0u; other != count; ++other) {
                if (registered[other] == base) {
                    inherits[index * count + other] = char(1);
                    break;
                }
            }
        }
    }

    std::vector<std::meta::info> entries;

    for (auto index = 0u; index != count; ++index) {
        std::vector<std::meta::info> entry;
        entry.push_back(registered[index]);
        // The class as its own improper base, as `inheritance_map` does.
        // `initialize` discards it, and `use_class_aux` cannot hold an empty
        // base array.
        entry.push_back(registered[index]);

        for (auto base = 0u; base != count; ++base) {
            if (!inherits[index * count + base]) {
                continue;
            }

            // Keep only the nearest ancestors - the direct bases of this class
            // in the lattice the registry will hold. One that another ancestor
            // also inherits from is reached through that one, and recording it
            // as well would make `initialize` see an edge that is not there.
            // Unregistered classes in between are skipped over, which is what
            // flattens the lattice down to the classes that dispatch.
            bool hidden = false;

            for (auto between = 0u; between != count; ++between) {
                if (between != base && inherits[index * count + between] &&
                    inherits[between * count + base]) {
                    hidden = true;
                    break;
                }
            }

            if (!hidden) {
                entry.push_back(registered[base]);
            }
        }

        entries.push_back(std::meta::substitute(^^mp11::mp_list, entry));
    }

    return std::meta::substitute(^^mp11::mp_list, entries);
}

// `mp_list<mp_list<Class, Class, Base...>, ...>`, ready for `use_class_aux`.
//
// clang-format off: the formatter predates P2996 and eats the spaces around the
// splice, leaving `typename[:...:]`.
template<class Registry, auto... Groups>
using reflected_registered_classes =
    typename [: reflected_registered_classes_info<Registry, Groups...>() :];
// clang-format on

// Register the classes the groups select in one registry - unless it opted out
// of reflection-based registration, in which case the scan does not even run.
template<class Registry, auto... Groups>
BOOST_FORCEINLINE auto use_reflected_classes_in() -> void {
    if constexpr (Registry::has_reflected_class_registration) {
        using registered = reflected_registered_classes<Registry, Groups...>;
        use_reflected_classes<Registry>(static_cast<registered*>(nullptr));
    }
}

#endif

} // namespace detail

#if BOOST_OPENMETHOD_HAS_REFLECTION

// MrDocs' front-end does not implement P2996, so it never sees this branch.
// The reference documentation for `register_classes` is on the stub in the
// `#elif` branch below; keep the two in step.

//! @see @ref register_classes for documentation.
template<detail::reflection_group... Groups>
class register_classes {
    static_assert(
        detail::register_classes_groups_are_valid<Groups...>(),
        "a group holds reflections of namespaces, classes or registries");
    static_assert(
        detail::register_classes_groups_are_homogeneous<Groups...>(),
        "a group holds one kind of reflection; put the namespaces, the "
        "classes and the registries in groups of their own");
    static_assert(
        detail::register_classes_groups_are_ordered<Groups...>(),
        "order the groups as namespaces, classes, registries");

    using found_registries = detail::register_classes_registries<Groups...>;
    using registries = mp11::mp_if<
        mp11::mp_empty<found_registries>,
        mp11::mp_list<BOOST_OPENMETHOD_DEFAULT_REGISTRY>, found_registries>;

    template<class... Registries>
    static auto use(mp11::mp_list<Registries...>*) -> void {
        (..., detail::use_reflected_classes_in<Registries, Groups...>());
    }

  public:
    register_classes() {
        use(static_cast<registries*>(nullptr));
    }
};

#elif defined(__MRDOCS__)

// Documentation stub. MrDocs compiles this branch instead of the real
// declaration above, which its front-end cannot parse. It names no `std::meta`
// type, so nothing has to stand in for one.

//! Find the classes taking part in dispatch by reflection, and register them
//! (C++26 and above).
//!
//! `register_classes` is a registrar class that finds the classes taking part
//! in dispatch by reflection, and adds them to one or more registries. It makes
//! @ref use_classes unnecessary in most cases.
//!
//! The arguments are groups of reflections, each written in braces - or, for a
//! group of one, as the reflection itself. A group holds one kind of
//! reflection, and the groups come in this order; each one is optional, and
//! omitting all of them is the common case:
//!
//! @li **Namespaces** to scan: `{^^app, ^^zoo}`, `{\^^::}`.
//! @li **Classes** to register: `{^^Animal}`. They are registered whether a
//! method dispatches on them or not, and they are extra roots for the scan,
//! which registers the classes it finds deriving from them.
//! @li **Registries** to register the classes in: `{^^my_registry}`. Each one
//! receives the registration. The default is
//! `BOOST_OPENMETHOD_DEFAULT_REGISTRY`.
//!
//! @code
//! register_classes<^^app, ^^my_registry> register_classes<{^^app, ^^zoo},
//! {^^r1, ^^r2}> register_classes<{^^Animal, ^^Dog}>
//! register_classes<^^my_registry> register_classes<>
//! @endcode
//!
//! A group holding two kinds of reflection is an error, and so is a group out
//! of order. Braces around a single reflection change nothing:
//! `register_classes<^^app>` and `register_classes<{^^app}>` are the same
//! registration.
//!
//! The scan covers the listed namespaces, the namespaces nested in them, and
//! the classes nested in the classes it finds, except `std` and `boost`:
//! walking those would cost a great deal and find nothing, as a method cannot
//! be declared on a class the program has never heard of. The exclusion applies
//! to recursion only, so a class in `std` or `boost` is registered by *listing*
//! the namespace it is in. A class is also found through an alias that names
//! it, but the scan does not walk *into* an alias: only a class the scanned
//! scope declares is descended into. The scan finds the methods of each target
//! registry, collects the classes they dispatch on, and registers those, the
//! listed classes, and every class in the scanned namespaces that derives from
//! one of them. A base class that no method dispatches on, and that is not
//! listed, is not registered: it could never be selected on.
//!
//! If no namespace is given, the global namespace is scanned - whatever the
//! other groups hold, and for @ref BOOST_OPENMETHOD_REGISTER_CLASSES too. Name
//! a namespace to scan only that one.
//!
//! Reflection sees only what precedes it, so `register_classes` must come
//! **after** the declarations it is meant to find - at the bottom of the file.
//!
//! A method is found through any namespace member that names its `method`
//! specialization: the alias @ref BOOST_OPENMETHOD declares for it, a `using`
//! declaration written by hand, or any of its registrar objects - the object
//! @ref BOOST_OPENMETHOD_OVERRIDE creates, or one written by hand. None of
//! those requires the method to have an overrider. A core interface method
//! whose `method<...>` type is spelled out in full at every use, with neither a
//! `using` declaration nor an overrider, is named by nothing and is not found;
//! its classes must be registered with @ref use_classes.
//!
//! Virtual and multiple inheritance are supported. Unlike @ref use_classes,
//! which rejects it, repeated inheritance is not an error here: a base a class
//! inherits more than once cannot be converted to, so it cannot take part in
//! that class' dispatch, and it is left out of its bases. A class left with no
//! registered base is not registered at all.
//!
//! This class template is available only if the compiler supports C++26
//! reflection, i.e. if `BOOST_OPENMETHOD_HAS_REFLECTION` is 1.
//!
//! @tparam Groups Braced groups of reflections: namespaces, classes,
//! registries, in that order.
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
template<auto... Groups>
class register_classes {
  public:
    //! Register the selected classes in each target registry.
    register_classes();
};

#endif

//! Aliases for the most frequently used types in the library.
namespace aliases {

using boost::openmethod::final_virtual_ptr;
using boost::openmethod::virtual_;
using boost::openmethod::virtual_ptr;

} // namespace aliases

// ==============================================================================
// Exposition only

#ifdef __MRDOCS__

//! Blueprint for a specialization of @ref virtual_traits (exposition only).
//!
//! Specializations of @ref virtual_traits must implement the members listed
//! here.
//!
//! @tparam T The type of a virtual parameter of a method.
//! @tparam Registry A @ref registry.
template<typename T, class Registry>
struct VirtualTraits {
    //! Class to use for dispatch.
    //!
    //! Aliases to the class to be considered during method dispatch to determine
    //! which overrider to select, and which type_id to use for error reporting.
    //! `virtual_traits<T>::virtual_type` aliases to `Class` if `T` is `Class&`,
    //! `const Class&`, `Class*`, `const Class*`, `virtual_ptr<Class>`,
    //! `virtual_ptr<const Class>`, `std::shared_ptr<Class>`,
    //! `std::shared_ptr<const Class>`, `virtual_ptr<std::shared_ptr<Class>>`,
    //! etc.
    //!
    //! @par Requirements
    //!
    //! `virtual_type` must be an alias to an *unadorned* *class* type, *not*
    //! cv-qualified.
    using virtual_type = detail::unspecified;

    //! Returns a reference to the object to use for dispatch.
    //!
    //! Return a reference to the object to use for dispatch. `arg` may not be
    //! copied, moved or altered in any way.
    //!
    //! @param arg An argument passed to the method call.
    //! @return A reference to an object.
    static auto peek(T arg) -> const virtual_type&;

    // Added by the `std::any` interop, under the name `type_vptr`. An `any`
    // dispatches on the type of the value it contains, which the rtti policy
    // cannot see: `dynamic_type` on the `any` itself yields the wrapper.

    //! Returns a *reference* to the v-table pointer for an object.
    //!
    //! `vptr` is optional. It is called on the object returned by @ref peek,
    //! not on the method argument itself. A method acquires the v-table
    //! pointer of a virtual argument from the first of the following that is
    //! available: a `boost_openmethod_vptr` function, found by ADL on the
    //! peeked object; `vptr`; @ref policies::VptrFn::dynamic_vptr of the
    //! registry's @ref policies::vptr policy.
    //!
    //! Implement `vptr` only if the v-table pointer cannot be obtained from
    //! the dynamic type of the peeked object, as reported by the registry's
    //! @ref policies::rtti policy, or if it is already at hand. The former is
    //! the case for `any`-like types: their dynamic type is the wrapper, not
    //! the value they contain. The `std::any` specializations read the
    //! @ref type_id of the contained value from `arg.type()`, and pass it to
    //! @ref policies::VptrFn::vptr. The latter is the case for a wide type
    //! that caches the v-table pointer: @ref virtual_any returns the one it
    //! acquired when it was created, without a lookup.
    //!
    //! `vptr` must return a *reference*, not a value, so that the caller
    //! observes the current v-table pointer if the registry contains the
    //! @ref policies::indirect_vptr policy and `initialize` is called again.
    //!
    //! @param arg The object returned by @ref peek.
    //! @return A reference to the v-table pointer for `arg`.
    static auto vptr(const virtual_type& arg) -> const vptr_type&;

    //! Casts a virtual argument.
    //!
    //! `cast` is responsible for passing virtual arguments from method to
    //! overrider. In general, this requires some form of adjustment, because a
    //! virtual parameter in the overrider usually has a different type than the
    //! corresponding parameter in the method. Typically, the adjustment
    //! consists of a cast, performed via `static_cast`, `dynamic_cast`, or
    //! other means, depending on the type of the argument and the rtti policy
    //! of the method. `cast` may return the adjusted argument by reference or
    //! as a temporary value.
    //!
    //! @tparam T The type of the virtual parameter in the method.
    //! @tparam U The type of the virtual parameter in the overrider.
    //! @param arg The argument passed to the method call.
    //! @return A value that can be passed as a U.
    template<typename U>
    static auto cast(T arg) -> detail::unspecified;

    //! Rebind to a another class (smart pointers only).
    //!
    //! If `T` is a smart pointer, `rebind<U>` is the same kind of smart
    //! pointer, but pointing to a `U`.
    //!
    //! @note `rebind` must be implemented @em only for smart pointer classes
    //! that can be used as object pointers by @ref virtual_ptr in place of
    //! plain pointers.
    //!
    //! @tparam U The new element type.
    template<class U>
    using rebind = detail::unspecified;
};

#endif

} // namespace boost::openmethod

#ifdef _MSC_VER
#pragma warning(pop)
#endif

#endif


// 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_MACROS_HPP
#define BOOST_OPENMETHOD_MACROS_HPP

#include <boost/preprocessor/cat.hpp>




namespace boost::openmethod::detail {

template<typename, class Method, typename ReturnType, typename... Parameters>
struct enable_forwarder;

template<class Method, typename ReturnType, typename... Parameters>
struct enable_forwarder<
    std::void_t<decltype(Method::fn(std::declval<Parameters>()...))>, Method,
    ReturnType, Parameters...> {
    using type = ReturnType;
};

template<class...>
struct va_args;

template<class ReturnType>
struct va_args<ReturnType> {
    using return_type = ReturnType;
    using registry = macro_default_registry;
};

template<class ReturnType, class Registry>
struct va_args<ReturnType, Registry> {
    using return_type = ReturnType;
    using registry = Registry;
};

template<typename...>
inline constexpr bool method_not_found = false;

} // namespace boost::openmethod::detail

#define BOOST_OPENMETHOD_GENSYM BOOST_PP_CAT(openmethod_gensym_, __COUNTER__)

//! Create a registrar object.
//!
//! Creates a registrar for a type, i.e. a static object of that type with a
//! unique generated name. At static initialization time, the object adds
//! itself to a list: methods and class registrations add themselves to a
//! @ref boost::openmethod::registry, and overriders add themselves to a
//! method's overrider list.
//!
//! @param ... The registrar's type. It is variadic so that it may contain
//! unparenthesized commas, as in `std::pair<int, int>`.
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
#define BOOST_OPENMETHOD_REGISTER(...)                                         \
    static __VA_ARGS__ BOOST_OPENMETHOD_GENSYM

//! Generate a method id.
//!
//! Generates a long, obfuscated name from a short name. All the other names
//! generated by macros are based on this name.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @param ID The method's name.
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
#define BOOST_OPENMETHOD_ID(ID) ID##_boost_openmethod

//! Return the class template containing the overriders for all the methods
//! with a given name.
//!
//! `BOOST_OPENMETHOD_OVERRIDERS` expands to the name of the class template that
//! contains the overriders for all the methods with a given name.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @param ID The method's name.
//!
//! @see [Header and Implementation Files](xref:ROOT:headers.adoc)
#define BOOST_OPENMETHOD_OVERRIDERS(ID)                                        \
    BOOST_PP_CAT(BOOST_OPENMETHOD_ID(ID), _overriders)

//! Return the class template specialization containing an overrider.
//!
//! Expands to the specialization of the class template that contains the
//! overrider with the given name, parameter list and return type.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @param ID The method's name.
//! @param PARAMETERS The overrider's parameter list, in parentheses.
//! @param ... The overrider's return type.
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
#define BOOST_OPENMETHOD_OVERRIDER(ID, PARAMETERS, ...)                        \
    BOOST_OPENMETHOD_OVERRIDERS(ID)<__VA_ARGS__ PARAMETERS>

#define BOOST_OPENMETHOD_GUIDE(ID) BOOST_PP_CAT(BOOST_OPENMETHOD_ID(ID), _guide)

//! Expand to a core `method` specialization.
//!
//! Expands to the core @ref boost::openmethod::method specialization created by
//! @ref BOOST_OPENMETHOD called with the same arguments.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @param ID The method's name.
//! @param PARAMETERS The method's parameter list, in parentheses.
//! @param ... The method's return type, optionally followed by the registry.
//!
//! @see [Core API](xref:ROOT:core_api.adoc)
#define BOOST_OPENMETHOD_TYPE(ID, PARAMETERS, ...)                             \
    ::boost::openmethod::method<                                               \
        BOOST_OPENMETHOD_ID(ID),                                               \
        ::boost::openmethod::detail::va_args<__VA_ARGS__>::return_type         \
            PARAMETERS,                                                        \
        ::boost::openmethod::detail::va_args<__VA_ARGS__>::registry>

//! Declare a method.
//!
//! Declares a method, called `ID`, with the given parameters and return type,
//! and adds it to a registry.
//!
//! `PARAMETERS` is a comma-separated list of types, possibly followed by
//! parameter names, just like in a function declaration. Parameters with a type
//! in the form `virtual_ptr<T>` or `virtual_<T>` are called virtual parameters.
//! The dynamic type of the arguments passed in virtual parameters determines
//! which overrider to call, following the same rules as overloaded function
//! resolution:
//!
//! @li Form the set of all applicable overriders. An overrider is applicable
//! if it can be called with the arguments passed to the method.
//!
//! @li If the set is empty, call the error handler (if present in the
//! registry), then terminate the program with `abort`.
//!
//! @li Remove the overriders that are dominated by other overriders in the
//! set. Overrider A dominates overrider B if any of its virtual formal
//! parameters is more specialized than B's, and if none of B's virtual
//! parameters is more specialized than A's.
//!
//! @li If the resulting set contains exactly one overrider, call it.
//!
//! If a single most specialized overrider does not exist, the program is
//! terminated via `abort`. If the registry contains an `error_handler` policy,
//! its `error` function is called with an object that describes the error,
//! prior to calling `abort`. `error` may prevent termination by throwing an
//! exception.
//!
//! For each virtual argument `arg`, the dispatch mechanism calls
//! `virtual_traits::peek(arg)` and deduces the v-table pointer from the
//! `result`, using the first of the following methods that applies:
//!
//! @li If `result` is a `virtual_ptr`, get the pointer to the v-table from it.
//!
//! @li If
//! [boost_openmethod_vptr](xref:reference:boost/openmethod/boost_openmethod_vptr.adoc)
//! can be called with `result` and a `Registry*`, and it returns a
//! `vptr_type`, call it.
//!
//! @li If `virtual_traits` provides a `vptr` function, call it.
//!
//! @li Call the
//! [dynamic_vptr](xref:reference:boost/openmethod/policies/VptrFn/dynamic_vptr.adoc)
//! of the registry's `vptr` policy.
//!
//! The macro creates an ordinary inline function in the current scope, with the
//! `virtual_` decorators removed from the parameter types. `virtual_ptr`
//! parameters are preserved.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @note The default registry is the value of
//! @ref BOOST_OPENMETHOD_DEFAULT_REGISTRY at the point
//! `<boost/openmethod/core.hpp>` is included, directly or through a header
//! like `<boost/openmethod.hpp>`. Changing the value of this symbol has no
//! effect after that point.
//!
//! @par Example
//!
//! See [BOOST_OPENMETHOD_OVERRIDE](xref:reference:BOOST_OPENMETHOD_OVERRIDE.adoc#_example)
//! for an example.
//!
//! @par Implementation Notes
//!
//! The macro creates several additional constructs:
//!
//! @li A `struct` forward declaration that acts as the method's identifier:
//! @code
//! struct BOOST_OPENMETHOD_ID(ID);
//! @endcode
//!
//! @li A class template declaration that acts as a container for the method's
//! overriders in the current scope:
//! @code
//! template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(ID);
//! @endcode
//!
//! @li A guide function used to match overriders with the method:
//! @code
//! auto BOOST_OPENMETHOD_ID(ID)_guide(...)
//!     -> ::boost::openmethod::method<
//!         BOOST_OPENMETHOD_ID(ID)(PARAMETERS...), RETURN_TYPE [, REGISTRY]>;
//! @endcode
//!
//! @li A registrar (see @ref BOOST_OPENMETHOD_REGISTER) that adds the method to
//! the registry.
//!
//! @param ID The method's name.
//! @param PARAMETERS The method's parameter list, in parentheses.
//! @param ... The method's return type, optionally followed by the registry.
//!
//! @see [Methods and Overriders](xref:ROOT:basics.adoc)
//! @see [Header and Implementation Files](xref:ROOT:headers.adoc)
#define BOOST_OPENMETHOD(ID, PARAMETERS, ...)                                  \
    struct BOOST_OPENMETHOD_ID(ID);                                            \
    using BOOST_OPENMETHOD_GENSYM =                                            \
        BOOST_OPENMETHOD_TYPE(ID, PARAMETERS, __VA_ARGS__);                    \
    template<typename... ForwarderParameters>                                  \
    typename ::boost::openmethod::detail::enable_forwarder<                    \
        void, BOOST_OPENMETHOD_TYPE(ID, PARAMETERS, __VA_ARGS__),              \
        typename BOOST_OPENMETHOD_TYPE(ID, PARAMETERS, __VA_ARGS__),           \
        ForwarderParameters...>::type                                          \
        BOOST_OPENMETHOD_GUIDE(ID)(ForwarderParameters && ... args);           \
    template<typename... ForwarderParameters>                                  \
    inline auto ID(ForwarderParameters&&... args) ->                           \
        typename ::boost::openmethod::detail::enable_forwarder<                \
            void, BOOST_OPENMETHOD_TYPE(ID, PARAMETERS, __VA_ARGS__),          \
            ::boost::openmethod::detail::va_args<__VA_ARGS__>::return_type,    \
            ForwarderParameters...>::type {                                    \
        return BOOST_OPENMETHOD_TYPE(ID, PARAMETERS, __VA_ARGS__)::fn(         \
            std::forward<ForwarderParameters>(args)...);                       \
    }                                                                          \
    template<typename...>                                                      \
    struct BOOST_OPENMETHOD_OVERRIDERS(ID)

#define BOOST_OPENMETHOD_DETAIL_LOCATE_METHOD(ID, PARAMETERS)                  \
    template<typename T, typename = void>                                      \
    struct boost_openmethod_detail_locate_method_aux {                         \
        static_assert(                                                         \
            ::boost::openmethod::detail::method_not_found<T>,                  \
            "BOOST_OPENMETHOD_OVERRIDE: cannot find '" #ID                     \
            "' method that accepts the same arguments as the overrider");      \
    };                                                                         \
    template<typename... A>                                                    \
    struct boost_openmethod_detail_locate_method_aux<                          \
        void(A...),                                                            \
        std::void_t<decltype(BOOST_OPENMETHOD_GUIDE(ID)(                       \
            std::declval<A>()...))>> {                                         \
        using type =                                                           \
            decltype(BOOST_OPENMETHOD_GUIDE(ID)(std::declval<A>()...));        \
    }

//! Declare a method overrider.
//!
//! Declares an overrider for a method, but does not start its definition. This
//! macro can be used in header files.
//!
//! `ID` is the identifier of the method to which the overrider is added.
//!
//! `PARAMETERS` is a comma-separated list of types, possibly followed by
//! parameter names, just like in a function declaration.
//!
//! The macro tries to locate a method that can be called with the same argument
//! list as the overrider, possibly via argument dependent lookup.
//!
//! Each `virtual_ptr<T>` in the method's parameter list must have a
//! corresponding `virtual_ptr<U>` parameter in the same position in the
//! overrider's parameter list, such that `U` is the same as `T`, or has `T` as
//! an accessible unambiguous base.
//!
//! Each `virtual_<T>` in the method's parameter list must have a corresponding
//! `U` parameter in the same position in the overrider's parameter list, such
//! that `U` is the same as `T`, or has `T` as an accessible unambiguous base.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @par Example
//!
//! Use this macro, rather than @ref BOOST_OPENMETHOD_OVERRIDE, to split an
//! overrider across a header and an implementation file. The header declares
//! the overrider without a body:
//!
//! include:../examples/rolex/2/roles.hpp#content
//!
//! The implementation file supplies the body with
//! @ref BOOST_OPENMETHOD_DEFINE_OVERRIDER:
//!
//! include:../examples/rolex/2/employee.cpp#content
//!
//! This specific overrider can be called from other overriders explictly. No
//! dynamic dispatch is performed.
//!
//! include:../examples/rolex/2/salesman.cpp#content
//!
//! @par Implementation Notes
//!
//! The macro creates additional entities in the current scope.
//!
//! @li A class template declaration that acts as a container for the method's
//! overriders in the current scope:
//! @code
//! template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(ID);
//! @endcode
//!
//! @li A specialization of the container for the overrider:
//! @code
//! struct BOOST_OPENMETHOD_OVERRIDERS(ID)<RETURN_TYPE(PARAMETERS...)> {
//!     static auto fn(PARAMETERS...) -> RETURN_TYPE;
//!     static auto has_next() -> bool;
//!     template<typename... Args>
//!     static auto next(typename... Args) -> RETURN_TYPE;
//! };
//! @endcode
//!
//! @param ID The method's name.
//! @param PARAMETERS The overrider's parameter list, in parentheses.
//! @param ... The overrider's return type.
//!
//! @see [Header and Implementation Files](xref:ROOT:headers.adoc)
#define BOOST_OPENMETHOD_DECLARE_OVERRIDER(ID, PARAMETERS, ...)                \
    template<typename...>                                                      \
    struct BOOST_OPENMETHOD_OVERRIDERS(ID);                                    \
    template<>                                                                 \
    struct BOOST_OPENMETHOD_OVERRIDERS(ID)<__VA_ARGS__ PARAMETERS> {           \
        BOOST_OPENMETHOD_DETAIL_LOCATE_METHOD(ID, PARAMETERS);                 \
        static auto fn PARAMETERS->__VA_ARGS__;                                \
        static auto has_next() -> bool;                                        \
        template<typename... Args>                                             \
        static auto next(Args&&... args) -> decltype(auto);                    \
    };                                                                         \
    inline auto BOOST_OPENMETHOD_OVERRIDERS(                                   \
        ID)<__VA_ARGS__ PARAMETERS>::has_next() -> bool {                      \
        return boost_openmethod_detail_locate_method_aux<                      \
            void PARAMETERS>::type::has_next<fn>();                            \
    }                                                                          \
    template<typename... Args>                                                 \
    inline auto BOOST_OPENMETHOD_OVERRIDERS(ID)<__VA_ARGS__ PARAMETERS>::next( \
        Args&&... args) -> decltype(auto) {                                    \
        return boost_openmethod_detail_locate_method_aux<                      \
            void PARAMETERS>::type::next<fn>(std::forward<Args>(args)...);     \
    }

// REGISTRAR selects which of method<...>::override<Fn> (plain) or
// method<...>::inline_override<Fn> (see core.hpp) registers the overrider.
// Unlike a runtime flag, this is a compile-time choice baked into the
// registrar's own type, so it can't be affected by static-initialization
// ordering (see the comment on class override/inline_override in core.hpp
// for why a runtime constructor argument doesn't work here). Everything
// else is unchanged from - and exactly as comma-safe as -
// BOOST_OPENMETHOD_REGISTER itself: REGISTRAR is a bare identifier (no
// commas to worry about), and the fully-variadic BOOST_OPENMETHOD_REGISTER
// still captures the whole trailing type expression (built from the
// overrider's return type, which may contain an unprotected top-level comma,
// e.g. an un-aliased std::pair<A, B>) as one argument.
#define BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER_AUX(                        \
    ID, PARAMETERS, REGISTRAR, ...)                                            \
    BOOST_OPENMETHOD_REGISTER(                                                 \
        BOOST_OPENMETHOD_OVERRIDERS(ID) < __VA_ARGS__ PARAMETERS >             \
        ::boost_openmethod_detail_locate_method_aux<void PARAMETERS>::type::   \
            REGISTRAR<                                                         \
                BOOST_OPENMETHOD_OVERRIDERS(ID) <                              \
                __VA_ARGS__ PARAMETERS>::fn >);

#define BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(ID, PARAMETERS, ...)        \
    BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER_AUX(                            \
        ID, PARAMETERS, override, __VA_ARGS__)

//! Define the body of a method overrider.
//!
//! Defines the body of an overrider declared with
//! @ref BOOST_OPENMETHOD_DECLARE_OVERRIDER. It should be called in an
//! implementation file, and followed by a function body.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @par Example
//!
//! See [BOOST_OPENMETHOD_DECLARE_OVERRIDER](xref:reference:BOOST_OPENMETHOD_DECLARE_OVERRIDER.adoc#_example)
//! for an example.
//!
//! @param ID The method's name.
//! @param PARAMETERS The overrider's parameter list, in parentheses.
//! @param ... The overrider's return type.
//!
//! @see [Header and Implementation Files](xref:ROOT:headers.adoc)
#define BOOST_OPENMETHOD_DEFINE_OVERRIDER(ID, PARAMETERS, ...)                 \
    BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER(ID, PARAMETERS, __VA_ARGS__)    \
    auto BOOST_OPENMETHOD_OVERRIDER(                                           \
        ID, PARAMETERS, __VA_ARGS__)::fn PARAMETERS                            \
        -> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>

//! Add an overrider to a method.
//!
//! `BOOST_OPENMETHOD_OVERRIDE` adds an overrider to a method. It is followed by
//! the overrider's body.
//!
//! `ID` is the identifier of the method to which the overrider is added.
//!
//! `PARAMETERS` is a comma-separated list of types, possibly followed by
//! parameter names, just like in a function declaration.
//!
//! The macro tries to locate a method that can be called with the same argument
//! list as the overrider, possibly via argument dependent lookup.
//!
//! Each `virtual_ptr<T>` in the method's parameter list must have a
//! corresponding `virtual_ptr<U>` parameter in the same position in the
//! overrider's parameter list, such that `U` is the same as `T`, or has `T` as
//! an accessible unambiguous base.
//!
//! Each `virtual_<T>` in the method's parameter list must have a corresponding
//! `U` parameter in the same position in the overrider's parameter list, such
//! that `U` is the same as `T`, or has `T` as an accessible unambiguous base.
//!
//! The following names are available inside the overrider's body:
//!
//! @li `fn`: a pointer to a function, the overrider itself. Can be used for
//! recursion.
//!
//! @li `next`: a function with the same signature as the method (minus the
//! `virtual_<>` decorators). It forwards to the next most specialized
//! overrider, if it exists and it is unique. If the next overrider does not
//! exist, or is ambiguous, calling `next` reports a
//! @ref boost::openmethod::no_overrider or a
//! @ref boost::openmethod::ambiguous_call and terminates the program.
//!
//! @li `has_next()`: returns `true` if the next most specialized overrider
//! exists.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @par Example
//!
//! include:macros.cpp#declare;override;call
//!
//! @par Implementation Notes
//!
//! The macro creates additional entities in the current scope.
//!
//! @li A class template declaration that acts as a container for the method's
//! overriders in the current scope:
//! @code
//! template<typename...> struct BOOST_OPENMETHOD_OVERRIDERS(ID);
//! @endcode
//!
//! @li A specialization of the container for the overrider:
//! @code
//! struct BOOST_OPENMETHOD_OVERRIDERS(ID)<RETURN_TYPE(PARAMETERS...)> {
//!     static auto fn(PARAMETERS...) -> RETURN_TYPE;
//!     static auto has_next() -> bool;
//!     template<typename... Args>
//!     static auto next(typename... Args) -> RETURN_TYPE;
//! };
//! @endcode
//!
//! @li A registrar (see @ref BOOST_OPENMETHOD_REGISTER) adding the overrider to
//! the method.
//!
//! @li Finally, the macro starts the definition of the overrider function:
//! @code
//! auto BOOST_OPENMETHOD_OVERRIDERS(ID)<RETURN_TYPE(PARAMETERS...)>::fn(
//!     PARAMETERS...) -> RETURN_TYPE
//! @endcode
//!
//! The `{}` block following the call to the macro is the body of the function.
//!
//! @param ID The method's name.
//! @param PARAMETERS The overrider's parameter list, in parentheses.
//! @param ... The overrider's return type.
//!
//! @see [Methods and Overriders](xref:ROOT:basics.adoc)
//! @see [Header and Implementation Files](xref:ROOT:headers.adoc)
//! @see [Namespaces](xref:ROOT:namespaces.adoc)
//! @see [Friends](xref:ROOT:friends.adoc)
#define BOOST_OPENMETHOD_OVERRIDE(ID, PARAMETERS, ...)                         \
    BOOST_OPENMETHOD_DECLARE_OVERRIDER(ID, PARAMETERS, __VA_ARGS__)            \
    BOOST_OPENMETHOD_DEFINE_OVERRIDER(ID, PARAMETERS, __VA_ARGS__)

// Unlike BOOST_OPENMETHOD_OVERRIDE, registers via method<...>::inline_override
// instead of method<...>::override, marking the overrider_info as
// inline_ = true (see core.hpp and overrider_info in preamble.hpp), which
// makes it eligible for cross-module dedup during augment_methods()
// consolidation. Only an overrider defined `inline` can legally have an
// identical definition appear in more than one translation unit/module in
// the first place, which is why plain BOOST_OPENMETHOD_OVERRIDE never sets
// this.

//! Add an overrider to a method as an inline function.
//!
//! `BOOST_OPENMETHOD_INLINE_OVERRIDE` performs the same function as
//! @ref BOOST_OPENMETHOD_OVERRIDE, except that the overrider is marked
//! `inline`.
//!
//! Use it for an overrider defined in a header, where the same definition
//! reaches more than one translation unit. `inline` is what makes the repeated
//! definition legal, and it lets @ref boost::openmethod::initialize merge the
//! repeated registrations. @ref BOOST_OPENMETHOD_OVERRIDE would instead record
//! them as distinct overriders for the same class, making the call ambiguous.
//!
//! @note `ID` must be an *identifier*. Qualified names are not allowed.
//!
//! @par Example
//!
//! A header that declares a method and supplies a default overrider for it.
//! Every translation unit including it gets the same definition:
//!
//! include:../examples/rolex/3/roles.hpp#content
//!
//! A translation unit that includes the header adds a more specialized
//! overrider of its own. That one is defined once, so it uses
//! @ref BOOST_OPENMETHOD_OVERRIDE; it reaches the header's overrider through
//! @ref BOOST_OPENMETHOD_OVERRIDER:
//!
//! include:../examples/rolex/3/salesman.cpp#content
//!
//! @param ID The method's name.
//! @param PARAMETERS The overrider's parameter list, in parentheses.
//! @param ... The overrider's return type.
//!
//! @see [Header and Implementation Files](xref:ROOT:headers.adoc)
#define BOOST_OPENMETHOD_INLINE_OVERRIDE(ID, PARAMETERS, ...)                  \
    BOOST_OPENMETHOD_DECLARE_OVERRIDER(ID, PARAMETERS, __VA_ARGS__)            \
    BOOST_OPENMETHOD_DETAIL_REGISTER_OVERRIDER_AUX(                            \
        ID, PARAMETERS, inline_override, __VA_ARGS__)                          \
    inline auto BOOST_OPENMETHOD_OVERRIDER(                                    \
        ID, PARAMETERS, __VA_ARGS__)::fn PARAMETERS                            \
        -> boost::mp11::mp_back<boost::mp11::mp_list<__VA_ARGS__>>

//! Register classes.
//!
//! Registers classes in a registry.
//!
//! This macro is a wrapper around @ref boost::openmethod::use_classes; see its
//! documentation for more details.
//!
//! @note The default registry is the value of
//! @ref BOOST_OPENMETHOD_DEFAULT_REGISTRY when `<boost/openmethod/core.hpp>`
//! is included, directly or through a header like `<boost/openmethod.hpp>`.
//! Subsequently changing it has no retroactive effect.
//!
//! @par Examples
//!
//! A class and its direct bases must appear together in one call. Take `Cat`
//! and `Dog`, both derived from `Animal`, and `Bulldog`, derived from `Dog`.
//! A single call listing all of them describes the hierarchy:
//!
//! @code
//! BOOST_OPENMETHOD_CLASSES(Animal, Cat, Dog, Bulldog);
//! @endcode
//!
//! Several calls do just as well, as long as every class appears alongside its
//! direct bases. `Dog` is listed twice here, and that is what attaches
//! `Bulldog` to it:
//!
//! @code
//! BOOST_OPENMETHOD_CLASSES(Animal, Cat, Dog);
//! BOOST_OPENMETHOD_CLASSES(Dog, Bulldog);
//! @endcode
//!
//! Registering the classes one per call describes no inheritance at all, and
//! @ref boost::openmethod::initialize reports a
//! @ref boost::openmethod::missing_base error:
//!
//! @code
//! BOOST_OPENMETHOD_CLASSES(Animal);
//! BOOST_OPENMETHOD_CLASSES(Cat);
//! BOOST_OPENMETHOD_CLASSES(Dog); // initialize reports missing_base
//! @endcode
//!
//! Listing a class with an ancestor in place of its direct base is the more
//! dangerous mistake, because nothing reports it. Below, `Bulldog` is recorded
//! as derived from `Animal`; an overrider for `Dog` no longer applies to it, so
//! a call passing a `Bulldog` quietly selects the overrider for `Animal`:
//!
//! @code
//! BOOST_OPENMETHOD_CLASSES(Animal, Cat, Dog);
//! BOOST_OPENMETHOD_CLASSES(Animal, Bulldog);
//! // OpenMethod believes that Bulldog derives from Animal, not Dog
//! @endcode
//!
//! @param ... The classes to register, optionally followed by the registry.
//!
//! @see [Methods and Overriders](xref:ROOT:basics.adoc)
#define BOOST_OPENMETHOD_CLASSES(...)                                          \
    BOOST_OPENMETHOD_REGISTER(::boost::openmethod::use_classes<__VA_ARGS__>)

// The reference documentation for this macro is on the fallback definition
// below. MrDocs compiles the `#else` branch, and a doc comment separated
// from its `#define` by preprocessor directives is not attached to it.
#if BOOST_OPENMETHOD_HAS_REFLECTION
#define BOOST_OPENMETHOD_REGISTER_CLASSES(...)                                 \
    BOOST_OPENMETHOD_REGISTER(                                                 \
        ::boost::openmethod::register_classes<__VA_ARGS__>)
#else
//! Find the classes taking part in dispatch by reflection, and register them.
//!
//! It makes @ref BOOST_OPENMETHOD_CLASSES unnecessary in most cases.
//!
//! This macro is a wrapper around @ref boost::openmethod::register_classes; see
//! its documentation for the meaning of the arguments, which are passed
//! through verbatim: groups of reflections, each written in braces - or, for a
//! group of one, as the reflection itself - holding the namespaces to scan,
//! the classes to register, and the registries; each group optional, in that
//! order. With no namespace group, the global namespace is scanned.
//!
//! Reflection sees only what precedes it, so this macro must come **after** the
//! declarations it is meant to find - at the bottom of the file:
//!
//! @code
//! struct Animal { virtual ~Animal() = default; };
//! struct Cat : Animal {};
//! struct Dog : Animal {};
//! struct Bulldog : Dog {};
//!
//! BOOST_OPENMETHOD(poke, (std::ostream&, virtual_<Animal&>), void);
//!
//! BOOST_OPENMETHOD_OVERRIDE(poke, (std::ostream& os, Dog&), void) {
//!     os << "bark";
//! }
//!
//! BOOST_OPENMETHOD_REGISTER_CLASSES(); // registers all four classes
//! @endcode
//!
//! Without reflection - in C++17, or in C++26 without the compiler flag that
//! enables it - this macro expands to nothing, so a file that also calls
//! @ref BOOST_OPENMETHOD_CLASSES builds under either standard.
//!
//! @param ... Braced groups: namespaces, classes, registries - see above.
//!
//! @see [Methods and Overriders](xref:ROOT:basics.adoc)
#define BOOST_OPENMETHOD_REGISTER_CLASSES(...) static_assert(true)
#endif

// The three macros below share a registry's state - the single variable
// registry_state<R::registry_type>::st, see registry_state in preamble.hpp -
// across module boundaries, by emitting the explicit instantiations that make
// it one shared symbol. See their documentation comments for how they are
// meant to be used. They exist because no single spelling is portable: the two
// ABIs want opposite things.
//
// * declspec platforms (Windows, Cygwin, MinGW): MSVC rejects `extern` together
//   with __declspec(dllexport) on an explicit instantiation outright ("warning
//   C4910: '__declspec(dllexport)' and 'extern' are incompatible on an explicit
//   instantiation"). Nor is such a declaration needed: visibility is not a PE
//   concept, so the owning module's other translation units may instantiate the
//   state implicitly and the linker merges them within the module. EXPORT
//   therefore expands to nothing, and INSTANTIATE carries the dllexport.
//
// * ELF and Mach-O: the attribute has to be on the *declaration*, so that every
//   translation unit of the owning module pins the symbol to default
//   visibility. Repeating it on the definition is an error on GCC ("type
//   attributes ignored after type is already defined"), which is why
//   INSTANTIATE carries no attribute there. And EXPORT is not decoration: a
//   translation unit of the owner that has neither it nor the definition
//   instantiates the state implicitly, and under -fvisibility=hidden that copy
//   is module-local; since ELF merges COMDATs at the most restrictive
//   visibility, the whole symbol then becomes local and clients fail to link.

//! Import a registry's state from the module that owns it.
//!
//! All of a registry's mutable state lives in a single variable (see
//! @ref boost::openmethod::registry_state). Sharing a registry across modules
//! means sharing that one symbol, which takes three macros: the owning module
//! uses @ref BOOST_OPENMETHOD_EXPORT_REGISTRY in the header its translation
//! units share, and @ref BOOST_OPENMETHOD_INSTANTIATE_REGISTRY in exactly one
//! of them; every client module uses `BOOST_OPENMETHOD_IMPORT_REGISTRY`.
//!
//! They exist to hide a platform incompatibility: on Windows, Cygwin and
//! MinGW, `__declspec(dllexport)` and `extern` are incompatible on an explicit
//! instantiation, while on ELF and Mach-O the visibility attribute must be on
//! the declaration and must not be repeated on the definition.
//!
//! Use at namespace scope, after the registry's definition, in every
//! translation unit of every module that uses the registry without owning it.
//! Being a declaration it may be repeated, so it belongs in the header those
//! modules share. Everything it emits is fully qualified, so there is no need
//! to be inside, or to open, namespace `boost::openmethod`.
//!
//! It emits an `extern template` declaration decorated with
//! `BOOST_SYMBOL_IMPORT` (`__declspec(dllimport)` on Windows, nothing on ELF).
//! The declaration suppresses the client's own instantiation, so it references
//! the owner's symbol instead of creating a private copy.
//!
//! The client module must be linked so the reference resolves: on Windows and
//! macOS by linking against the owning module; on ELF a dynamically loaded
//! library may also leave it for the dynamic linker to resolve at load time.
//!
//! @param REGISTRY The registry to import. May be any registry, predefined or
//! user-defined.
//!
//! @see [Shared Libraries](xref:ROOT:shared_libraries.adoc) for the full
//! discussion, including the required link setup.
#define BOOST_OPENMETHOD_IMPORT_REGISTRY(REGISTRY)                             \
    extern template struct BOOST_SYMBOL_IMPORT ::boost::openmethod::           \
        registry_state<REGISTRY::registry_type>

#ifdef BOOST_HAS_DECLSPEC

#define BOOST_OPENMETHOD_DETAIL_EXPORT_REGISTRY(REGISTRY) static_assert(true)

#define BOOST_OPENMETHOD_DETAIL_INSTANTIATE_REGISTRY(REGISTRY)                 \
    template struct BOOST_SYMBOL_EXPORT ::boost::openmethod::registry_state<   \
        REGISTRY::registry_type>

#else

#define BOOST_OPENMETHOD_DETAIL_EXPORT_REGISTRY(REGISTRY)                      \
    extern template struct BOOST_SYMBOL_EXPORT ::boost::openmethod::           \
        registry_state<REGISTRY::registry_type>

#define BOOST_OPENMETHOD_DETAIL_INSTANTIATE_REGISTRY(REGISTRY)                 \
    template struct ::boost::openmethod::registry_state<REGISTRY::registry_type>

#endif

//! Declare a registry's state exported, in every translation unit of the
//! owning module.
//!
//! See @ref BOOST_OPENMETHOD_IMPORT_REGISTRY for how the three registry-sharing
//! macros fit together.
//!
//! Use at namespace scope, after the registry's definition, in *every*
//! translation unit of the module that owns the registry. Being a declaration
//! it may be repeated, so it belongs in the header those translation units
//! share.
//!
//! On ELF it emits an exported explicit instantiation *declaration*, which
//! both suppresses implicit instantiation and pins the symbol to default
//! visibility. On declspec platforms it expands to nothing, because there the
//! export belongs on the instantiation instead.
//!
//! @warning On ELF this macro is not decoration. A translation unit of the
//! owning module that uses neither it nor
//! @ref BOOST_OPENMETHOD_INSTANTIATE_REGISTRY instantiates the state
//! implicitly, and under `-fvisibility=hidden` that copy is module-local. Since
//! ELF merges COMDATs at the *most restrictive* visibility, the merged symbol
//! becomes local: the module builds, exports nothing, and clients fail to link
//! with an undefined reference to `registry_state<...>::st`.
//!
//! @param REGISTRY The registry to export. May be any registry, predefined or
//! user-defined.
//!
//! @see [Shared Libraries](xref:ROOT:shared_libraries.adoc) for the full
//! discussion, including the required link setup.
#define BOOST_OPENMETHOD_EXPORT_REGISTRY(REGISTRY)                             \
    BOOST_OPENMETHOD_DETAIL_EXPORT_REGISTRY(REGISTRY)

//! Instantiate a registry's state, in exactly one translation unit of the
//! owning module.
//!
//! See @ref BOOST_OPENMETHOD_IMPORT_REGISTRY for how the three registry-sharing
//! macros fit together.
//!
//! Use at namespace scope, after the registry's definition, in *exactly one*
//! translation unit of the module that owns the registry. It belongs in a
//! `.cpp` file, never in a header.
//!
//! It emits the explicit instantiation *definition* of the registry state, of
//! which a program may contain only one. On declspec platforms the definition
//! carries the `dllexport`; on ELF and Mach-O it carries no attribute, that
//! having been supplied by @ref BOOST_OPENMETHOD_EXPORT_REGISTRY in the header.
//!
//! @param REGISTRY The registry to instantiate. May be any registry, predefined
//! or user-defined.
//!
//! @see [Shared Libraries](xref:ROOT:shared_libraries.adoc) for the full
//! discussion, including the required link setup.
#define BOOST_OPENMETHOD_INSTANTIATE_REGISTRY(REGISTRY)                        \
    BOOST_OPENMETHOD_DETAIL_INSTANTIATE_REGISTRY(REGISTRY)

#endif


#endif // BOOST_OPENMETHOD_HPP
