|
|
|||
File indexing completed on 2026-08-17 08:52:05
0001 #ifndef BOOST_OPENMETHOD_REGISTRY_HPP 0002 #define BOOST_OPENMETHOD_REGISTRY_HPP 0003 0004 #include <boost/openmethod/detail/static_list.hpp> 0005 0006 #include <boost/mp11/algorithm.hpp> 0007 #include <boost/mp11/bind.hpp> 0008 0009 #include <stdlib.h> 0010 #include <vector> 0011 #include <cstdint> 0012 0013 #ifdef _MSC_VER 0014 #pragma warning(push) 0015 #pragma warning(disable : 4702) 0016 #endif 0017 0018 namespace boost::openmethod { 0019 0020 namespace detail { 0021 0022 union word { 0023 word() { 0024 } // undefined 0025 word(void (*pf)()) : pf(pf) { 0026 } 0027 word(word* pw) : pw(pw) { 0028 } 0029 word(std::size_t i) : i(i) { 0030 } 0031 0032 void (*pf)(); 0033 std::size_t i; 0034 word* pw; 0035 }; 0036 0037 } // namespace detail 0038 0039 //! Alias to v-table pointer type. 0040 //! 0041 //! `vptr_type` is an alias to the type of a v-table pointer. 0042 using vptr_type = const detail::word*; 0043 0044 //! Type used to identify a class. 0045 //! 0046 //! `type_id` is the return type of the @ref static_type and @ref dynamic_type 0047 //! functions. It can be used as an actual data pointer (e.g. to a 0048 //! `std::type_info` object), or as an opaque integer type. 0049 using type_id = const void*; 0050 0051 //! Decorator for virtual parameters. 0052 //! 0053 //! `virtual_` marks a formal parameter of a method as virtual. It is a @em 0054 //! decorator, not an actual type that can be instantiated (it does not have a 0055 //! definition). It is removed from the method's signature. 0056 //! 0057 //! @note `virtual_` can be used @em only in method declarations, @em not in 0058 //! overriders. A parameter in overriders is implicitly virtual if it is in 0059 //! the same position as a virtual parameter in the method's declaration. 0060 //! 0061 //! @par Requirements 0062 //! 0063 //! - @ref virtual_traits must be specialized for `T`. 0064 //! 0065 //! @tparam T A class. 0066 template<typename T> 0067 struct virtual_; 0068 0069 template<typename T, class Registry> 0070 struct virtual_traits; 0071 0072 // ----------------------------------------------------------------------------- 0073 // Error handling 0074 0075 //! Base class for all OpenMethod errors. 0076 struct openmethod_error {}; 0077 0078 //! One Definition Rule violation. 0079 //! 0080 //! This error is raised if the definition of @ref default_registry is 0081 //! inconsistent across translation units, due to misuse of 0082 //! {{BOOST_OPENMETHOD_ENABLE_RUNTIME_CHECKS}}. 0083 struct odr_violation : openmethod_error { 0084 //! Write a description of the error to a stream. 0085 //! @tparam Registry The registry containing this policy. 0086 //! @param stream The stream to write to. 0087 template<class Registry, class Stream> 0088 auto write(Stream& stream) const { 0089 stream << "conflicting definitions of "; 0090 Registry::rtti::type_name( 0091 Registry::rtti::template static_type<Registry>(), stream); 0092 } 0093 }; 0094 0095 namespace detail { 0096 0097 template<class Registry> 0098 struct odr_check { 0099 static std::size_t count; 0100 template<class R> 0101 static std::size_t inc; 0102 0103 odr_check() { 0104 [[maybe_unused]] auto _ = &inc<typename Registry::registry_type>; 0105 } 0106 }; 0107 0108 template<class Registry> 0109 std::size_t odr_check<Registry>::count; 0110 0111 template<class Registry> 0112 template<class R> 0113 std::size_t odr_check<Registry>::inc = count++; 0114 0115 } // namespace detail 0116 0117 //! Registry not initialized 0118 struct not_initialized : openmethod_error { 0119 //! Write a short description to an output stream 0120 //! @param os The output stream 0121 //! @tparam Registry The registry 0122 //! @tparam Stream A @ref LightweightOutputStream 0123 template<class Registry, class Stream> 0124 auto write(Stream& os) const { 0125 os << "not initialized"; 0126 } 0127 }; 0128 0129 //! Missing class. 0130 //! 0131 //! A class used as a virtual parameter in a method, an overrider or a method 0132 //! call was not registered. 0133 //! 0134 //! @par Examples 0135 //! 0136 //! Missing registration of a class used as a virtual parameter in a method: 0137 //! @code 0138 //! struct Animal { virtual ~Animal() {} }; 0139 //! struct Dog : Animal {}; 0140 //! 0141 //! BOOST_OPENMETHOD_CLASSES(Animal); 0142 //! 0143 //! BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>), void); 0144 //! 0145 //! initialize(); // throws missing_class; 0146 //! @endcode 0147 //! 0148 //! Missing registration of a class used as a virtual parameter in an overrider: 0149 //! @code 0150 //! BOOST_OPENMETHOD_CLASSES(Animal); 0151 //! 0152 //! BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>), void); 0153 //! 0154 //! BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>), void) { /* ... */ } 0155 //! 0156 //! initialize(); // throws missing_class; 0157 //! @endcode 0158 //! 0159 //! Missing registration of a class used as a virtual parameter in a call: 0160 //! @code 0161 //! struct Bulldog : Dog {}; 0162 //! 0163 //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); 0164 //! 0165 //! BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>), void); 0166 //! 0167 //! BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>), void) { /* ... */ } 0168 //! 0169 //! Bulldog hector; 0170 //! poke(hector); // throws missing_class; 0171 //! @endcode 0172 struct missing_class : openmethod_error { 0173 //! The type_id of the unknown class. 0174 type_id type; 0175 0176 //! Write a short description to an output stream 0177 //! @param os The output stream 0178 //! @tparam Registry The registry 0179 //! @tparam Stream A @ref LightweightOutputStream 0180 template<class Registry, class Stream> 0181 auto write(Stream& os) const; 0182 }; 0183 0184 //! Missing base. 0185 //! 0186 //! A class used in an overrider virtual parameter was not registered as a 0187 //! derived class of the class in the same position in the method's virtual 0188 //! parameter list. 0189 //! 0190 //! @par Example 0191 //! In the following code, OpenMethod cannot infer that `Dog` is derived from 0192 //! `Animal`, because they are not registered in a same call to @ref 0193 //! BOOST_OPENMETHOD_CLASSES. 0194 //! 0195 //! @code 0196 //! BOOST_OPENMETHOD_CLASSES(Animal); 0197 //! BOOST_OPENMETHOD_CLASSES(Dog); 0198 //! 0199 //! BOOST_OPENMETHOD(poke, (virtual_ptr<Animal>), void); 0200 //! 0201 //! BOOST_OPENMETHOD_OVERRIDE(poke, (virtual_ptr<Dog>), void) { /* ... */ } 0202 //! 0203 //! initialize(); // throws missing_base; 0204 //! @endcode 0205 //! 0206 //! Fix: 0207 //! 0208 //! @code 0209 //! BOOST_OPENMETHOD_CLASSES(Animal, Dog); 0210 //! @endcode 0211 struct missing_base : openmethod_error { 0212 //! The type_id of the base class. 0213 type_id base; 0214 //! The type_id of the derived class. 0215 type_id derived; 0216 0217 //! Write a short description to an output stream 0218 //! @param os The output stream 0219 //! @tparam Registry The registry 0220 //! @tparam Stream A @ref LightweightOutputStream 0221 template<class Registry, class Stream> 0222 auto write(Stream& os) const; 0223 }; 0224 0225 //! No valid overrider 0226 struct bad_call : openmethod_error { 0227 //! The type_id of method that was called 0228 type_id method; 0229 //! The number of @em virtual arguments in the call 0230 std::size_t arity; 0231 //! The maximum size of `types` 0232 static constexpr std::size_t max_types = 16; 0233 //! The type_ids of the arguments. 0234 type_id types[max_types]; 0235 }; 0236 0237 //! No overrider for virtual tuple 0238 //! 0239 //! @see @ref bad_call for data members. 0240 struct no_overrider : bad_call { 0241 //! Write a short description to an output stream 0242 //! @param os The output stream 0243 //! @tparam Registry The registry 0244 //! @tparam Stream A @ref LightweightOutputStream 0245 template<class Registry, class Stream> 0246 auto write(Stream& os) const { 0247 os << "not implemented"; 0248 } 0249 }; 0250 0251 //! Ambiguous call 0252 //! 0253 //! @see @ref bad_call for data members. 0254 struct ambiguous_call : bad_call { 0255 //! Write a short description to an output stream 0256 //! @param os The output stream 0257 //! @tparam Registry The registry 0258 //! @tparam Stream A @ref LightweightOutputStream 0259 template<class Registry, class Stream> 0260 auto write(Stream& os) const { 0261 os << "ambiguous"; 0262 } 0263 }; 0264 0265 //! Static and dynamic type mismatch in "final" construct 0266 //! 0267 //! If runtime checks are enabled, the "final" construct checks that the static 0268 //! and dynamic types of the object, as reported by the `rtti` policy, are the 0269 //! same. If they are not, and if the registry contains an @ref error_handler 0270 //! policy, its @ref error function is called with a `final_error` object, then 0271 //! the program is terminated with 0272 //! @ref abort. 0273 struct final_error : openmethod_error { 0274 type_id static_type, dynamic_type; 0275 0276 //! Write a short description to an output stream 0277 //! @param os The output stream 0278 //! @tparam Registry The registry 0279 //! @tparam Stream A @ref LightweightOutputStream 0280 template<class Registry, class Stream> 0281 auto write(Stream& os) const; 0282 }; 0283 0284 namespace detail { 0285 0286 struct empty {}; 0287 0288 template<typename Iterator> 0289 struct range { 0290 range(Iterator first, Iterator last) : first(first), last(last) { 0291 } 0292 0293 Iterator first, last; 0294 0295 auto begin() const -> Iterator { 0296 return first; 0297 } 0298 0299 auto end() const -> Iterator { 0300 return last; 0301 } 0302 }; 0303 0304 // ----------------------------------------------------------------------------- 0305 // class info 0306 0307 struct class_info : static_list<class_info>::static_link { 0308 type_id type; 0309 vptr_type* static_vptr; 0310 type_id *first_base, *last_base; 0311 bool is_abstract{false}; 0312 0313 auto vptr() const { 0314 return static_vptr; 0315 } 0316 0317 auto type_id_begin() const { 0318 return &type; 0319 } 0320 0321 auto type_id_end() const { 0322 return &type + 1; 0323 } 0324 }; 0325 0326 struct deferred_class_info : class_info { 0327 virtual void resolve_type_ids() = 0; 0328 }; 0329 0330 // ----------- 0331 // method info 0332 0333 struct overrider_info; 0334 0335 struct method_info : static_list<method_info>::static_link { 0336 type_id* vp_begin; 0337 type_id* vp_end; 0338 static_list<overrider_info> overriders; 0339 void (*not_implemented)(); 0340 void (*ambiguous)(); 0341 type_id method_type_id; 0342 type_id return_type_id; 0343 std::size_t* slots_strides_ptr; 0344 0345 auto arity() const { 0346 return std::distance(vp_begin, vp_end); 0347 } 0348 }; 0349 0350 struct deferred_method_info : method_info { 0351 virtual void resolve_type_ids() = 0; 0352 }; 0353 0354 struct overrider_info : static_list<overrider_info>::static_link { 0355 ~overrider_info() { 0356 method->overriders.remove(*this); 0357 } 0358 0359 method_info* method; // for the destructor, to remove definition 0360 type_id return_type; // for N2216 disambiguation 0361 type_id type; // of the function, for trace 0362 void (**next)(); 0363 type_id *vp_begin, *vp_end; 0364 void (*pf)(); 0365 }; 0366 0367 struct deferred_overrider_info : overrider_info { 0368 virtual void resolve_type_ids() = 0; 0369 }; 0370 0371 struct unspecified {}; 0372 0373 } // namespace detail 0374 0375 #ifdef __MRDOCS__ 0376 0377 //! Blueprint for a lightweight output stream (exposition only). 0378 //! 0379 //! Classes used as output streams in policies must provide the operations 0380 //! described on this page, either as members or as free functions. 0381 struct LightweightOutputStream { 0382 //! Writes a null-terminated string to the stream. 0383 LightweightOutputStream& operator<<(const char* str); 0384 0385 //! Writes a string view to the stream. 0386 LightweightOutputStream& operator<<(const std::string_view& view); 0387 0388 //! Writes a pointer value to the stream. 0389 LightweightOutputStream& operator<<(const void* value); 0390 0391 //! Writes a size_t value to the stream. 0392 LightweightOutputStream& operator<<(std::size_t value); 0393 }; 0394 0395 #endif 0396 0397 //! N2216 ambiguity resolution. 0398 //! 0399 //! If `n2216` is present in @ref initialize\'s `Options`, additional steps are 0400 //! taken to select a single overrider in presence of ambiguous overriders sets, 0401 //! according to the rules defined in the N2216 paper. If the normal resolution 0402 //! procedure fails to select a single overrider, the following steps are 0403 //! applied, in order: 0404 //! 0405 //! - If the return types of the remaining overriders are all polymorphic and 0406 //! covariant, and one of the return types is more specialized thjat all the 0407 //! others, use it. 0408 //! 0409 //! - Otherwise, pick one of the overriders. Which one is used is unspecified, 0410 //! but remains the same throughtout the program, and across different runs of 0411 //! the same program. 0412 struct n2216 {}; 0413 0414 //! Enable `initialize` tracing. 0415 //! 0416 //! If `trace` is passed to @ref initialize, tracing code is added to various 0417 //! parts of the initialization process (dispatch table construction, hash 0418 //! factors search, etc). The tracing code is executed only if 0419 //! @ref trace::on is set to `true`. 0420 //! 0421 //! `trace` requires the registry being initialized to have an @ref output 0422 //! policy. 0423 //! 0424 //! The content of the trace is neither specified, nor stable across versions. 0425 //! It is comprehensive, and useful for troubleshooting missing class 0426 //! registrations, missing or ambiguous overriders, etc. 0427 struct trace { 0428 //! Enable trace if `true`. 0429 bool on = true; 0430 0431 trace(bool on = true) : on(on) { 0432 } 0433 0434 //! Returns a `trace` object with `on` set to `true` if the environment 0435 //! variable `BOOST_OPENMETHOD_TRACE` is set to the string "1", and false 0436 //! otherwise. 0437 static trace from_env(); 0438 }; 0439 0440 inline trace trace::from_env() { 0441 #ifdef _MSC_VER 0442 char* env; 0443 std::size_t len; 0444 auto result = _dupenv_s(&env, &len, "BOOST_OPENMETHOD_TRACE") == 0 && env && 0445 len == 2 && *env == '1'; 0446 free(env); 0447 return trace(result); 0448 #else 0449 auto env = getenv("BOOST_OPENMETHOD_TRACE"); 0450 return trace(env && *env++ == '1' && *env++ == 0); 0451 #endif 0452 } 0453 0454 //! Namespace for policies. 0455 //! 0456 //! Classes with snake case names are "blueprints", i.e. exposition-only classes 0457 //! that describe the requirements for policies of a given category. Classes 0458 //! implementing these blueprints must provide a `fn<Registry>` metafunction 0459 //! that conforms to the blueprint's requirements. 0460 //! 0461 //! @see @ref registry for a complete explanation of registries and policies. 0462 0463 namespace policies { 0464 0465 #ifdef __MRDOCS__ 0466 0467 //! Class information for initializing a policy (exposition only). 0468 //! 0469 //! Provides the v-table pointer for a class, identified by one or more type 0470 //! ids, via the members described on this page. 0471 struct InitializeClass { 0472 //! Beginning of a range of type ids for a class. 0473 //! 0474 //! @return A forward iterator to the beginning of a range of type ids for 0475 //! a class. 0476 auto type_id_begin() const -> detail::unspecified; 0477 0478 //! End of a range of type ids for a class. 0479 //! 0480 //! @return A forward iterator to the end of a range of type ids for a 0481 //! class. 0482 auto type_id_end() const -> detail::unspecified; 0483 0484 //! Reference to the v-table pointer for the class. 0485 //! 0486 //! @return A reference to the v-table pointer for the class. 0487 auto vptr() const -> const vptr_type&; 0488 }; 0489 0490 //! Context for initializing a policy (exposition only). 0491 //! 0492 //! @ref initialize passes a "context" object, of unspecified type, to the 0493 //! `initialize` functions of the policies that have one. It provides the 0494 //! v-table pointer for the registered classes, via the members described on 0495 //! this page. 0496 struct InitializeContext { 0497 //! Beginning of a range of `InitializeClass` objects. 0498 //! 0499 //! @return A forward iterator to the beginning of a range of @ref 0500 //! InitializeClass objects. 0501 detail::unspecified classes_begin() const; 0502 0503 //! End of a range of `InitializeClass` objects. 0504 //! 0505 //! @return A forward iterator to the end of a range of @ref 0506 //! InitializeClass objects. 0507 detail::unspecified classes_end() const; 0508 }; 0509 0510 //! Blueprint for @ref rtti metafunctions (exposition only). 0511 template<class Registry> 0512 struct RttiFn { 0513 //! Tests if a class is polymorphic. 0514 //! 0515 //! @tparam Class A class. 0516 template<class Class> 0517 static constexpr bool is_polymorphic = std::is_polymorphic_v<Class>; 0518 0519 //! Returns the static @ref type_id of a type. 0520 //! 0521 //! @note `Class` is not necessarily a @e registered class. This 0522 //! function is also called to acquire the type_id of non-virtual 0523 //! parameters, library types, etc, for diagnostic and trace purposes. 0524 //! 0525 //! @tparam Class A class. 0526 //! @return The static type_id of Class. 0527 template<class Class> 0528 static auto static_type() -> type_id; 0529 0530 //! Returns the dynamic @ref type_id of an object. 0531 //! 0532 //! @tparam Class A registered class. 0533 //! @param obj A reference to an instance of `Class`. 0534 //! @return The type_id of `obj`'s class. 0535 template<class Class> 0536 static auto dynamic_type(const Class& obj) -> type_id; 0537 0538 //! Writes a representation of a @ref type_id to a stream. 0539 //! 0540 //! @tparam Stream A LightweightOutputStream. 0541 //! @param type The `type_id` to write. 0542 //! @param stream The stream to write to. 0543 template<typename Stream> 0544 static auto type_name(type_id type, Stream& stream); 0545 0546 //! Returns a key that uniquely identifies a class. 0547 //! 0548 //! @param type A `type_id`. 0549 //! @return A unique value that identifies a class with the given 0550 //! `type_id`. 0551 static auto type_index(type_id type); 0552 0553 //! Casts an object to a type. 0554 //! 0555 //! @tparam D A reference to a subclass of `B`. 0556 //! @tparam B A registered class. 0557 //! @param obj A reference to an instance of `B`. 0558 template<typename D, typename B> 0559 static auto dynamic_cast_ref(B&& obj) -> D; 0560 }; 0561 0562 #endif 0563 0564 //! Policy for manipulating type information. 0565 //! 0566 //! `rtti` policies are responsible for type information acquisition and dynamic 0567 //! casting. 0568 //! 0569 //! @par Requirements 0570 //! 0571 //! Classes implementing this policy must: 0572 //! @li derive from `rtti`. 0573 //! @li provide a `fn<Registry>` metafunction that conforms to the @ref RttiFn 0574 //! blueprint. 0575 struct rtti { 0576 // Policy category. 0577 using category = rtti; 0578 0579 //! Default implementations of some `rtti` requirements. 0580 struct defaults { 0581 //! Default implementation for `type_index`. 0582 //! 0583 //! @param type A `type_id`. 0584 //! 0585 //! @return `type` itself. 0586 static auto type_index(type_id type) -> type_id { 0587 return type; 0588 } 0589 0590 //! Default implementation of `type_name`. 0591 //! 0592 //! Executes `stream << "type_id(" << type << ")"`. 0593 //! 0594 //! @param type A `type_id`. 0595 //! @param stream A stream to write to. 0596 template<typename Stream> 0597 static void type_name(type_id type, Stream& stream) { 0598 stream << "type_id(" << type << ")"; 0599 } 0600 }; 0601 }; 0602 0603 //! Policy for deferred type id collection. 0604 //! 0605 //! Some custom RTTI systems rely on static constructors to assign type ids. 0606 //! OpenMethod itself relies on static constructors to register classes, methods 0607 //! and overriders. This creates order-of-initialization issues. Deriving a @e 0608 //! rtti policy from this class - instead of just `rtti` - causes the collection 0609 //! of type ids to be deferred until the first call to @ref update. 0610 struct deferred_static_rtti : rtti {}; 0611 0612 #ifdef __MRDOCS__ 0613 //! Blueprint for @ref error_handler metafunctions (exposition only). 0614 template<class Registry> 0615 struct ErrorHandlerFn { 0616 //! Called when an error is detected. 0617 //! 0618 //! `error` is a function, or a set of functions, that can be called 0619 //! with an instance of any subclass of `openmethod_error`. 0620 static auto error(const auto& error) -> void; 0621 }; 0622 #endif 0623 0624 //! Policy for error handling. 0625 //! 0626 //! A @e error_handler policy runs code before the library terminates the 0627 //! program due to an error. This can be useful for throwing, logging, cleanup, 0628 //! or other actions. 0629 //! 0630 //! @par Requirements 0631 //! 0632 //! Classes implementing this policy must: 0633 //! @li derive from `error_handler`. 0634 //! @li provide a `fn<Registry>` metafunction that conforms to the @ref 0635 //! ErrorHandlerFn blueprint. 0636 struct error_handler { 0637 // Policy category. 0638 using category = error_handler; 0639 }; 0640 0641 #ifdef __MRDOCS__ 0642 0643 //! Blueprint for `vptr` metafunctions (exposition only). 0644 //! 0645 //! @tparam Registry The registry containing the policy. 0646 template<class Registry> 0647 struct VptrFn { 0648 //! Register the v-table pointers. 0649 //! 0650 //! Called by @ref registry::initialize to let the policy store the v-table 0651 //! pointer associated to each `type_id`. 0652 //! 0653 //! @tparam Context A class that conforms to the @ref InitializeContext 0654 //! blueprint. 0655 template<class Context> 0656 static auto initialize(const Context& ctx) -> void; 0657 0658 //! Return a *reference* to a v-table pointer for an object. 0659 //! 0660 //! @tparam Class A registered class. 0661 //! @param arg A reference to a const object of type `Class`. 0662 //! @return A reference to a the v-table pointer for `Class`. 0663 template<class Class> 0664 static auto dynamic_vptr(const Class& arg) -> const vptr_type&; 0665 0666 //! Release the resources allocated by `initialize`. 0667 //! 0668 //! This function is optional. 0669 //! 0670 //! @tparam Options... Zero or more option types, deduced from the 0671 //! function arguments. 0672 //! @param options A tuple of option objects. 0673 template<class... Options> 0674 static auto finalize(const std::tuple<Options...>& options) -> void; 0675 }; 0676 0677 #endif 0678 0679 //! Policy for v-table pointer acquisition. 0680 //! 0681 //! @par Requirements 0682 //! 0683 //! Classes implementing this policy must: 0684 //! @li derive from `vptr`. 0685 //! @li provide a `fn<Registry>` metafunction that conforms to the @ref 0686 //! VptrFn blueprint. 0687 struct vptr { 0688 // Policy category. 0689 using category = vptr; 0690 }; 0691 0692 //! Policy to add an indirection to pointers to v-tables. 0693 //! 0694 //! If this policy is present, constructs like @ref virtual_ptr, @ref 0695 //! inplace_vptr, @ref vptr_vector, etc use pointers to pointers to v-tables. 0696 //! These indirect pointers remain valid after a call to @ref initialize, after 0697 //! dynamically loading a library that adds classes, methods and overriders to 0698 //! the registry. 0699 struct indirect_vptr final { 0700 // Policy category. 0701 using category = indirect_vptr; 0702 template<class Registry> 0703 struct fn {}; 0704 }; 0705 0706 #ifdef __MRDOCS__ 0707 //! Blueprint for @ref type_hash metafunctions (exposition only). 0708 //! 0709 //! @tparam Registry The registry containing the policy. 0710 template<class Registry> 0711 struct TypeHashFn { 0712 //! Initialize the hash table. 0713 //! 0714 //! @tparam Context A class that conforms to the @ref InitializeContext 0715 //! blueprint. 0716 //! @return A pair containing the minimum and maximum hash values. 0717 template<class Context> 0718 static auto 0719 initialize(const Context& ctx) -> std::pair<std::size_t, std::size_t>; 0720 0721 //! Hash a `type_id`. 0722 //! 0723 //! @param type A @ref type_id. 0724 //! @return A hash value for the given `type_id`. 0725 static auto hash(type_id type) -> std::size_t; 0726 0727 //! Release the resources allocated by `initialize`. 0728 //! 0729 //! This function is optional. 0730 //! 0731 //! @tparam Options... Zero or more option types, deduced from the 0732 //! function arguments. 0733 //! @param options A tuple of option objects. 0734 template<class... Options> 0735 static auto finalize(const std::tuple<Options...>& options) -> void; 0736 }; 0737 #endif 0738 0739 //! Policy for hashing type ids. 0740 //! 0741 //! @par Requirements 0742 //! 0743 //! Classes implementing this policy must: 0744 //! @li derive from `rtti`. 0745 //! @li provide a `fn<Registry>` metafunction that conforms to the @ref 0746 //! TypeHashFn blueprint. 0747 struct type_hash { 0748 // Policy category. 0749 using category = type_hash; 0750 }; 0751 0752 #ifdef __MRDOCS__ 0753 0754 //! Blueprint for @ref output metafunctions (exposition only). 0755 //! 0756 //! @tparam Registry The registry containing the policy. 0757 template<class Registry> 0758 struct OutputFn { 0759 //! A @ref LightweightOutputStream. 0760 inline static LightweightOutputStream os; 0761 }; 0762 0763 #endif 0764 0765 //! Policy for writing diagnostics and trace. 0766 //! 0767 //! If an `output` policy is present, the default error handler uses it to write 0768 //! error messages to its output stream. @ref registry::initialize can also use 0769 //! it to write trace messages. 0770 //! 0771 //! @par Requirements 0772 //! 0773 //! Classes implementing this policy must: 0774 //! @li derive from `output`. 0775 //! @li provide a `fn<Registry>` metafunction that conforms to the @ref 0776 //! OutputFn blueprint. 0777 struct output { 0778 // Policy category. 0779 using category = output; 0780 }; 0781 0782 //! Policy for post-initialize runtime checks. 0783 //! 0784 //! If this policy is present, performs the following checks: 0785 //! @li Classes of virtual arguments have been registered. 0786 //! @li Dynamic and static types match in "final" constructs (@ref 0787 //! final_virtual_ptr and related functions). 0788 struct runtime_checks final { 0789 // Policy category. 0790 using category = runtime_checks; 0791 template<class Registry> 0792 struct fn {}; 0793 }; 0794 0795 } // namespace policies 0796 0797 namespace detail { 0798 0799 struct registry_base {}; 0800 0801 template<typename T> 0802 constexpr bool is_registry = std::is_base_of_v<registry_base, T>; 0803 0804 template<typename T> 0805 constexpr bool is_not_void = !std::is_same_v<T, void>; 0806 0807 template< 0808 class Registry, class Index, 0809 class Size = mp11::mp_size<typename Registry::policy_list>> 0810 struct get_policy_aux { 0811 using type = typename mp11::mp_at< 0812 typename Registry::policy_list, Index>::template fn<Registry>; 0813 }; 0814 0815 template<class Registry, class Size> 0816 struct get_policy_aux<Registry, Size, Size> { 0817 using type = void; 0818 }; 0819 0820 using class_catalog = detail::static_list<detail::class_info>; 0821 using method_catalog = detail::static_list<detail::method_info>; 0822 0823 template<class Policies, class...> 0824 struct with_aux; 0825 0826 template<class Policies> 0827 struct with_aux<Policies> { 0828 using type = Policies; 0829 }; 0830 0831 template<class Policies, class Policy, class... MorePolicies> 0832 struct with_aux<Policies, Policy, MorePolicies...> { 0833 using replace = mp11::mp_replace_if_q< 0834 Policies, 0835 mp11::mp_bind_front_q< 0836 mp11::mp_quote_trait<std::is_base_of>, typename Policy::category>, 0837 Policy>; 0838 using replace_or_add = std::conditional_t< 0839 std::is_same_v<replace, Policies>, mp11::mp_push_back<Policies, Policy>, 0840 replace>; 0841 using type = typename with_aux<replace_or_add, MorePolicies...>::type; 0842 }; 0843 0844 template<class Policies, class...> 0845 struct without_aux; 0846 0847 template<class Policies> 0848 struct without_aux<Policies> { 0849 using type = Policies; 0850 }; 0851 0852 template<class Policies, class Policy, class... MorePolicies> 0853 struct without_aux<Policies, Policy, MorePolicies...> { 0854 using type = typename without_aux< 0855 mp11::mp_remove_if_q< 0856 Policies, 0857 mp11::mp_bind_front_q< 0858 mp11::mp_quote_trait<std::is_base_of>, 0859 typename Policy::category>>, 0860 MorePolicies...>::type; 0861 }; 0862 0863 template<class...> 0864 struct use_class_aux; 0865 0866 template<typename, class...> 0867 struct initialize_aux; 0868 0869 } // namespace detail 0870 0871 //! Methods, classes and policies. 0872 //! 0873 //! Methods exist in the context of a registry. Any class used as a method or 0874 //! overrider parameter, or in as a method call argument, must be registered 0875 //! with the same registry. 0876 //! 0877 //! Before calling a method, its registry must be initialized with the @ref 0878 //! initialize function. This is typically done at the beginning of `main`. 0879 //! 0880 //! Multiple registries can co-exist in the same program. They must be 0881 //! initialized individually. Classes referenced by methods in different 0882 //! registries must be registered with each registry. 0883 //! 0884 //! A registry also contains a set of @ref policies that control how certain 0885 //! operations are performed. For example, the `rtti` policy provides type 0886 //! information, implements dynamic casting, etc. It can be replaced to 0887 //! interface with custom RTII systems (like LLVM's). 0888 //! 0889 //! Policies are implemented as Boost.MP11 quoted metafunctions. A policy class 0890 //! must contain a `fn<Registry>` template that provides a set of static 0891 //! members, specific to the responsibility of the policy. Registries 0892 //! instantiate policies by passing themselves to the nested `fn` class 0893 //! templates. 0894 //! 0895 //! There are two reason for this design. 0896 //! 0897 //! Some policies are "stateful": they contain static _data_ members. Since 0898 //! several registries can co-exist in the same program, each stateful policy 0899 //! needs its own, separate set of static data members. For example, @ref 0900 //! vptr_vector, a "vptr" policy, contains a static vector of vptrs, which 0901 //! cannot be shared with other registries. 0902 //! 0903 //! Also, some policies need access to other policies in the same registry. They 0904 //! can be accessed via the `Registry` template parameter. For example, @ref 0905 //! vptr_vector hashes type_ids before using them as an indexes, if `Registry` 0906 //! cotains a `type_hash` policy. It performs out-of-bounds checks if `Registry` 0907 //! contains the `runtime_checks` policy. If an error is detected, it invokes 0908 //! the @ref error_handler policy if there is one. 0909 //! 0910 //! @tparam Policy The policies used in the registry. 0911 //! 0912 //! @par Requirements 0913 //! 0914 //! @li `Policy` must contain a `category` alias to its root base class. The 0915 //! registry may contain at most one policy per category. 0916 //! 0917 //! @li `Policy` must contain a `fn<Registry>` metafunction. 0918 //! 0919 //! @see @ref policies 0920 template<class... Policy> 0921 class registry : detail::registry_base { 0922 static detail::class_catalog classes; 0923 static detail::method_catalog methods; 0924 0925 template<class...> 0926 friend struct detail::use_class_aux; 0927 template<typename Name, typename ReturnType, class Registry> 0928 friend class method; 0929 0930 static std::vector<detail::word> dispatch_data; 0931 static bool initialized; 0932 0933 public: 0934 //! The type of this registry. 0935 using registry_type = registry; 0936 0937 template<class... Options> 0938 struct compiler; 0939 0940 //! Check that the registry is initialized. 0941 //! 0942 //! Check if `initialize` has been called for this registry, and report an 0943 //! error if not. 0944 //! 0945 //! @par Errors 0946 //! 0947 //! @li @ref not_initialized: The registry is not initialized. 0948 static void require_initialized(); 0949 0950 template<class... Options> 0951 static void finalize(Options... opts); 0952 0953 //! A pointer to the virtual table for a registered class. 0954 //! 0955 //! `static_vptr` is set by @ref registry::initialize to the address of the 0956 //! class' virtual table. It remains valid until the next call to 0957 //! `initialize` or `finalize`. 0958 //! 0959 //! @tparam Class A registered class. 0960 template<class Class> 0961 static vptr_type static_vptr; 0962 0963 //! List of policies selected in a registry. 0964 //! 0965 //! `policy_list` is a Boost.Mp11 list containing the policies passed to the 0966 //! @ref registry clas template. 0967 //! 0968 //! @tparam Class A registered class. 0969 using policy_list = mp11::mp_list<Policy...>; 0970 0971 //! Find a policy by category. 0972 //! 0973 //! `policy` searches for a policy that derives from the specified @ref 0974 //! Category. If none is found, it aliases to `void`. Otherwise, it aliases 0975 //! to the policy's `fn` metafunction, applied to the registry. 0976 //! 0977 //! @tparam A policy. 0978 template<class Category> 0979 using policy = typename detail::get_policy_aux< 0980 registry, 0981 mp11::mp_find_if_q< 0982 policy_list, 0983 mp11::mp_bind_front_q< 0984 mp11::mp_quote_trait<std::is_base_of>, Category>>>::type; 0985 0986 //! Add or replace policies. 0987 //! 0988 //! `with` aliases to a registry with additional policies, overwriting any 0989 //! existing policies in the same category as the new ones. 0990 //! 0991 //! @tparam NewPolicies Models of @ref policies::Policy. 0992 template<class... NewPolicies> 0993 using with = boost::mp11::mp_apply< 0994 registry, typename detail::with_aux<policy_list, NewPolicies...>::type>; 0995 0996 //! Remove policies. 0997 //! 0998 //! `without` aliases to a registry containing the same policies, except those 0999 //! that derive from `Categories`. 1000 //! 1001 //! @tparam Categories Models of @ref policies::PolicyCategory. 1002 template<class... Categories> 1003 using without = boost::mp11::mp_apply< 1004 registry, 1005 typename detail::without_aux<policy_list, Categories...>::type>; 1006 1007 //! The registry's rtti policy. 1008 using rtti = policy<policies::rtti>; 1009 1010 //! The registry's vptr policy if it contains one, or `void`. 1011 using vptr = policy<policies::vptr>; 1012 1013 //! `true` if the registry has a vptr policy. 1014 static constexpr auto has_vptr = !std::is_same_v<vptr, void>; 1015 1016 //! The registry's error_handler policy if it contains one, or `void`. 1017 using error_handler = policy<policies::error_handler>; 1018 1019 //! `true` if the registry has an error_handler policy. 1020 static constexpr auto has_error_handler = 1021 !std::is_same_v<error_handler, void>; 1022 1023 //! The registry's output policy if it contains one, or `void`. 1024 using output = policy<policies::output>; 1025 1026 //! `true` if the registry has an output policy. 1027 static constexpr auto has_output = !std::is_same_v<output, void>; 1028 1029 //! `true` if the registry has a deferred_static_rtti policy. 1030 static constexpr auto has_deferred_static_rtti = 1031 !std::is_same_v<policy<policies::deferred_static_rtti>, void>; 1032 1033 //! `true` if the registry has a runtime_checks policy. 1034 static constexpr auto has_runtime_checks = 1035 !std::is_same_v<policy<policies::runtime_checks>, void>; 1036 1037 //! `true` if the registry has an indirect_vptr policy. 1038 static constexpr auto has_indirect_vptr = 1039 !std::is_same_v<policy<policies::indirect_vptr>, void>; 1040 }; 1041 1042 template<class... Policies> 1043 detail::class_catalog registry<Policies...>::classes; 1044 1045 template<class... Policies> 1046 detail::method_catalog registry<Policies...>::methods; 1047 1048 template<class... Policies> 1049 std::vector<detail::word> registry<Policies...>::dispatch_data; 1050 1051 template<class... Policies> 1052 bool registry<Policies...>::initialized; 1053 1054 template<class... Policies> 1055 template<class Class> 1056 vptr_type registry<Policies...>::static_vptr; 1057 1058 template<class... Policies> 1059 void registry<Policies...>::require_initialized() { 1060 if constexpr (registry::has_runtime_checks) { 1061 if (!initialized) { 1062 if constexpr (registry::has_error_handler) { 1063 error_handler::error(not_initialized()); 1064 } 1065 1066 abort(); 1067 } 1068 } 1069 } 1070 1071 template<class Registry, class Stream> 1072 auto missing_class::write(Stream& os) const { 1073 os << "unknown class "; 1074 Registry::rtti::type_name(type, os); 1075 } 1076 1077 template<class Registry, class Stream> 1078 auto missing_base::write(Stream& os) const { 1079 os << "missing base "; 1080 Registry::rtti::type_name(base, os); 1081 os << " -<| "; 1082 Registry::rtti::type_name(derived, os); 1083 } 1084 1085 template<class Registry, class Stream> 1086 auto final_error::write(Stream& os) const { 1087 os << "invalid call to final construct: static type = "; 1088 Registry::rtti::type_name(static_type, os); 1089 os << ", dynamic type = "; 1090 Registry::rtti::type_name(dynamic_type, os); 1091 } 1092 1093 } // namespace boost::openmethod 1094 1095 #ifdef _MSC_VER 1096 #pragma warning(pop) 1097 #endif 1098 1099 #endif // BOOST_OPENMETHOD_REGISTRY_HPP
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|