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

#include <boost/openmethod/core.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
