#ifndef BOOST_OPENMETHOD_REGISTRY_HPP #define BOOST_OPENMETHOD_REGISTRY_HPP #include #include #include #include #include #include #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4702) #endif namespace boost::openmethod { 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 //! 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*; //! 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. template struct virtual_; template struct virtual_traits; // ----------------------------------------------------------------------------- // Error handling //! Base class for all OpenMethod errors. 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 //! {{BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS}}. 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 auto write(Stream& stream) const { stream << "conflicting definitions of "; Registry::rtti::type_name( Registry::rtti::template static_type(), stream); } }; namespace detail { template struct odr_check { static std::size_t count; template static std::size_t inc; odr_check() { [[maybe_unused]] auto _ = &inc; } }; template std::size_t odr_check::count; template template std::size_t odr_check::inc = count++; } // namespace detail //! Registry not initialized 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 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 //! //! Missing registration of a class used as a virtual parameter in a method: //! @code //! struct Animal { virtual ~Animal() {} }; //! struct Dog : Animal {}; //! //! BOOST_OPENMETHOD_CLASSES(Animal); //! //! BOOST_OPENMETHOD(poke, (virtual_ptr), void); //! //! initialize(); // throws missing_class; //! @endcode //! //! Missing registration of a class used as a virtual parameter in an overrider: //! @code //! BOOST_OPENMETHOD_CLASSES(Animal); //! //! BOOST_OPENMETHOD(poke, (virtual_ptr), void); //! //! BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr), void) { /* ... */ } //! //! initialize(); // throws missing_class; //! @endcode //! //! Missing registration of a class used as a virtual parameter in a call: //! @code //! struct Bulldog : Dog {}; //! //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! //! BOOST_OPENMETHOD(poke, (virtual_ptr), void); //! //! BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr), void) { /* ... */ } //! //! Bulldog hector; //! poke(hector); // throws missing_class; //! @endcode 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 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 //! 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. //! //! @code //! BOOST_OPENMETHOD_CLASSES(Animal); //! BOOST_OPENMETHOD_CLASSES(Dog); //! //! BOOST_OPENMETHOD(poke, (virtual_ptr), void); //! //! BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr), void) { /* ... */ } //! //! initialize(); // throws missing_base; //! @endcode //! //! Fix: //! //! @code //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); //! @endcode 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 auto write(Stream& os) const; }; //! No valid overrider 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 //! //! @see @ref bad_call for data members. 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 auto write(Stream& os) const { os << "not implemented"; } }; //! Ambiguous call //! //! @see @ref bad_call for data members. 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 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 //! @ref abort. 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 auto write(Stream& os) const; }; namespace detail { struct empty {}; template struct range { range(Iterator first, Iterator last) : first(first), last(last) { } Iterator first, last; auto begin() const -> Iterator { return first; } auto end() const -> Iterator { return last; } }; // ----------------------------------------------------------------------------- // class info struct class_info : static_list::static_link { type_id type; vptr_type* static_vptr; type_id *first_base, *last_base; bool is_abstract{false}; auto vptr() const { 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::static_link { type_id* vp_begin; type_id* vp_end; static_list 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::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)(); }; struct deferred_overrider_info : overrider_info { virtual void resolve_type_ids() = 0; }; struct unspecified {}; } // namespace detail #ifdef __MRDOCS__ //! 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 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 {}; //! 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 } //! 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` metafunction //! that conforms to the blueprint's requirements. //! //! @see @ref registry for a complete explanation of registries and policies. 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 struct RttiFn { //! Tests if a class is polymorphic. //! //! @tparam Class A class. template static constexpr bool is_polymorphic = std::is_polymorphic_v; //! 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 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 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 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 static auto dynamic_cast_ref(B&& obj) -> D; }; #endif //! 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 `rtti`. //! @li provide a `fn` metafunction that conforms to the @ref RttiFn //! blueprint. 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 static void type_name(type_id type, Stream& stream) { stream << "type_id(" << type << ")"; } }; }; //! 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 update. struct deferred_static_rtti : rtti {}; #ifdef __MRDOCS__ //! Blueprint for @ref error_handler metafunctions (exposition only). template 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 `error_handler`. //! @li provide a `fn` metafunction that conforms to the @ref //! ErrorHandlerFn blueprint. struct error_handler { // Policy category. using category = error_handler; }; #ifdef __MRDOCS__ //! Blueprint for `vptr` metafunctions (exposition only). //! //! @tparam Registry The registry containing the policy. template 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. template static auto initialize(const Context& ctx) -> 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 a the v-table pointer for `Class`. template static auto dynamic_vptr(const Class& arg) -> 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 static auto finalize(const std::tuple& options) -> void; }; #endif //! Policy for v-table pointer acquisition. //! //! @par Requirements //! //! Classes implementing this policy must: //! @li derive from `vptr`. //! @li provide a `fn` metafunction that conforms to the @ref //! VptrFn blueprint. 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. struct indirect_vptr final { // Policy category. using category = indirect_vptr; template struct fn {}; }; #ifdef __MRDOCS__ //! Blueprint for @ref type_hash metafunctions (exposition only). //! //! @tparam Registry The registry containing the policy. template struct TypeHashFn { //! Initialize the hash table. //! //! @tparam Context A class that conforms to the @ref InitializeContext //! blueprint. //! @return A pair containing the minimum and maximum hash values. template static auto initialize(const Context& ctx) -> std::pair; //! 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 static auto finalize(const std::tuple& options) -> void; }; #endif //! Policy for hashing type ids. //! //! @par Requirements //! //! Classes implementing this policy must: //! @li derive from `rtti`. //! @li provide a `fn` metafunction that conforms to the @ref //! TypeHashFn blueprint. 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 struct OutputFn { //! A @ref LightweightOutputStream. inline static LightweightOutputStream os; }; #endif //! 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 `output`. //! @li provide a `fn` metafunction that conforms to the @ref //! OutputFn blueprint. struct output { // Policy category. using category = output; }; //! 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). struct runtime_checks final { // Policy category. using category = runtime_checks; template struct fn {}; }; } // namespace policies namespace detail { struct registry_base {}; template constexpr bool is_registry = std::is_base_of_v; template constexpr bool is_not_void = !std::is_same_v; template< class Registry, class Index, class Size = mp11::mp_size> struct get_policy_aux { using type = typename mp11::mp_at< typename Registry::policy_list, Index>::template fn; }; template struct get_policy_aux { using type = void; }; using class_catalog = detail::static_list; using method_catalog = detail::static_list; template struct with_aux; template struct with_aux { using type = Policies; }; template struct with_aux { using replace = mp11::mp_replace_if_q< Policies, mp11::mp_bind_front_q< mp11::mp_quote_trait, typename Policy::category>, Policy>; using replace_or_add = std::conditional_t< std::is_same_v, mp11::mp_push_back, replace>; using type = typename with_aux::type; }; template struct without_aux; template struct without_aux { using type = Policies; }; template struct without_aux { using type = typename without_aux< mp11::mp_remove_if_q< Policies, mp11::mp_bind_front_q< mp11::mp_quote_trait, typename Policy::category>>, MorePolicies...>::type; }; template struct use_class_aux; template struct initialize_aux; } // namespace detail //! 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` 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. //! //! @tparam Policy The policies used in the registry. //! //! @par Requirements //! //! @li `Policy` must contain a `category` alias to its root base class. The //! registry may contain at most one policy per category. //! //! @li `Policy` must contain a `fn` metafunction. //! //! @see @ref policies template class registry : detail::registry_base { static detail::class_catalog classes; static detail::method_catalog methods; template friend struct detail::use_class_aux; template friend class method; static std::vector dispatch_data; static bool initialized; public: //! The type of this registry. using registry_type = registry; template 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 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 static vptr_type static_vptr; //! 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; //! 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 using policy = typename detail::get_policy_aux< registry, mp11::mp_find_if_q< policy_list, mp11::mp_bind_front_q< mp11::mp_quote_trait, Category>>>::type; //! 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 Models of @ref policies::Policy. template using with = boost::mp11::mp_apply< registry, typename detail::with_aux::type>; //! Remove policies. //! //! `without` aliases to a registry containing the same policies, except those //! that derive from `Categories`. //! //! @tparam Categories Models of @ref policies::PolicyCategory. template using without = boost::mp11::mp_apply< registry, typename detail::without_aux::type>; //! The registry's rtti policy. using rtti = policy; //! The registry's vptr policy if it contains one, or `void`. using vptr = policy; //! `true` if the registry has a vptr policy. static constexpr auto has_vptr = !std::is_same_v; //! The registry's error_handler policy if it contains one, or `void`. using error_handler = policy; //! `true` if the registry has an error_handler policy. static constexpr auto has_error_handler = !std::is_same_v; //! The registry's output policy if it contains one, or `void`. using output = policy; //! `true` if the registry has an output policy. static constexpr auto has_output = !std::is_same_v; //! `true` if the registry has a deferred_static_rtti policy. static constexpr auto has_deferred_static_rtti = !std::is_same_v, void>; //! `true` if the registry has a runtime_checks policy. static constexpr auto has_runtime_checks = !std::is_same_v, void>; //! `true` if the registry has an indirect_vptr policy. static constexpr auto has_indirect_vptr = !std::is_same_v, void>; }; template detail::class_catalog registry::classes; template detail::method_catalog registry::methods; template std::vector registry::dispatch_data; template bool registry::initialized; template template vptr_type registry::static_vptr; template void registry::require_initialized() { if constexpr (registry::has_runtime_checks) { if (!initialized) { if constexpr (registry::has_error_handler) { error_handler::error(not_initialized()); } abort(); } } } template auto missing_class::write(Stream& os) const { os << "unknown class "; Registry::rtti::type_name(type, os); } template auto missing_base::write(Stream& os) const { os << "missing base "; Registry::rtti::type_name(base, os); os << " -<| "; Registry::rtti::type_name(derived, os); } template 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); } } // namespace boost::openmethod #ifdef _MSC_VER #pragma warning(pop) #endif #endif // BOOST_OPENMETHOD_REGISTRY_HPP