| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878 |
- // Copyright (c) 2018-2025 Jean-Louis Leroy
- // Distributed under the Boost Software License, Version 1.0.
- // See accompanying file LICENSE_1_0.txt
- // or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_OPENMETHOD_CORE_HPP
- #define BOOST_OPENMETHOD_CORE_HPP
- #include <stdint.h>
- #include <algorithm>
- #include <cstdlib>
- #include <tuple>
- #include <type_traits>
- #include <utility>
- #include <boost/assert.hpp>
- #include <boost/config.hpp>
- #include <boost/mp11/algorithm.hpp>
- #include <boost/mp11/bind.hpp>
- #include <boost/mp11/integral.hpp>
- #include <boost/mp11/list.hpp>
- #include <boost/openmethod/preamble.hpp>
- #include <boost/openmethod/default_registry.hpp>
- #ifndef BOOST_OPENMETHOD_DEFAULT_REGISTRY
- #define BOOST_OPENMETHOD_DEFAULT_REGISTRY ::boost::openmethod::default_registry
- #endif
- #ifdef _MSC_VER
- #pragma warning(push)
- #pragma warning(disable : 4100)
- #pragma warning(disable : 4646)
- #pragma warning(disable : 4702) // unreachable code
- #endif
- //! Top namespace of the library.
- namespace boost::openmethod {
- #ifdef __MRDOCS__
- #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
- #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
- #define BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- #else
- #define BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS namespace detail {
- #define BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS }
- #define BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS detail::
- #endif
- namespace detail {
- using sfinae = void;
- }
- template<
- class Class, class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY,
- typename = detail::sfinae>
- class virtual_ptr;
- // =============================================================================
- // Helpers
- namespace detail {
- using macro_default_registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY;
- template<typename...>
- struct extract_registry;
- template<>
- struct extract_registry<> {
- using registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY;
- using others = mp11::mp_list<>;
- };
- template<typename Type>
- struct extract_registry<Type> {
- using registry = std::conditional_t<
- is_registry<Type>, Type, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
- using others = std::conditional_t<
- is_registry<Type>, mp11::mp_list<>, mp11::mp_list<Type>>;
- };
- template<typename Type1, typename Type2, typename... MoreTypes>
- struct extract_registry<Type1, Type2, MoreTypes...> {
- static_assert(!is_registry<Type1>, "policy must be the last in the list");
- using registry = typename extract_registry<Type2, MoreTypes...>::registry;
- using others = mp11::mp_push_front<
- typename extract_registry<Type2, MoreTypes...>::others, Type1>;
- };
- template<class Registry, class... Class>
- struct init_type_ids;
- template<class Registry, class... Class>
- struct init_type_ids<Registry, mp11::mp_list<Class...>> {
- static auto fn(type_id* ids) {
- (..., (*ids++ = Registry::rtti::template static_type<Class>()));
- return ids;
- }
- };
- template<class Base, class Derived>
- struct is_unambiguous_accessible_base_of : std::is_base_of<Base, Derived> {
- static_assert(
- std::is_base_of_v<Base, Derived> ==
- std::is_convertible_v<Derived&, Base&>,
- "class must be an accessible unambiguous base, repeated inheritance is "
- "not "
- "supported");
- };
- // Collect the base classes of a list of classes. The result is a mp11 map that
- // associates each class to a list starting with the class itself, followed by
- // all its bases, as per std::is_base_of. Thus the list includes the class
- // itself at least twice: at the front, and down the list, as its own improper
- // base. The direct and indirect bases are all included. The runtime will
- // extract the direct proper bases.
- template<typename... Cs>
- using inheritance_map = mp11::mp_list<boost::mp11::mp_push_front<
- boost::mp11::mp_filter_q<
- boost::mp11::mp_bind_back<is_unambiguous_accessible_base_of, Cs>,
- mp11::mp_list<Cs...>>,
- Cs>...>;
- // =============================================================================
- // optimal_cast
- template<typename B, typename D, typename = void>
- struct requires_dynamic_cast_ref_aux : std::true_type {};
- template<typename B, typename D>
- struct requires_dynamic_cast_ref_aux<
- B, D, std::void_t<decltype(static_cast<D>(std::declval<B>()))>>
- : std::false_type {};
- template<class B, class D>
- constexpr bool requires_dynamic_cast =
- detail::requires_dynamic_cast_ref_aux<B, D>::value;
- template<class Registry, class D, class B>
- auto optimal_cast(B&& obj) -> decltype(auto) {
- if constexpr (requires_dynamic_cast<B, D>) {
- return Registry::rtti::template dynamic_cast_ref<D>(
- std::forward<B>(obj));
- } else {
- return static_cast<D>(obj);
- }
- }
- // =============================================================================
- // Common details
- template<typename T>
- struct is_virtual : std::false_type {};
- template<typename T>
- struct is_virtual<virtual_<T>> : std::true_type {};
- template<typename T>
- struct remove_virtual_aux {
- using type = T;
- };
- template<typename T>
- struct remove_virtual_aux<virtual_<T>> {
- using type = T;
- };
- template<typename T>
- using remove_virtual_ = typename remove_virtual_aux<T>::type;
- template<typename T, class Registry, typename = void>
- struct virtual_type_aux {
- using type = void;
- };
- template<typename T, class Registry>
- struct virtual_type_aux<
- T, Registry,
- std::void_t<typename virtual_traits<T, Registry>::virtual_type>> {
- using type = typename virtual_traits<T, Registry>::virtual_type;
- };
- template<typename T, class Registry>
- using virtual_type = typename virtual_type_aux<T, Registry>::type;
- template<typename MethodArgList>
- using virtual_types = boost::mp11::mp_transform<
- remove_virtual_, boost::mp11::mp_filter<detail::is_virtual, MethodArgList>>;
- } // namespace detail
- BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
- //! Removes the virtual_<> decorator, if present (exposition only).
- //!
- //! Provides a nested `type` equal to `T`. The template is specialized for
- //! `virtual_<T>`.
- //!
- //! @tparam T A type.
- template<typename T>
- struct StripVirtualDecorator {
- //! Same as `T`
- using type = T;
- };
- //! Removes the virtual_<> decorator (exposition only).
- //!
- //! Provides a nested `type` equal to `T`.
- //!
- //! @tparam T A type.
- template<typename T>
- struct StripVirtualDecorator<virtual_<T>> {
- //! Same as `T`.
- using type = T;
- };
- BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
- // =============================================================================
- // virtual_traits
- //! Traits for types used as virtual parameters.
- //!
- //! `virtual_traits` must be specialized for each type that can be used as a
- //! virtual parameters. It enables methods to:
- //! @li find the type of the object the argument refers to (e.g. `Node` from
- //! `Node&`)
- //! @li obtain a non-modifiable reference to that object (e.g. a `const Node&` from
- //! `Node&`)
- //! @li cast the argument to another type (e.g. cast a `Node&` to a `Plus&`)
- //!
- //! @par Requirements
- //!
- //! Specializations of `virtual_traits` must provide the members described to
- //! the @ref VirtualTraits blueprint.
- //!
- //! @tparam T A type referring (in the broad sense) to an instance of a class.
- //! @tparam Registry A @ref registry.
- template<typename T, class Registry>
- struct virtual_traits;
- //! Specialize virtual_traits for lvalue reference types.
- //!
- //! @tparam Class A class type, possibly cv-qualified.
- //! @tparam Registry A @ref registry.
- template<class Class, class Registry>
- struct virtual_traits<Class&, Registry> {
- //! `Class`, stripped from cv-qualifiers.
- using virtual_type = std::remove_cv_t<Class>;
- //! Return a reference to a non-modifiable `Class` object.
- //! @param arg A reference to a non-modifiable `Class` object.
- //! @return A reference to the same object.
- static auto peek(const Class& arg) -> const Class& {
- return arg;
- }
- //! Cast to another type.
- //!
- //! Cast an object to another type. If possible, use `static_cast`.
- //! Otherwise, use `Registry::rtti::dynamic_cast_ref`.
- //!
- //! @tparam Derived A lvalue reference type.
- //! @param obj A reference to a `Class` object.
- //! @return A reference to the same object, cast to `Derived`.
- template<typename Derived>
- static auto cast(Class& obj) -> Derived {
- static_assert(std::is_lvalue_reference_v<Derived>);
- return detail::optimal_cast<Registry, Derived>(obj);
- }
- };
- //! Specialize virtual_traits for xvalue reference types.
- //!
- //! @tparam T A xvalue reference type.
- //! @tparam Registry A @ref registry.
- template<class Class, class Registry>
- struct virtual_traits<Class&&, Registry> {
- //! Same as `Class`.
- using virtual_type = Class;
- //! Return a reference to a non-modifiable `Class` object.
- //! @param arg A reference to a non-modifiable `Class` object.
- //! @return A reference to the same object.
- static auto peek(const Class& arg) -> const Class& {
- return arg;
- }
- //! Cast to another type.
- //!
- //! Cast an object to another type. If possible, use `static_cast`.
- //! Otherwise, use `Registry::rtti::dynamic_cast_ref`.
- //!
- //! @tparam Derived A rvalue reference type.
- //! @param obj A reference to a `Class` object.
- //! @return A reference to the same object, cast to `Derived`.
- template<typename Derived>
- static auto cast(Class&& obj) -> Derived {
- static_assert(std::is_rvalue_reference_v<Derived>);
- return detail::optimal_cast<Registry, Derived>(obj);
- }
- };
- //! Specialize virtual_traits for pointer types.
- //!
- //! @tparam Class A class type, possibly cv-qualified.
- //! @tparam Registry A @ref registry.
- template<class Class, class Registry>
- struct virtual_traits<Class*, Registry> {
- //! `Class`, stripped from cv-qualifiers.
- using virtual_type = std::remove_cv_t<Class>;
- //! Return a reference to a non-modifiable `Class` object.
- //! @param arg A pointer to a non-modifiable `Class` object.
- //! @return A const reference to the same object.
- static auto peek(const Class* arg) -> const Class& {
- return *arg;
- }
- //! Cast to another type.
- //!
- //! Cast an object to another type. If possible, use `static_cast`.
- //! Otherwise, use `Registry::rtti::dynamic_cast_ref`.
- //!
- //! @tparam Derived A pointer type.
- //! @param obj A pointer to a `Class` object.
- //! @return A pointer to the same object, cast to `Derived`.
- template<typename Derived>
- static auto cast(Class* ptr) -> Derived {
- static_assert(std::is_pointer_v<Derived>);
- if constexpr (detail::requires_dynamic_cast<Class*, Derived>) {
- return dynamic_cast<Derived>(ptr);
- } else {
- return static_cast<Derived>(ptr);
- }
- }
- };
- namespace detail {
- template<class...>
- struct use_class_aux;
- template<class Registry, class Class, typename... Bases>
- struct use_class_aux<Registry, mp11::mp_list<Class, Bases...>>
- : std::conditional_t<
- Registry::has_deferred_static_rtti, detail::deferred_class_info,
- detail::class_info> {
- static type_id bases[sizeof...(Bases)];
- use_class_aux() {
- this->first_base = bases;
- this->last_base = bases + sizeof...(Bases);
- this->is_abstract = std::is_abstract_v<Class>;
- this->static_vptr = &Registry::template static_vptr<Class>;
- if constexpr (!Registry::has_deferred_static_rtti) {
- resolve_type_ids();
- }
- Registry::classes.push_back(*this);
- }
- void resolve_type_ids() {
- this->type = Registry::rtti::template static_type<Class>();
- auto iter = bases;
- (..., (*iter++ = Registry::rtti::template static_type<Bases>()));
- }
- ~use_class_aux() {
- Registry::classes.remove(*this);
- }
- };
- template<class Registry, class Class, typename... Bases>
- type_id use_class_aux<
- Registry, mp11::mp_list<Class, Bases...>>::bases[sizeof...(Bases)];
- template<class... Classes>
- using use_classes_tuple_type = boost::mp11::mp_apply<
- std::tuple,
- boost::mp11::mp_transform_q<
- boost::mp11::mp_bind_front<
- detail::use_class_aux,
- typename detail::extract_registry<Classes...>::registry>,
- boost::mp11::mp_apply<
- detail::inheritance_map,
- typename detail::extract_registry<Classes...>::others>>>;
- } // namespace detail
- //! Add classes to a registry
- //!
- //! `use_classes` is a registrar class that adds one or more classes to a
- //! registry.
- //!
- //! Classes potentially involved in a method definition, an overrider, or a
- //! method call must be registered via `use_classes`. A class may be registered
- //! multiple times. A class and its direct bases must be listed together in one
- //! or more instantiations of `use_classes`.
- //!
- //! If a class is identified by different type ids in different translation
- //! units, it must be registered in as many translation units as necessary for
- //! `use_classes` to register all the type ids. This situation can occur when
- //! using standard RTTI, because the address of the `type_info` objects are used
- //! as type ids, and the standard does not guarantee that there is exactly one
- //! such object per class. The only such case known to the author is when using
- //! Windows DLLs.
- //!
- //! Virtual and multiple inheritance are supported, with the exclusion of
- //! repeated inheritance.
- template<class... Classes>
- class use_classes {
- detail::use_classes_tuple_type<Classes...> tuple;
- };
- // =============================================================================
- // virtual_ptr
- namespace detail {
- void boost_openmethod_vptr(...);
- template<typename, class, typename = void>
- struct is_smart_ptr_aux : std::false_type {};
- template<typename Class, class Registry>
- struct is_smart_ptr_aux<
- Class, Registry,
- std::void_t<
- typename virtual_traits<Class, Registry>::template rebind<Class>>>
- : std::true_type {};
- template<class Class, class Other, class Registry, typename = void>
- struct same_smart_ptr_aux : std::false_type {};
- template<class Class, class Other, class Registry>
- struct same_smart_ptr_aux<
- Class, Other, Registry,
- std::void_t<typename virtual_traits<Class, Registry>::template rebind<
- typename Other::element_type>>>
- : std::is_same<
- Other,
- typename virtual_traits<Class, Registry>::template rebind<
- typename Other::element_type>> {};
- } // namespace detail
- BOOST_OPENMETHOD_OPEN_NAMESPACE_DETAIL_UNLESS_MRDOCS
- //! Test if argument is polymorphic (exposition only)
- //!
- //! Evaluates to `true` if `Class` is a polymorphic type, according to the
- //! `rtti` policy of `Registry`.
- //!
- //! If Registry's `rtti` policy is std_rtti`, this is the same as
- //! `std::is_polymorphic`. However, other `rtti` policies may have a different
- //! view of what is polymorphic.
- //!
- //! @tparam Class A class type.
- //! @tparam Registry A registry.
- template<class Class, class Registry>
- constexpr bool IsPolymorphic = Registry::rtti::template is_polymorphic<Class>;
- //! Test if argument is a smart pointer (exposition only)
- //!
- //! Evaluates to `true` if `Class` is a smart pointer type, and false otherwise.
- //! `Class` is considered a smart pointer if `virtual_traits<Class, Registry>`
- //! exists and it defines a nested template `rebind<T>` that can be instantiated
- //! with `Class`.
- //!
- //! @tparam Class A class type.
- //! @tparam Registry A registry.
- template<typename Class, class Registry>
- constexpr bool IsSmartPtr = detail::is_smart_ptr_aux<Class, Registry>::value;
- //! Test if arguments are same kind of smart pointers (exposition only)
- //!
- //! Evaluates to `true` if `Class` and `Other` are both smart pointers of the
- //! same type.
- //!
- //! @tparam Class A class type.
- //! @tparam Other Another class type.
- //! @tparam Registry A registry.
- template<class Class, class Other, class Registry>
- constexpr bool SameSmartPtr =
- detail::same_smart_ptr_aux<Class, Other, Registry>::value;
- BOOST_OPENMETHOD_CLOSE_NAMESPACE_DETAIL_UNLESS_MRDOCS
- template<class Registry, typename Arg>
- inline auto final_virtual_ptr(Arg&& obj);
- namespace detail {
- template<class Class, class Registry>
- struct is_virtual<virtual_ptr<Class, Registry, void>> : std::true_type {};
- template<class Class, class Registry>
- struct is_virtual<virtual_ptr<Class, Registry, void>&> : std::true_type {};
- template<class Class, class Registry>
- struct is_virtual<const virtual_ptr<Class, Registry, void>&> : std::true_type {
- };
- template<typename>
- struct is_virtual_ptr_aux : std::false_type {};
- template<class Class, class Registry>
- struct is_virtual_ptr_aux<virtual_ptr<Class, Registry, void>> : std::true_type {
- };
- template<class Class, class Registry>
- struct is_virtual_ptr_aux<const virtual_ptr<Class, Registry, void>&>
- : std::true_type {};
- template<typename T>
- constexpr bool is_virtual_ptr = detail::is_virtual_ptr_aux<T>::value;
- template<class Class, class Registry>
- constexpr bool has_vptr_fn = std::is_same_v<
- decltype(boost_openmethod_vptr(
- std::declval<const Class&>(), std::declval<Registry*>())),
- vptr_type>;
- template<class Registry, class ArgType>
- decltype(auto) acquire_vptr(const ArgType& arg) {
- Registry::require_initialized();
- if constexpr (detail::has_vptr_fn<ArgType, Registry>) {
- return boost_openmethod_vptr(arg, static_cast<Registry*>(nullptr));
- } else {
- return Registry::template policy<policies::vptr>::dynamic_vptr(arg);
- }
- }
- template<bool Indirect>
- inline auto box_vptr(const vptr_type& vp) {
- if constexpr (Indirect) {
- return &vp;
- } else {
- return vp;
- }
- }
- inline auto unbox_vptr(vptr_type vp) {
- return vp;
- }
- inline auto unbox_vptr(const vptr_type* vpp) {
- return *vpp;
- }
- inline vptr_type null_vptr = nullptr;
- } // namespace detail
- //! Creates a `virtual_ptr` for an object of a known dynamic type.
- //!
- //! Creates a @ref virtual_ptr to an object, setting its v-table pointer
- //! according to the declared type of its argument. Assumes that the static and
- //! dynamic types are the same. Sets the v-table pointer to the
- //! @ref registry::static_vptr for the class.
- //!
- //! `Class` is _not_ required to be polymorphic.
- //!
- //! If runtime checks are enabled, and the argument is polymorphic, checks if
- //! the static and dynamic types are the same. If not, calls the error handler
- //! with a @ref final_error value, then terminates the program with @ref abort.
- //!
- //! @par Errors
- //!
- //! @li @ref final_error The static and dynamic types of the object are
- //! different.
- //!
- //! @tparam Registry A @ref registry.
- //! @tparam Arg The type of the argument.
- //! @param obj A reference to an object.
- //! @return A `virtual_ptr<Class, Registry>` pointing to `obj`.
- template<class Registry, typename Arg>
- inline auto final_virtual_ptr(Arg&& obj) {
- using namespace detail;
- using VirtualPtr = virtual_ptr<std::remove_reference_t<Arg>, Registry>;
- using Traits = virtual_traits<Arg, Registry>;
- using Class = typename Traits::virtual_type;
- static_assert(!std::is_const_v<Class>);
- static_assert(!std::is_volatile_v<Class>);
- static_assert(!std::is_reference_v<Class>);
- static_assert(!std::is_pointer_v<Class>);
- Registry::require_initialized();
- if constexpr (
- Registry::has_runtime_checks &&
- Registry::rtti::template is_polymorphic<Class>) {
- // check that dynamic type == static type
- auto static_type = Registry::rtti::template static_type<Class>();
- auto dynamic_type = Registry::rtti::dynamic_type(Traits::peek(obj));
- if (dynamic_type != static_type) {
- if constexpr (is_not_void<typename Registry::error_handler>) {
- final_error error;
- error.static_type = static_type;
- error.dynamic_type = dynamic_type;
- Registry::error_handler::error(error);
- }
- abort();
- }
- }
- const vptr_type& vptr = Registry::template static_vptr<Class>;
- BOOST_ASSERT(vptr);
- return VirtualPtr(
- std::forward<Arg>(obj),
- detail::box_vptr<VirtualPtr::use_indirect_vptrs>(vptr));
- }
- //! Create a `virtual_ptr` for an object of a known dynamic type.
- //!
- //! This is an overload of `final_virtual_ptr` that uses the default
- //! registry as the `Registry` template parameter.
- //!
- //! @see @ref final_virtual_ptr
- // We could give a default value to Registry in the main template, but gcc
- // doesn't like it.
- template<class Arg>
- inline auto final_virtual_ptr(Arg&& obj) {
- return final_virtual_ptr<BOOST_OPENMETHOD_DEFAULT_REGISTRY, Arg>(
- std::forward<Arg>(obj));
- }
- //! Wide pointer combining pointers to an object and its v-table
- //!
- //! A `virtual_ptr` is a wide pointer that combines pointers to an object and
- //! its v-table. Calls to methods via `virtual_ptr` are as fast as ordinary
- //! virtual function calls (typically two instructions).
- //!
- //! A `virtual_ptr` can be implicitly constructed from a reference, a pointer,
- //! or another `virtual_ptr`, provided that they are type-compatible.
- //!
- //! `virtual_ptr` has specializations that use a `std::shared_ptr` or a
- //! `std::unique_ptr` as the pointer to the object. The mechanism can be
- //! extended to other smart pointers by specializing @ref virtual_traits. A
- //! "plain" `virtual_ptr` can be constructed from a smart `virtual_ptr`, but not
- //! the other way around.
- //!
- //! The default value for `Registry` can be customized by defining the
- //! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}
- //! preprocessor symbol.
- //!
- //! @par Requirements
- //!
- //! @li @ref virtual_traits must be specialized for `Class&`.
- //! @li `Class` must be a class type, possibly cv-qualified, registered in
- //! `Registry`.
- //!
- //! @tparam Class The class of the object, possibly cv-qualified
- //! @tparam Registry The registry in which `Class` is registered
- //! @tparam unnamed Implementation defined, use default
- template<class Class, class Registry, typename>
- class virtual_ptr {
- using traits = virtual_traits<Class&, Registry>;
- #ifndef __MRDOCS__
- template<class, class, typename>
- friend class virtual_ptr;
- template<class, typename Arg>
- friend auto final_virtual_ptr(Arg&& obj);
- #endif
- static constexpr bool is_smart_ptr = false;
- static constexpr bool use_indirect_vptrs = Registry::has_indirect_vptr;
- std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type> vp;
- Class* obj;
- template<
- class Other,
- typename = std::enable_if_t<std::is_constructible_v<Class*, Other*>>>
- virtual_ptr(Other& other, decltype(vp) vp) : vp(vp), obj(&other) {
- }
- public:
- //! Class
- //!
- //! This is the same as `Class`.
- using element_type = Class;
- //! Default constructor
- //!
- //! @note This constructor does nothing. The state of the two pointers
- //! inside the object is as specified for uninitialized variables by C++.
- virtual_ptr() = default;
- //! Construct from `nullptr`
- //!
- //! Set both object and v-table pointers to `nullptr`.
- //!
- //! @param value A `nullptr`.
- //!
- //! @par Example
- //!
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<Dog> p{nullptr};
- //! BOOST_TEST(p.get() == nullptr);
- //! BOOST_TEST(p.vptr() == nullptr);
- //! @endcode
- //!
- //! @param value A `nullptr`.
- explicit virtual_ptr(std::nullptr_t)
- : vp(detail::box_vptr<use_indirect_vptrs>(detail::null_vptr)),
- obj(nullptr) {
- }
- //! Construct a `virtual_ptr` from a reference to an object
- //!
- //! The pointer to the v-table is obtained by calling
- //! @ref boost_openmethod_vptr if a suitable overload exists, or the
- //! @ref policies::vptr::fn::dynamic_vptr of the registry's
- //! `vptr` policy otherwise.
- //!
- //! @param other A reference to a polymorphic object
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! Dog snoopy;
- //! Animal& animal = snoopy;
- //!
- //! virtual_ptr<Animal> p = animal;
- //!
- //! BOOST_TEST(p.get() == &snoopy);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `Other` must be a polymorphic class, according to `Registry`'s
- //! `rtti` policy.
- //! @li `Other\*` must be constructible from `Class\*`.
- //!
- //! @par Errors
- //!
- //! The following errors may occur, depending on the policies selected in
- //! `Registry`:
- //!
- //! @li @ref missing_class
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- IsPolymorphic<Other, Registry> &&
- std::is_constructible_v<Class*, Other*>>>
- virtual_ptr(Other& other)
- : vp(detail::box_vptr<use_indirect_vptrs>(
- detail::acquire_vptr<Registry>(other))),
- obj(&other) {
- }
- //! Construct a `virtual_ptr` from a pointer to an object
- //!
- //! The pointer to the v-table is obtained by calling
- //! @ref boost_openmethod_vptr if a suitable overload exists, or the
- //! @ref policies::vptr::fn::dynamic_vptr of the registry's
- //! `vptr` policy otherwise.
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! Dog snoopy;
- //! Animal* animal = &snoopy;
- //!
- //! virtual_ptr<Animal> p = animal;
- //!
- //! BOOST_TEST(p.get() == &snoopy);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @param other A pointer to a polymorphic object
- //!
- //! @par Requirements
- //!
- //! @li `Other` must be a polymorphic class, according to `Registry`'s
- //! `rtti` policy.
- //!
- //! @li `Other\*` must be constructible from `Class\*`.
- //!
- //! @par Errors
- //!
- //! The following errors may occur, depending on the policies selected in
- //! `Registry`:
- //!
- //! @li @ref missing_class
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- IsPolymorphic<Class, Registry> &&
- std::is_constructible_v<Class*, Other*>>>
- virtual_ptr(Other* other)
- : vp(detail::box_vptr<use_indirect_vptrs>(
- detail::acquire_vptr<Registry>(*other))),
- obj(other) {
- }
- //! Construct a `virtual_ptr` from another `virtual_ptr`
- //!
- //! Copy the object and v-table pointers from `other` to `this.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Examples
- //!
- //! Assigning from a plain virtual_ptr:
- //!
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! Dog snoopy;
- //! virtual_ptr<Dog> dog = final_virtual_ptr(snoopy);
- //! virtual_ptr<Animal> p{nullptr};
- //!
- //! p = dog;
- //!
- //! BOOST_TEST(p.get() == &snoopy);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! Assigning from a smart virtual_ptr:
- //!
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Animal>> snoopy = make_shared_virtual<Dog>();
- //! virtual_ptr<Animal> p = snoopy;
- //!
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! No construction of a smart `virtual_ptr` from a plain `virtual_ptr`:
- //!
- //! @code
- //! static_assert(
- //! std::is_constructible_v<
- //! shared_virtual_ptr<Animal>, virtual_ptr<Dog>> == false);
- //! @endcode
- //!
- //! @param other A virtual_ptr to a type-compatible object
- //!
- //! @par Requirements
- //! @li `Other`\'s object pointer must be assignable to a `Class\*`.
- template<
- class Other,
- typename = std::enable_if_t<std::is_constructible_v<
- Class*, typename virtual_ptr<Other, Registry>::element_type*>>>
- virtual_ptr(const virtual_ptr<Other, Registry>& other)
- : vp(other.vp), obj(other.get()) {
- }
- //! Assign a `virtual_ptr` from a reference to an object
- //!
- //! The pointer to the v-table is obtained by calling
- //! @ref boost_openmethod_vptr if a suitable overload exists, or the
- //! @ref policies::vptr::fn::dynamic_vptr of the registry's
- //! `vptr` policy otherwise.
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<Animal> p{nullptr};
- //! Dog snoopy;
- //! Animal& animal = snoopy;
- //!
- //! p = animal;
- //!
- //! BOOST_TEST(p.get() == &snoopy);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @param other A reference to a polymorphic object
- //!
- //! @par Requirements
- //!
- //! @li `Other` must be a polymorphic class, according to `Registry`'s
- //! `rtti` policy.
- //!
- //! @li `Other\*` must be constructible from `Class\*`.
- //!
- //! @par Errors
- //!
- //! The following errors may occur, depending on the policies selected in
- //! `Registry`:
- //!
- //! @li @ref missing_class
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- IsPolymorphic<Class, Registry> &&
- std::is_assignable_v<Class*&, Other*>>>
- virtual_ptr& operator=(Other& other) {
- obj = &other;
- vp = detail::box_vptr<use_indirect_vptrs>(
- detail::acquire_vptr<Registry>(other));
- return *this;
- }
- //! Assign a `virtual_ptr` from a pointer to an object
- //!
- //! The pointer to the v-table is obtained by calling
- //! @ref boost_openmethod_vptr if a suitable overload exists, or the
- //! @ref policies::vptr::fn::dynamic_vptr of the registry's
- //! `vptr` policy otherwise.
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<Animal> p{nullptr};
- //! Dog snoopy;
- //! Animal* animal = &snoopy;
- //!
- //! p = animal;
- //!
- //! BOOST_TEST(p.get() == &snoopy);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @param other A pointer to a polymorphic object
- //!
- //! @par Requirements
- //! @li `Other` must be a polymorphic class, according to `Registry`'s
- //! `rtti` policy.
- //! @li `Other\*` must be constructible from `Class\*`.
- //!
- //! @par Errors
- //!
- //! The following errors may occur, depending on the policies selected in
- //! `Registry`:
- //!
- //! @li @ref missing_class
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- IsPolymorphic<Class, Registry> &&
- std::is_assignable_v<Class*&, Other*>>>
- virtual_ptr& operator=(Other* other) {
- obj = other;
- vp = detail::box_vptr<use_indirect_vptrs>(
- detail::acquire_vptr<Registry>(*other));
- return *this;
- }
- //! Assign a `virtual_ptr` from another `virtual_ptr`
- //!
- //! Copy the object and v-table pointers from `other` to `this.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Examples
- //!
- //! Assigning from a plain virtual_ptr:
- //!
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! Dog snoopy;
- //! virtual_ptr<Dog> dog = final_virtual_ptr(snoopy);
- //! virtual_ptr<Animal> p{nullptr};
- //!
- //! p = dog;
- //!
- //! BOOST_TEST(p.get() == &snoopy);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! Assigning from a smart virtual_ptr:
- //!
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Animal>> snoopy = make_shared_virtual<Dog>();
- //! virtual_ptr<Animal> p;
- //!
- //! p = snoopy;
- //!
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! No assignment from a plain `virtual_ptr` to a smart `virtual_ptr`:
- //!
- //! @code
- //! static_assert(
- //! std::is_assignable_v<
- //! shared_virtual_ptr<Animal>&, virtual_ptr<Dog>> == false);
- //! @endcode
- //!
- //! @param other A virtual_ptr to a type-compatible object
- //!
- //! @par Requirements
- //! @li `Other`\'s object pointer must be assignable to a `Class\*`.
- template<
- class Other,
- typename = std::enable_if_t<std::is_assignable_v<
- Class*&, typename virtual_ptr<Other, Registry>::element_type*>>>
- virtual_ptr& operator=(const virtual_ptr<Other, Registry>& other) {
- obj = other.get();
- vp = other.vp;
- return *this;
- }
- //! Set a `virtual_ptr` to `nullptr`
- //!
- //! Set both object and v-table pointers to `nullptr`.
- //!
- //! @par Example
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! Dog snoopy;
- //! virtual_ptr<Animal> p = final_virtual_ptr(snoopy);
- //!
- //! p = nullptr;
- //!
- //! BOOST_TEST(p.get() == nullptr);
- //! BOOST_TEST(p.vptr() == nullptr);
- //! //! @code
- //! @endcode
- virtual_ptr& operator=(std::nullptr_t) {
- obj = nullptr;
- vp = detail::box_vptr<use_indirect_vptrs>(detail::null_vptr);
- return *this;
- }
- //! Get a pointer to the object
- //!
- //! @return A pointer to the object
- auto get() const -> Class* {
- return obj;
- }
- //! Get a pointer to the object
- //!
- //! @return A pointer to the object
- auto operator->() const {
- return get();
- }
- //! Get a reference to the object
- //!
- //! @return A reference to the object
- auto operator*() const -> element_type& {
- return *get();
- }
- //! Get a pointer to the object
- //!
- //! @return A pointer to the object
- auto pointer() const -> element_type* {
- return obj;
- }
- //! Cast to another `virtual_ptr` type
- //!
- //! @par Example
- //! @code
- //! @endcode
- //!
- //! @tparam Other The target class of the cast
- //! @return A `virtual_ptr<Other, Registry>` pointing to the same object
- //! @par Requirements
- //! @li `Other` must be a base or derived class of `Class`.
- template<
- class Other,
- typename = std::enable_if_t<
- std::is_base_of_v<element_type, Other> ||
- std::is_base_of_v<Other, element_type>>>
- auto cast() const -> decltype(auto) {
- return virtual_ptr<Other, Registry>(
- traits::template cast<Other&>(*obj), vp);
- }
- //! Construct a `virtual_ptr` from a reference to an object
- //!
- //! This function forwards to @ref final_virtual_ptr.
- //!
- //! @tparam Other The type of the argument
- //! @param obj A reference to an object
- //! @return A `virtual_ptr<Class, Registry>` pointing to `obj`
- template<class Other>
- static auto final(Other&& obj) {
- return final_virtual_ptr<Registry>(std::forward<Other>(obj));
- }
- //! Get the v-table pointer
- //! @return The v-table pointer
- auto vptr() const {
- return detail::unbox_vptr(this->vp);
- }
- };
- //! Wide pointer combining a smart pointer to an object and a pointer to its
- //! v-table
- //!
- //! This specialization of `virtual_ptr` uses a smart pointer to track the
- //! object, instead of a plain pointer.
- //!
- //! @tparam SmartPtr A smart pointer type
- //! @tparam Registry The registry in which the underlying class is registered
- template<class SmartPtr, class Registry>
- class virtual_ptr<
- SmartPtr, Registry,
- std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS IsSmartPtr<SmartPtr, Registry>>> {
- #ifndef __MRDOCS__
- template<class, class, typename>
- friend class virtual_ptr;
- template<class, typename Arg>
- friend auto final_virtual_ptr(Arg&& obj);
- #endif
- static constexpr bool is_smart_ptr = true;
- static constexpr bool use_indirect_vptrs = Registry::has_indirect_vptr;
- using traits = virtual_traits<SmartPtr, Registry>;
- std::conditional_t<use_indirect_vptrs, const vptr_type*, vptr_type> vp;
- SmartPtr obj;
- template<
- class Other,
- typename = std::enable_if_t<std::is_constructible_v<SmartPtr*, Other*>>>
- virtual_ptr(Other& other, decltype(vp) vp) : vp(vp), obj(&other) {
- }
- template<typename Arg>
- virtual_ptr(Arg&& obj, decltype(vp) vp)
- : vp(vp), obj(std::forward<Arg>(obj)) {
- }
- public:
- //! Class pointed to by SmartPtr
- using element_type = typename SmartPtr::element_type;
- //! Default constructor
- //!
- //! Construct the object pointer using its default constructor. Set the
- //! v-table pointer to `nullptr`.
- //!
- //! @par Example
- //! @code
- //! struct Dog {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Dog>> p;
- //! BOOST_TEST(p.get() == nullptr);
- //! BOOST_TEST(p.vptr() == nullptr);
- //! @par Example
- //! @endcode
- virtual_ptr()
- : vp(detail::box_vptr<use_indirect_vptrs>(detail::null_vptr)) {
- }
- //! Construct from `nullptr`
- //!
- //! Construct the object pointer using its default constructor. Set the
- //! v-table pointer to `nullptr`.
- //!
- //! @par Example
- //! @code
- //! struct Dog {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Dog>> p{nullptr};
- //! BOOST_TEST(p.get() == nullptr);
- //! BOOST_TEST(p.vptr() == nullptr);
- //! @endcode
- //!
- //! @param value A `nullptr`.
- explicit virtual_ptr(std::nullptr_t)
- : vp(detail::box_vptr<use_indirect_vptrs>(detail::null_vptr)) {
- }
- virtual_ptr(const virtual_ptr& other) = default;
- virtual_ptr(virtual_ptr&& other)
- : vp(std::exchange(
- other.vp,
- detail::box_vptr<use_indirect_vptrs>(detail::null_vptr))),
- obj(std::move(other.obj)) {
- }
- #ifdef __MRDOCS__
- //! Construct from a (const) smart pointer to a derived class
- //!
- //! Set the object pointer with a copy of `other`. Set the v-table pointer
- //! according to the dynamic type of `*other`.
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! const std::shared_ptr<Dog> snoopy = std::make_shared<Dog>();
- //! virtual_ptr<std::shared_ptr<Animal>> p = snoopy;
- //!
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a polymorphic class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `const Other&`.
- template<
- class Other,
- typename = std::enable_if_t<
- SameSmartPtr<SmartPtr, Other, Registry> &&
- IsPolymorphic<typename Other::element_type, Registry> &&
- std::is_constructible_v<SmartPtr, const Other&>>>
- #else
- template<
- class Other,
- typename = std::enable_if_t<
- detail::SameSmartPtr<SmartPtr, Other, Registry> &&
- detail::IsPolymorphic<typename Other::element_type, Registry> &&
- std::is_constructible_v<SmartPtr, const Other&>>>
- #endif
- virtual_ptr(const Other& other)
- : vp(detail::box_vptr<use_indirect_vptrs>(
- other ? detail::acquire_vptr<Registry>(*other)
- : detail::null_vptr)),
- obj(other) {
- }
- #if __MRDOCS__
- //! Construct from a smart pointer to a derived class
- //!
- //! Copy object pointer from `other` to `this`. Set the v-table pointer
- //! according to the dynamic type of `*other`.
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! std::shared_ptr<Dog> snoopy = std::make_shared<Dog>();
- //! virtual_ptr<std::shared_ptr<Animal>> p = snoopy;
- //!
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a polymorphic class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&`.
- template<
- class Other,
- typename = std::enable_if_t<
- SameSmartPtr<SmartPtr, Other, Registry> &&
- IsPolymorphic<typename Other::element_type, Registry> &&
- std::is_constructible_v<SmartPtr, Other&>>>
- #else
- template<
- class Other,
- typename = std::enable_if_t<
- detail::SameSmartPtr<SmartPtr, Other, Registry> &&
- detail::IsPolymorphic<typename Other::element_type, Registry> &&
- std::is_constructible_v<SmartPtr, Other&>>>
- #endif
- virtual_ptr(Other& other)
- : vp(detail::box_vptr<use_indirect_vptrs>(
- other ? detail::acquire_vptr<Registry>(*other)
- : detail::null_vptr)),
- obj(other) {
- }
- #ifdef __MRDOCS__
- //! Move-construct from a smart pointer to a derived class
- //!
- //! Move object pointer from `other` to `this`. Set the v-table pointer
- //! according to the dynamic type of `*other`.
- //!
- //! @par Example
- //! @code
- //! struct Animal { virtual ~Animal() { } }; // polymorphic
- //! struct Dog : Animal {}; // polymorphic
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! std::shared_ptr<Dog> snoopy = std::make_shared<Dog>();
- //! Dog* moving = snoopy.get();
- //!
- //! virtual_ptr<std::shared_ptr<Animal>> p = std::move(snoopy);
- //!
- //! BOOST_TEST(p.get() == moving);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.get() == nullptr);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a polymorphic class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&&`.
- template<
- class Other,
- typename = std::enable_if_t<
- SameSmartPtr<SmartPtr, Other, Registry> &&
- IsPolymorphic<typename Other::element_type, Registry> &&
- std::is_constructible_v<SmartPtr, Other&&>>>
- #else
- template<
- class Other,
- typename = std::enable_if_t<
- detail::SameSmartPtr<SmartPtr, Other, Registry> &&
- detail::IsPolymorphic<typename Other::element_type, Registry> &&
- std::is_constructible_v<SmartPtr, Other&&>>>
- #endif
- virtual_ptr(Other&& other)
- : vp(detail::box_vptr<use_indirect_vptrs>(
- other ? detail::acquire_vptr<Registry>(*other)
- : detail::null_vptr)),
- obj(std::move(other)) {
- }
- //! Construct from a smart virtual (const) pointer to a derived class
- //!
- //! Copy the object and v-table pointers from `other`.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Example
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! const virtual_ptr<std::shared_ptr<Dog>> snoopy = make_shared_virtual<Dog>();
- //! virtual_ptr<std::shared_ptr<Animal>> p = snoopy;
- //!
- //! BOOST_TEST(snoopy.get() != nullptr);
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a virtual pointer to a class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_constructible_v<SmartPtr, const Other&>>>
- virtual_ptr(const virtual_ptr<Other, Registry>& other)
- : vp(other.vp), obj(other.obj) {
- }
- //! Construct-move from a virtual pointer to a derived class
- //!
- //! Move the object pointer from `other` to `this`. Copy the v-table pointer
- //! from `other`.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Example
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Dog>> snoopy = make_shared_virtual<Dog>();
- //! Dog* dog = snoopy.get();
- //!
- //! virtual_ptr<std::shared_ptr<Animal>> p = std::move(snoopy);
- //!
- //! BOOST_TEST(p.get() == dog);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.get() == nullptr);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_constructible_v<SmartPtr, Other&&>>>
- virtual_ptr(virtual_ptr<Other, Registry>&& other)
- : vp(std::exchange(
- other.vp,
- detail::box_vptr<use_indirect_vptrs>(detail::null_vptr))),
- obj(std::move(other.obj)) {
- }
- //! Assign from `nullptr`
- //!
- //! Reset the object pointer using its default constructor. Set the
- //! v-table pointer to `nullptr`.
- //!
- //! @par Example
- //! @code
- //! struct Dog {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Dog>> p = make_shared_virtual<Dog>();
- //!
- //! p = nullptr;
- //!
- //! BOOST_TEST(p.get() == nullptr);
- //! BOOST_TEST(p.vptr() == nullptr);
- //! BOOST_TEST((p == virtual_ptr<std::shared_ptr<Dog>>()));
- //! @endcode
- //!
- //! @param value A `nullptr`.
- virtual_ptr& operator=(std::nullptr_t) {
- obj = SmartPtr();
- vp = detail::box_vptr<use_indirect_vptrs>(detail::null_vptr);
- return *this;
- }
- //! Assign from a (const) smart pointer to a derived class
- //!
- //! Copy the object pointer from `other` to `this`. Set the v-table pointer
- //! according to the dynamic type of `*other`.
- //!
- //! @par Example
- //! @code
- //! virtual_ptr<std::shared_ptr<Dog>> snoopy = make_shared_virtual<Dog>();
- //! virtual_ptr<std::shared_ptr<Dog>> p;
- //!
- //! p = snoopy;
- //!
- //! BOOST_TEST(p.get() != nullptr);
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a polymorphic class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `const Other&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_assignable_v<SmartPtr, const Other&> &&
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- IsPolymorphic<typename Other::element_type, Registry>>>
- virtual_ptr& operator=(const Other& other) {
- obj = other;
- vp = detail::box_vptr<use_indirect_vptrs>(
- detail::acquire_vptr<Registry>(*other));
- return *this;
- }
- //! Move-assign from a smart pointer to a derived class
- //!
- //! Move object pointer from `other` to `this`. Set the v-table pointer
- //! according to the dynamic type of `*other`.
- //!
- //! @par Example
- //! @code
- //! virtual_ptr<std::shared_ptr<Dog>> snoopy = make_shared_virtual<Dog>();
- //! Dog* moving = snoopy.get();
- //! virtual_ptr<std::shared_ptr<Dog>> p;
- //!
- //! p = std::move(snoopy);
- //!
- //! BOOST_TEST(p.get() == moving);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.get() == nullptr);
- //! BOOST_TEST(snoopy.vptr() == nullptr);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a polymorphic class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_assignable_v<SmartPtr, Other&&> &&
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- IsPolymorphic<typename Other::element_type, Registry>>>
- virtual_ptr& operator=(Other&& other) {
- vp = detail::box_vptr<use_indirect_vptrs>(
- other ? detail::acquire_vptr<Registry>(*other) : detail::null_vptr);
- obj = std::move(other);
- return *this;
- }
- //! Assign from a smart virtual pointer to a derived class
- //!
- //! Copy the object and v-table pointers from `other` to `this`.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Example
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Dog>> snoopy = make_shared_virtual<Dog>();
- //! virtual_ptr<std::shared_ptr<Dog>> p;
- //!
- //! p = snoopy;
- //!
- //! BOOST_TEST(p.get() != nullptr);
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a virtual pointer to a class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_assignable_v<SmartPtr, Other&>>>
- virtual_ptr& operator=(virtual_ptr<Other, Registry>& other) {
- obj = other.obj;
- vp = other.vp;
- return *this;
- }
- virtual_ptr& operator=(const virtual_ptr& other) = default;
- //! Assign from a smart virtual const pointer to a derived class
- //!
- //! Copy the object and v-table pointers from `other` to `this`.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Example
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! const virtual_ptr<std::shared_ptr<Dog>> snoopy = make_shared_virtual<Dog>();
- //! virtual_ptr<std::shared_ptr<Dog>> p;
- //!
- //! p = snoopy;
- //!
- //! BOOST_TEST(p.get() != nullptr);
- //! BOOST_TEST(p.get() == snoopy.get());
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.vptr() == default_registry::static_vptr<Dog>);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a virtual pointer to a class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_assignable_v<SmartPtr, const Other&>>>
- virtual_ptr& operator=(const virtual_ptr<Other, Registry>& other) {
- obj = other.obj;
- vp = other.vp;
- return *this;
- }
- //! Move from a virtual pointer to a derived class
- //!
- //! Move the object pointer from `other` to `this`. Copy the v-table pointer
- //! from `other`.
- //!
- //! `Other` is _not_ required to be a pointer to a polymorphic class.
- //!
- //! @par Example
- //! @code
- //! struct Animal {}; // polymorphism not required
- //! struct Dog : Animal {}; // polymorphism not required
- //! BOOST_OPENMETHOD_CLASSES(Animal, Dog);
- //! initialize();
- //!
- //! virtual_ptr<std::shared_ptr<Dog>> snoopy =
- //! make_shared_virtual<Dog>();
- //! Dog* moving = snoopy.get();
- //! virtual_ptr<std::shared_ptr<Dog>> p;
- //!
- //! p = std::move(snoopy);
- //!
- //! BOOST_TEST(p.get() == moving);
- //! BOOST_TEST(p.vptr() == default_registry::static_vptr<Dog>);
- //! BOOST_TEST(snoopy.get() == nullptr);
- //! BOOST_TEST(snoopy.vptr() == nullptr);
- //! @endcode
- //!
- //! @par Requirements
- //! @li `SmartPtr` and `Other` must be instantiated from the same template -
- //! e.g. both `std::shared_ptr` or both `std::unique_ptr`.
- //! @li `Other` must be a smart pointer to a class derived from
- //! `element_type`.
- //! @li `SmartPtr` must be constructible from `Other&&`.
- template<
- class Other,
- typename = std::enable_if_t<
- BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- SameSmartPtr<SmartPtr, Other, Registry> &&
- std::is_assignable_v<SmartPtr, Other&&>>>
- virtual_ptr& operator=(virtual_ptr<Other, Registry>&& other) {
- vp = std::exchange(
- other.vp, detail::box_vptr<use_indirect_vptrs>(detail::null_vptr));
- obj = std::move(other.obj);
- return *this;
- }
- //! Get a pointer to the object
- //!
- //! @return A *plain* pointer to the object
- auto get() const -> element_type* {
- return obj.get();
- }
- //! Get a pointer to the object
- //!
- //! @return A *plain* pointer to the object
- auto operator->() const -> element_type* {
- return get();
- }
- //! Get a reference to the object
- //!
- //! @return A reference to the object
- auto operator*() const -> element_type& {
- return *get();
- }
- //! Get a smart pointer to the object
- //!
- //! @return A const reference to the object pointer
- auto pointer() const -> const SmartPtr& {
- return obj;
- }
- //! Cast to another `virtual_ptr` type
- //! @tparam Other The target class of the cast
- //! @return A `virtual_ptr<Other, Registry>` pointing to the same object
- //! @par Requirements
- //! @li `Other` must be a base or a derived class of `Class`.
- template<
- class Other,
- typename = std::enable_if_t<
- std::is_base_of_v<element_type, Other> ||
- std::is_base_of_v<Other, element_type>>>
- auto cast() & -> decltype(auto) {
- using other_smart_ptr = typename traits::template rebind<Other>;
- return virtual_ptr<other_smart_ptr, Registry>(
- traits::template cast<other_smart_ptr>(obj), vp);
- }
- template<
- class Other,
- typename = std::enable_if_t<
- std::is_base_of_v<element_type, Other> ||
- std::is_base_of_v<Other, element_type>>>
- auto cast() const& -> decltype(auto) {
- using other_smart_ptr = typename traits::template rebind<Other>;
- return virtual_ptr<other_smart_ptr, Registry>(
- traits::template cast<other_smart_ptr>(obj), vp);
- }
- template<class Other>
- auto cast() && -> decltype(auto) {
- static_assert(
- std::is_base_of_v<element_type, Other> ||
- std::is_base_of_v<Other, element_type>);
- using other_smart_ptr = typename traits::template rebind<Other>;
- return virtual_ptr<other_smart_ptr, Registry>(
- traits::template cast<other_smart_ptr>(std::move(obj)), vp);
- }
- //! Construct a `virtual_ptr` from a smart pointer to an object
- //!
- //! This function forwards to @ref final_virtual_ptr.
- //!
- //! @tparam Other The type of the argument
- //! @param obj A reference to an object
- //! @return A `virtual_ptr<Class, Registry>` pointing to `obj`
- template<class Other>
- static auto final(Other&& obj) {
- return final_virtual_ptr<Registry>(std::forward<Other>(obj));
- }
- //! Get the v-table pointer
- //! @return The v-table pointer
- auto vptr() const {
- return detail::unbox_vptr(this->vp);
- }
- };
- //! Construct a `virtual_ptr` from a lvalue reference.
- //!
- //! @tparam Class A class type, possibly cv-qualified.
- //! @param obj A lvalue reference to an object.
- //! @return A `virtual_ptr<Class>`.
- template<class Class>
- virtual_ptr(Class& obj)
- -> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
- //! Construct a `virtual_ptr` from a xvalue reference.
- //!
- //! @tparam Class A class type.
- //! @param obj A xvalue reference to an object.
- //! @return A `virtual_ptr<Class>`.
- template<class Class>
- virtual_ptr(Class&& obj)
- -> virtual_ptr<Class, BOOST_OPENMETHOD_DEFAULT_REGISTRY>;
- // Alas this is not allowed:
- // template<class Registry, class Class>
- // virtual_ptr<Registry>(Class&) -> virtual_ptr<Class, Registry>;
- //! Compare two `virtual_ptr`s for equality.
- //!
- //! Compare the underlying object pointers for equality. The v-table pointers
- //! are not compared.
- //!!
- //! @tparam Left The type of the left-hand side argument.
- //! @tparam Right The type of the right-hand side argument.
- //! @tparam Registry A @ref registry.
- //! @param left A reference to a `virtual_ptr`.
- //! @param right A reference to a `virtual_ptr`.
- //! @return `true` if both `virtual_ptr`s point to the same object or both
- //! are `nullptr`, `false` otherwise.
- template<class Left, class Right, class Registry>
- auto operator==(
- const virtual_ptr<Left, Registry>& left,
- const virtual_ptr<Right, Registry>& right) -> bool {
- return left.pointer() == right.pointer();
- }
- //! Compare two `virtual_ptr`s for inequality.
- //!
- //! Compare the underlying object pointers for inequality. The v-table pointers
- //! are not compared.
- //!! @tparam Left The type of the left-hand side argument.
- //! @tparam Right The type of the right-hand side argument.
- //! @tparam Registry A @ref registry.
- //! @param left A reference to a `virtual_ptr`.
- //! @param right A reference to a `virtual_ptr`.
- //! @return `true` if both `virtual_ptr`s point to different objects, or one
- //! is `nullptr` and the other is not, `false` otherwise.
- template<class Left, class Right, class Registry>
- auto operator!=(
- const virtual_ptr<Left, Registry>& left,
- const virtual_ptr<Right, Registry>& right) -> bool {
- return !(left == right);
- }
- //! Specialize virtual_traits for `virtual_ptr`.
- //!
- //! Specialize virtual_traits for `virtual_ptr`\'s passed by value.
- //!
- //! @tparam Class A class type, possibly cv-qualified.
- //! @tparam Registry A @ref registry.
- template<class Class, class Registry>
- struct virtual_traits<virtual_ptr<Class, Registry>, Registry> {
- //! `Class`, stripped from cv-qualifiers.
- using virtual_type =
- std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;
- //! Return a reference to a non-modifiable `Class` object.
- //! @param arg A reference to a non-modifiable `Class` object.
- //! @return A reference to the same object.
- static auto peek(const virtual_ptr<Class, Registry>& ptr)
- -> const virtual_ptr<Class, Registry>& {
- return ptr;
- }
- //! Cast to another type.
- //!
- //! Cast a `virtual_ptr` to another type, using its `cast` member function.
- //!
- //! @param obj A lvalue reference to a `virtual_ptr`.
- //! @return A lvalue reference to a `virtual_ptr` to the same object, cast
- //! to `Derived::element_type`.
- template<typename Derived>
- static auto
- cast(const virtual_ptr<Class, Registry>& ptr) -> decltype(auto) {
- return ptr.template cast<typename Derived::element_type>();
- }
- //! Cast to another type.
- //!
- //! Cast a `virtual_ptr` to another type, using its `cast` member function.
- //!
- //! @param obj A xvalue reference to a `virtual_ptr`.
- //! @return A xvalue reference to a `virtual_ptr` to the same object, cast
- //! to `Derived::element_type`.
- template<typename Derived>
- static auto cast(virtual_ptr<Class, Registry>&& ptr) -> decltype(auto) {
- return std::move(ptr).template cast<typename Derived::element_type>();
- }
- };
- //! Specialize virtual_traits for `virtual_ptr`.
- //!
- //! Specialize virtual_traits for `virtual_ptr`\'s passed by const reference.
- //!
- //! @tparam Class A class type, possibly cv-qualified.
- //! @tparam Registry A @ref registry.
- template<class Class, class Registry>
- struct virtual_traits<const virtual_ptr<Class, Registry>&, Registry> {
- //! `Class`, stripped from cv-qualifiers.
- using virtual_type =
- std::remove_cv_t<typename virtual_ptr<Class, Registry>::element_type>;
- //! Return a reference to a non-modifiable `Class` object.
- //! @param arg A reference to a non-modifiable `Class` object.
- //! @return A reference to the same object.
- static auto peek(const virtual_ptr<Class, Registry>& ptr)
- -> const virtual_ptr<Class, Registry>& {
- return ptr;
- }
- //! Cast to another type.
- //!
- //! Cast a `virtual_ptr` to another type, using its `cast` member function.
- //!
- //! @param obj A lvalue reference to a `virtual_ptr`.
- //! @return A lvalue reference to a `virtual_ptr` to the same object, cast
- //! to `Derived::element_type`.
- template<typename Derived>
- static auto
- cast(const virtual_ptr<Class, Registry>& ptr) -> decltype(auto) {
- return ptr.template cast<
- typename std::remove_reference_t<Derived>::element_type>();
- }
- };
- // =============================================================================
- // Method
- namespace detail {
- template<typename P, typename Q, class Registry>
- struct select_overrider_virtual_type_aux {
- using type = void;
- };
- template<typename P, typename Q, class Registry>
- struct select_overrider_virtual_type_aux<virtual_<P>, Q, Registry> {
- using type = virtual_type<Q, Registry>;
- };
- template<typename P, typename Q, class Registry>
- struct select_overrider_virtual_type_aux<
- virtual_ptr<P, Registry>, virtual_ptr<Q, Registry>, Registry> {
- using type = typename virtual_traits<
- virtual_ptr<Q, Registry>, Registry>::virtual_type;
- };
- template<typename P, typename Q, class Registry>
- struct select_overrider_virtual_type_aux<
- const virtual_ptr<P, Registry>&, const virtual_ptr<Q, Registry>&,
- Registry> {
- using type = typename virtual_traits<
- const virtual_ptr<Q, Registry>&, Registry>::virtual_type;
- };
- template<typename P, typename Q, class Registry>
- using select_overrider_virtual_type =
- typename select_overrider_virtual_type_aux<P, Q, Registry>::type;
- template<
- typename MethodParameters, typename OverriderParameters, class Registry>
- using overrider_virtual_types = boost::mp11::mp_remove<
- boost::mp11::mp_transform_q<
- boost::mp11::mp_bind_back<select_overrider_virtual_type, Registry>,
- MethodParameters, OverriderParameters>,
- void>;
- template<class Method, class Rtti, std::size_t Index>
- struct init_bad_call {
- template<typename Arg, typename... Args>
- static auto fn(bad_call& error, const Arg& arg, const Args&... args) {
- if constexpr (Index == 0u) {
- error.method = Rtti::template static_type<Method>();
- error.arity = sizeof...(args) + 1;
- }
- type_id arg_type_id;
- if constexpr (is_virtual_ptr<Arg>) {
- arg_type_id = Rtti::dynamic_type(*arg);
- } else {
- arg_type_id = Rtti::dynamic_type(arg);
- }
- error.types[Index] = arg_type_id;
- init_bad_call<Method, Rtti, Index + 1>::fn(error, args...);
- }
- static auto fn(bad_call&) {
- }
- };
- template<class Method, class Rtti>
- struct init_bad_call<Method, Rtti, bad_call::max_types> {
- static auto fn(bad_call&) {
- }
- };
- template<class Registry>
- using method_base = std::conditional_t<
- Registry::has_deferred_static_rtti, deferred_method_info, method_info>;
- template<typename T, class Registry>
- struct parameter_traits {
- static auto peek(const T& value) -> const T& {
- return value;
- }
- template<typename>
- static auto cast(T value) -> T {
- return value;
- }
- };
- template<typename T, class Registry>
- struct parameter_traits<virtual_<T>, Registry> : virtual_traits<T, Registry> {};
- template<class Class, class Registry>
- struct parameter_traits<virtual_ptr<Class, Registry, void>, Registry>
- : virtual_traits<virtual_ptr<Class, Registry, void>, Registry> {};
- template<class Class, class Registry>
- struct parameter_traits<const virtual_ptr<Class, Registry, void>&, Registry>
- : virtual_traits<const virtual_ptr<Class, Registry, void>&, Registry> {};
- template<typename...>
- constexpr bool false_t = false; // workaround before CWG2518/P2593R1
- template<typename T, class Registry, typename = void>
- struct validate_method_parameter : std::true_type {};
- template<typename T, class Registry, typename U>
- struct validate_method_parameter<virtual_<T>, Registry, U> : std::false_type {
- static_assert(false_t<T>, "virtual_traits not specialized for type");
- };
- template<typename T, class Registry>
- struct validate_method_parameter<
- virtual_<T>, Registry,
- std::void_t<typename virtual_traits<T, Registry>::virtual_type>>
- : std::bool_constant<
- has_vptr_fn<virtual_type<T, Registry>, Registry> ||
- Registry::rtti::template is_polymorphic<virtual_type<T, Registry>>> {
- static_assert(
- validate_method_parameter::value,
- "virtual_<> parameter is not a polymorphic class and no "
- "boost_openmethod_vptr is applicable");
- };
- template<class Class, class Registry>
- struct validate_method_parameter<virtual_ptr<Class, Registry>, Registry, void>
- : std::true_type {};
- template<class Class, class Registry, class MethodRegistry>
- struct validate_method_parameter<
- virtual_ptr<Class, Registry>, MethodRegistry, void> : std::false_type {
- static_assert(
- false_t<Class, Registry, MethodRegistry>, "registry mismatch");
- };
- } // namespace detail
- //! Implement a method
- //!
- //! Methods are created by specializing the `method` class template with an
- //! identifier, a function type and optionally a registry.
- //!
- //! `Id` is a type, typically an incomplete class declaration named after the
- //! method's purpose. It is used to allow different methods with the same
- //! signature.
- //!
- //! `Fn` is a function type, i.e. a type in the form `ReturnType(Parameters...)`.
- //!
- //! `Registry` is an instantiation of class template @ref registry. Methods may
- //! use only classes that have been registered in the same registry as virtual
- //! parameters and arguments. The registry also contains a set of policies that
- //! influence several aspects of the dispatch mechanism - for example, how to
- //! acquire a v-table pointer for an object, how to report errors, whether to
- //! perform sanity checks, etc.
- //!
- //! The default value for `Registry` is @ref default_registry, but it can be
- //! overridden by defining the preprocessor symbol
- //! {{BOOST_OPENMETHOD_DEFAULT_REGISTRY}}, *before* including
- //! `<boost/openmethod/core.hpp>`. Setting the symbol afterwards has no effect.
- //!
- //! Specializations of `method` have a single instance: the static member `fn`,
- //! which has an `operator()` that forwards to the appropriate overrider. It is
- //! selected in the same way as overloaded function resolution:
- //!
- //! 1. Form the set of all applicable overriders. An overrider is applicable
- //! if it can be called with the arguments passed to the method.
- //!
- //! 2. If the set is empty, call the error handler (if present in the
- //! registry), then terminate the program with `abort`.
- //!
- //! 3. Remove the overriders that are dominated by other overriders in the set.
- //! Overrider A dominates overrider B if at least one of its virtual formal
- //! parameters is more specialized than B's, and if none of B's virtual
- //! parameters is more specialized than A's.
- //!
- //! 4. If the resulting set contains exactly one overrider, call it.
- //!
- //! If a single most specialized overrider does not exist, the program is
- //! terminated via `abort`. If the registry contains an @ref error_handler
- //! policy, its `error` function is called with an object that describes the
- //! error, prior calling `abort`. `error` may prevent termination by throwing an
- //! exception.
- //!
- //! For each virtual argument `arg`, the dispatch mechanism calls
- //! `virtual_traits::peek(arg)` and deduces the v-table pointer from the
- //! `result`, using the first of the following methods that applies:
- //!
- //! 1. If `result` is a `virtual_ptr`, get the pointer to the v-table from it.
- //!
- //! 2. If `boost_openmethod_vptr` can be called with `result` and a `Registry*`,
- //! and it returns a `vptr_type`, call it.
- //!
- //! 3. Call `Registry::rtti::dynamic_vptr(result)`.
- //!
- //! @par N2216 Handling of Ambiguous Calls
- //!
- //! If `Registry` was initialized with the @ref N2216 option, ambiguous calls
- //! are not an error. Instead, the following extra steps are taken to select an
- //! overrider:
- //!
- //! 1. If the return type is a registered polymorphic type, remove all the
- //! overriders that return a less specific type than others.
- //!
- //! 2. If the resulting set contains only one overrider, call it.
- //!
- //! 3. Otherwise, call one of the remaining overriders. Which overrider is
- //! selected is not specified, but it is the same across calls with the
- //! same arguments types.
- //!
- //! @tparam Id A type
- //! @tparam Fn A function type
- //! @tparam Registry The registry in which the method is defined
- template<
- typename Id, typename Fn,
- class Registry = BOOST_OPENMETHOD_DEFAULT_REGISTRY>
- class method;
- //! Method with a specific id, signature and return type
- //!
- //! `method` implements an open-method that takes a parameter list -
- //! `Parameters` - and returns a `ReturnType`.
- //!
- //! `Parameters` must contain at least one virtual parameter, i.e. a parameter
- //! that has a type in the form `virtual_ptr<T, Registry>` or `virtual\_<T>`.
- //! The dynamic types of the virtual arguments are taken into account to select
- //! the overrider to call.
- //!
- //! @see method
- //!
- //! @tparam Id A type representing the method's name
- //! @tparam ReturnType The return type of the method
- //! @tparam Parameters The types of the parameters
- //! @tparam Registry The registry of the method
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- class method<Id, ReturnType(Parameters...), Registry>
- : public detail::method_base<Registry> {
- template<auto Function, typename FunctionType>
- struct override_aux;
- // Aliases used in implementation only. Everything extracted from template
- // arguments is capitalized like the arguments themselves.
- using RegistryType = Registry;
- using rtti = typename Registry::rtti;
- using DeclaredParameters = mp11::mp_list<Parameters...>;
- using CallParameters =
- boost::mp11::mp_transform<detail::remove_virtual_, DeclaredParameters>;
- using VirtualParameters =
- typename detail::virtual_types<DeclaredParameters>;
- using Signature = auto(Parameters...) -> ReturnType;
- using FunctionPointer = auto (*)(detail::remove_virtual_<Parameters>...)
- -> ReturnType;
- public:
- //! Method singleton
- //!
- //! The only instance of `method`. Its `operator()` is used to call
- //! the method.
- static method fn;
- //! Call the method
- //!
- //! Call the method with `args`. The types of the arguments are the same as
- //! the method `Parameters...`, stripped from any `virtual\_` decorators.
- //!
- //! @param args The arguments for the method call
- //!
- //! @par Errors
- //!
- //! If `Registry` contains an @ref error_handler policy, call its `error`
- //! function with an object of one of the following types:
- //!
- //! @li @ref not_implemented: No overrider is applicable.
- //! @li @ref ambiguous_call: More than one overrider is applicable, and
- //! none is more specialized than all the others.
- //!
- auto operator()(typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- StripVirtualDecorator<Parameters>::type... args) const
- -> ReturnType;
- //! Check if a next most specialized overrider exists
- //!
- //! Return `true` if a next most specialized overrider after _Fn_ exists,
- //! and @ref next can be called without causing a @ref bad_call.
- //!
- //! @par Requirements
- //!
- //! `Fn` must be a function that is an overrider of the method.
- //!
- //! @tparam Fn A function that is an overrider of the method.
- //! @return `true` if a next most specialized overrider exists
- template<auto Fn>
- static bool has_next();
- //! The next most specialized overrider
- //!
- //! A pointer to the next most specialized overrider after `Fn`, i.e. the
- //! overrider that would be called for the same tuple of virtual arguments
- //! if `Fn` was not present. Set to `nullptr` if no such overrider exists.
- //! @par Requirements
- //!
- //! `Fn` must be a function that is an overrider of the method.
- //!
- //! @tparam Fn A function that is an overrider of the method.
- template<auto Fn>
- static FunctionPointer next;
- //! Add overriders to method
- //!
- //! `override`, instantiated as a static object, adds one or more overriders
- //! to an open-method.
- //!
- //! @par Requirements
- //!
- //! `Fn` must be a function that fulfills the following requirements:
- //!
- //! @li Have the same number of formal parameters as the method.
- //!
- //! @li Each `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.
- //!
- //! @li 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.
- //!
- //! @li All other formal parameters must have the same type as the method's
- //! corresponding parameters.
- //!
- //! @li The return type of the overrider must be the same as the method's
- //! return type or, if it is a polymorphic type, covariant with the method's
- //! return type.
- //!
- //! @tparam Fn One or more functions to the overrider list
- template<auto... Fn>
- class override {
- std::tuple<override_aux<Fn, decltype(Fn)>...> impl;
- };
- private:
- static constexpr auto Arity = boost::mp11::mp_count_if<
- mp11::mp_list<Parameters...>, detail::is_virtual>::value;
- // sanity checks
- static_assert((
- detail::validate_method_parameter<Parameters, Registry>::value && ...));
- static_assert(Arity > 0, "method has no virtual parameters");
- type_id vp_type_ids[Arity];
- std::size_t slots_strides[2 * Arity - 1];
- // Slots followed by strides. No stride for first virtual argument.
- // For 1-method: the offset of the method in the method table, which
- // contains a pointer to a function.
- // For multi-methods: the offset of the first virtual argument in the
- // method table, which contains a pointer to the corresponding cell in
- // the dispatch table, followed by the offset of the second argument and
- // the stride in the second dimension, etc.
- void resolve_type_ids();
- template<typename ArgType>
- auto vptr(const ArgType& arg) const -> vptr_type;
- template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
- auto resolve_uni(const ArgType& arg, const MoreArgTypes&... more_args) const
- -> detail::word;
- template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
- auto resolve_multi_first(
- const ArgType& arg,
- const MoreArgTypes&... more_args) const -> detail::word;
- template<
- std::size_t VirtualArg, typename MethodArgList, typename ArgType,
- typename... MoreArgTypes>
- auto resolve_multi_next(
- vptr_type dispatch, const ArgType& arg,
- const MoreArgTypes&... more_args) const -> detail::word;
- template<typename... ArgType>
- FunctionPointer resolve(const ArgType&... args) const;
- template<auto, typename>
- struct thunk;
- template<auto, typename>
- struct thunk;
- method();
- method(const method&) = delete;
- method(method&&) = delete;
- ~method();
- void resolve(); // virtual if Registry contains has_deferred_static_rtti
- static BOOST_NORETURN auto fn_not_implemented(
- detail::remove_virtual_<Parameters>... args) -> ReturnType;
- static BOOST_NORETURN auto
- fn_ambiguous(detail::remove_virtual_<Parameters>... args) -> ReturnType;
- template<
- auto Overrider, typename OverriderReturn,
- typename... OverriderParameters>
- struct thunk<Overrider, OverriderReturn (*)(OverriderParameters...)> {
- static auto
- fn(detail::remove_virtual_<Parameters>... arg) -> ReturnType;
- using OverriderVirtualParameters = detail::overrider_virtual_types<
- DeclaredParameters, mp11::mp_list<OverriderParameters...>,
- Registry>;
- };
- template<auto Function, typename FnReturnType>
- struct override_impl
- : std::conditional_t<
- Registry::has_deferred_static_rtti,
- detail::deferred_overrider_info, detail::overrider_info> {
- explicit override_impl(FunctionPointer* next = nullptr);
- void resolve_type_ids();
- static type_id vp_type_ids[Arity];
- };
- template<auto Function, typename FunctionType>
- struct override_aux;
- template<auto Function, typename FnReturnType, typename... FnParameters>
- struct override_aux<Function, FnReturnType (*)(FnParameters...)> {
- override_aux() {
- (void)&impl;
- }
- static override_impl<Function, FnReturnType> impl;
- };
- };
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- method<Id, ReturnType(Parameters...), Registry>
- method<Id, ReturnType(Parameters...), Registry>::fn;
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<auto Fn>
- typename method<Id, ReturnType(Parameters...), Registry>::FunctionPointer
- method<Id, ReturnType(Parameters...), Registry>::next;
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<auto Function, typename FnReturnType>
- type_id method<Id, ReturnType(Parameters...), Registry>::override_impl<
- Function, FnReturnType>::vp_type_ids[Arity];
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<auto Function, typename FnReturnType, typename... FnParameters>
- typename method<Id, ReturnType(Parameters...), Registry>::
- template override_impl<Function, FnReturnType>
- method<Id, ReturnType(Parameters...), Registry>::override_aux<
- Function, FnReturnType (*)(FnParameters...)>::impl;
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- method<Id, ReturnType(Parameters...), Registry>::method() {
- using namespace policies;
- this->slots_strides_ptr = slots_strides;
- if constexpr (!Registry::has_deferred_static_rtti) {
- resolve_type_ids();
- }
- this->vp_begin = vp_type_ids;
- this->vp_end = vp_type_ids + Arity;
- this->not_implemented = reinterpret_cast<void (*)()>(fn_not_implemented);
- this->ambiguous = reinterpret_cast<void (*)()>(fn_ambiguous);
- Registry::methods.push_back(*this);
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- void method<Id, ReturnType(Parameters...), Registry>::resolve_type_ids() {
- using namespace detail;
- this->method_type_id = rtti::template static_type<method>();
- this->return_type_id =
- rtti::template static_type<virtual_type<ReturnType, Registry>>();
- init_type_ids<
- Registry,
- mp11::mp_transform_q<
- mp11::mp_bind_back<virtual_type, Registry>,
- VirtualParameters>>::fn(this->vp_type_ids);
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- method<Id, ReturnType(Parameters...), Registry>::~method() {
- Registry::methods.remove(*this);
- }
- // -----------------------------------------------------------------------------
- // method dispatch
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- BOOST_FORCEINLINE auto
- method<Id, ReturnType(Parameters...), Registry>::operator()(
- typename BOOST_OPENMETHOD_DETAIL_UNLESS_MRDOCS
- StripVirtualDecorator<Parameters>::type... args) const -> ReturnType {
- using namespace detail;
- auto pf = resolve(parameter_traits<Parameters, Registry>::peek(args)...);
- return pf(std::forward<typename StripVirtualDecorator<Parameters>::type>(
- args)...);
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<typename... ArgType>
- BOOST_FORCEINLINE
- typename method<Id, ReturnType(Parameters...), Registry>::FunctionPointer
- method<Id, ReturnType(Parameters...), Registry>::resolve(
- const ArgType&... args) const {
- using namespace detail;
- Registry::require_initialized();
- void (*pf)();
- if constexpr (Arity == 1) {
- pf = resolve_uni<mp11::mp_list<Parameters...>, ArgType...>(args...).pf;
- } else {
- pf = resolve_multi_first<mp11::mp_list<Parameters...>, ArgType...>(
- args...)
- .pf;
- }
- return reinterpret_cast<FunctionPointer>(pf);
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<typename ArgType>
- BOOST_FORCEINLINE auto method<Id, ReturnType(Parameters...), Registry>::vptr(
- const ArgType& arg) const -> vptr_type {
- if constexpr (detail::is_virtual_ptr<ArgType>) {
- return arg.vptr();
- } else {
- return detail::acquire_vptr<Registry>(arg);
- }
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
- BOOST_FORCEINLINE auto
- method<Id, ReturnType(Parameters...), Registry>::resolve_uni(
- const ArgType& arg,
- const MoreArgTypes&... more_args) const -> detail::word {
- using namespace detail;
- using namespace policies;
- using namespace boost::mp11;
- if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
- vptr_type vtbl = vptr<ArgType>(arg);
- return vtbl[this->slots_strides[0]];
- } else {
- return resolve_uni<mp_rest<MethodArgList>>(more_args...);
- }
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<typename MethodArgList, typename ArgType, typename... MoreArgTypes>
- BOOST_FORCEINLINE auto
- method<Id, ReturnType(Parameters...), Registry>::resolve_multi_first(
- const ArgType& arg,
- const MoreArgTypes&... more_args) const -> detail::word {
- using namespace detail;
- using namespace boost::mp11;
- if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
- vptr_type vtbl = vptr<ArgType>(arg);
- std::size_t slot = this->slots_strides[0];
- // The first virtual parameter is special. Since its stride is
- // 1, there is no need to store it. Also, the method table
- // contains a pointer into the multi-dimensional dispatch table,
- // already resolved to the appropriate group.
- auto dispatch = vtbl[slot].pw;
- return resolve_multi_next<1, mp_rest<MethodArgList>, MoreArgTypes...>(
- dispatch, more_args...);
- } else {
- return resolve_multi_first<mp_rest<MethodArgList>, MoreArgTypes...>(
- more_args...);
- }
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<
- std::size_t VirtualArg, typename MethodArgList, typename ArgType,
- typename... MoreArgTypes>
- BOOST_FORCEINLINE auto
- method<Id, ReturnType(Parameters...), Registry>::resolve_multi_next(
- vptr_type dispatch, const ArgType& arg,
- const MoreArgTypes&... more_args) const -> detail::word {
- using namespace detail;
- using namespace boost::mp11;
- if constexpr (is_virtual<mp_first<MethodArgList>>::value) {
- vptr_type vtbl = vptr<ArgType>(arg);
- std::size_t slot = this->slots_strides[VirtualArg];
- std::size_t stride = this->slots_strides[Arity + VirtualArg - 1];
- dispatch = dispatch + vtbl[slot].i * stride;
- }
- if constexpr (VirtualArg + 1 == Arity) {
- return *dispatch;
- } else {
- return resolve_multi_next<
- VirtualArg + 1, mp_rest<MethodArgList>, MoreArgTypes...>(
- dispatch, more_args...);
- }
- }
- // -----------------------------------------------------------------------------
- // Error handling
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<auto Fn>
- inline auto
- method<Id, ReturnType(Parameters...), Registry>::has_next() -> bool {
- if (next<Fn> == fn_not_implemented) {
- return false;
- }
- if (next<Fn> == fn_ambiguous) {
- return false;
- }
- return true;
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- BOOST_NORETURN auto
- method<Id, ReturnType(Parameters...), Registry>::fn_not_implemented(
- detail::remove_virtual_<Parameters>... args) -> ReturnType {
- using namespace policies;
- if constexpr (Registry::has_error_handler) {
- no_overrider error;
- detail::init_bad_call<method, rtti, 0u>::fn(
- error,
- detail::parameter_traits<Parameters, Registry>::peek(args)...);
- Registry::error_handler::error(error);
- }
- abort(); // in case user handler "forgets" to abort
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- BOOST_NORETURN auto
- method<Id, ReturnType(Parameters...), Registry>::fn_ambiguous(
- detail::remove_virtual_<Parameters>... args) -> ReturnType {
- using namespace policies;
- if constexpr (Registry::has_error_handler) {
- ambiguous_call error;
- detail::init_bad_call<method, rtti, 0u>::fn(
- error,
- detail::parameter_traits<Parameters, Registry>::peek(args)...);
- Registry::error_handler::error(error);
- }
- abort(); // in case user handler "forgets" to abort
- }
- // -----------------------------------------------------------------------------
- // overriders
- namespace detail {
- template<typename T, typename U>
- struct same_reference_category {
- static constexpr bool value = (std::is_lvalue_reference<T>::value ==
- std::is_lvalue_reference<U>::value) &&
- (std::is_rvalue_reference<T>::value ==
- std::is_rvalue_reference<U>::value);
- };
- template<class T1, class T2, typename = void>
- struct validate_overrider_parameter : std::false_type {
- static_assert(
- false_t<T1, T2>, "non-virtual parameter types must match exactly");
- };
- template<class T1, class T2>
- struct validate_overrider_parameter<
- T1, T2,
- std::enable_if_t<
- is_virtual_ptr<T1> && is_virtual_ptr<T2> &&
- !same_reference_category<T1, T2>::value>> : std::false_type {
- static_assert(
- false_t<T1, T2>, "different virtual_ptr<> reference categories");
- };
- template<class T1, class T2>
- struct validate_overrider_parameter<
- T1, T2, std::enable_if_t<is_virtual_ptr<T1> && !is_virtual_ptr<T2>>>
- : std::false_type {
- static_assert(
- false_t<T1, T2>,
- "virtual_ptr<> is required in overrider in same position as in "
- "method");
- };
- template<class T>
- struct validate_overrider_parameter<T, T, void> : std::true_type {};
- template<class T1, class T2>
- struct validate_overrider_parameter<virtual_<T1>, T2, void> : std::true_type {};
- template<class T1, class T2>
- struct validate_overrider_parameter<virtual_<T1>, virtual_<T2>, void>
- : std::false_type {
- static_assert(false_t<T1, T2>, "virtual_<> is not allowed in overriders");
- };
- template<class T, class R>
- struct validate_overrider_parameter<virtual_ptr<T, R>, virtual_ptr<T, R>, void>
- : std::true_type {};
- template<class T1, class R, class T2, class R2>
- struct validate_overrider_parameter<
- virtual_ptr<T1, R>, virtual_ptr<T2, R2>, void> : std::true_type {
- static_assert(std::is_same_v<R, R2>, "registry mismatch");
- using C1 = virtual_type<virtual_ptr<T1, R>, R>;
- using C2 = virtual_type<virtual_ptr<T2, R>, R>;
- static_assert(
- std::is_base_of_v<C1, C2> &&
- std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
- "method parameter must be an unambiguous accessible base "
- "of corresponding overrider parameter");
- };
- template<class T1, class R, class T2, class R2>
- struct validate_overrider_parameter<
- const virtual_ptr<T1, R>&, const virtual_ptr<T2, R2>&, void>
- : std::true_type {
- static_assert(std::is_same_v<R, R2>, "registry mismatch");
- using C1 = virtual_type<const virtual_ptr<T1, R>&, R>;
- using C2 = virtual_type<const virtual_ptr<T2, R>&, R>;
- static_assert(
- std::is_base_of_v<C1, C2> &&
- std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
- "method parameter must be an unambiguous accessible base "
- "of corresponding overrider parameter");
- };
- template<class T1, class R, class T2, class R2>
- struct validate_overrider_parameter<
- virtual_ptr<T1, R>&&, virtual_ptr<T2, R2>&&, void> : std::true_type {
- static_assert(std::is_same_v<R, R2>, "registry mismatch");
- using C1 = virtual_type<virtual_ptr<T1, R>&&, R>;
- using C2 = virtual_type<virtual_ptr<T2, R>&&, R>;
- static_assert(
- std::is_base_of_v<C1, C2> &&
- std::is_convertible_v<virtual_ptr<T2, R>, virtual_ptr<T1, R>>,
- "method parameter must be an unambiguous accessible base "
- "of corresponding overrider parameter");
- };
- } // namespace detail
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<
- auto Overrider, typename OverriderReturn, typename... OverriderParameters>
- auto method<Id, ReturnType(Parameters...), Registry>::
- thunk<Overrider, OverriderReturn (*)(OverriderParameters...)>::fn(
- detail::remove_virtual_<Parameters>... arg) -> ReturnType {
- using namespace detail;
- static_assert(
- (validate_overrider_parameter<Parameters, OverriderParameters>::value &&
- ...),
- "virtual_ptr category mismatch");
- return Overrider(
- detail::parameter_traits<Parameters, Registry>::template cast<
- OverriderParameters>(
- std::forward<detail::remove_virtual_<Parameters>>(arg))...);
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<auto Function, typename FnReturnType>
- method<Id, ReturnType(Parameters...), Registry>::override_impl<
- Function, FnReturnType>::override_impl(FunctionPointer* p_next) {
- using namespace detail;
- // static variable this->method below is zero-initialized but gcc and clang
- // don't always see that.
- #ifdef BOOST_CLANG
- #pragma clang diagnostic push
- #pragma clang diagnostic ignored "-Wuninitialized"
- #endif
- #ifdef BOOST_GCC
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wuninitialized"
- #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
- #endif
- // zero-initalized static variable
- // coverity[uninit_use:FALSE]
- if (overrider_info::method) {
- BOOST_ASSERT(overrider_info::method == &fn);
- return;
- }
- #ifdef BOOST_CLANG
- #pragma clang diagnostic pop
- #endif
- #ifdef BOOST_GCC
- #pragma GCC diagnostic pop
- #endif
- overrider_info::method = &fn;
- if constexpr (!Registry::has_deferred_static_rtti) {
- resolve_type_ids();
- }
- this->next = reinterpret_cast<void (**)()>(
- p_next ? p_next : &method::next<Function>);
- using Thunk = thunk<Function, decltype(Function)>;
- this->pf = reinterpret_cast<void (*)()>(Thunk::fn);
- this->vp_begin = vp_type_ids;
- this->vp_end = vp_type_ids + Arity;
- fn.overriders.push_back(*this);
- }
- template<
- typename Id, typename... Parameters, typename ReturnType, class Registry>
- template<auto Function, typename FnReturnType>
- void method<Id, ReturnType(Parameters...), Registry>::override_impl<
- Function, FnReturnType>::resolve_type_ids() {
- using namespace detail;
- this->return_type = Registry::rtti::template static_type<
- virtual_type<FnReturnType, Registry>>();
- this->type = Registry::rtti::template static_type<decltype(Function)>();
- using Thunk = thunk<Function, decltype(Function)>;
- detail::
- init_type_ids<Registry, typename Thunk::OverriderVirtualParameters>::fn(
- this->vp_type_ids);
- }
- //! Aliases for the most frequently used types in the library.
- namespace aliases {
- using boost::openmethod::final_virtual_ptr;
- using boost::openmethod::virtual_;
- using boost::openmethod::virtual_ptr;
- } // namespace aliases
- // ==============================================================================
- // Exposition only
- #ifdef __MRDOCS__
- //! Blueprint for a specialization of @ref virtual_traits (exposition only).
- //!
- //! Specializations of @ref virtual_traits must implement the members listed
- //! here.
- //!
- //! @tparam T The type of a virtual parameter of a method.
- //! @tparam Registry A @ref registry.
- template<typename T, class Registry>
- struct VirtualTraits {
- //! Class to use for dispatch.
- //!
- //! Aliases to the class to be considered during method dispatch to determine
- //! which overrider to select, and which type_id to use for error reporting.
- //! `virtual_traits<T>::virtual_type` aliases to `Class` if `T` is `Class&`,
- //! `const Class&`, `Class*`, `const Class*`, `virtual_ptr<Class>`,
- //! `virtual_ptr<const Class>`, `std::shared_ptr<Class>`,
- //! `std::shared_ptr<const Class>`, `virtual_ptr<std::shared_ptr<Class>>`,
- //! etc.
- //!
- //! @par Requirements
- //!
- //! `virtual_type` must be an alias to an *unadorned* *class* type, *not*
- //! cv-qualified.
- using virtual_type = detail::unspecified;
- //! Returns a reference to the object to use for dispatch.
- //!
- //! Return a reference to the object to use for dispatch. `arg` may not be
- //! copied, moved or altered in any way.
- //!
- //! @param arg An argument passed to the method call.
- //! @return A reference to an object.
- static auto peek(T arg) -> const virtual_type&;
- //! Casts a virtual argument.
- //!
- //! Casts a virtual argument to the type expected by the overrider.
- //!
- //! @tparam T The type of a virtual parameter of a method.
- //! @tparam U The type of a virtual parameter of an overrider.
- //! @param arg The argument passed to a method call.
- //! @return A reference to the argument, cast to `U`.
- template<typename U>
- static auto cast(T arg) -> U;
- //! Rebind to a another class (smart pointers only).
- //!
- //! If `T` is a smart pointer, `rebind<U>` is the same kind of smart
- //! pointer, but pointing to a `U`.
- //!
- //! @note `rebind` must be implemented @em only for smart pointer classes
- //! that can be used as object pointers by @ref virtual_ptr in place of
- //! plain pointers.
- //!
- //! @tparam U The new element type.
- template<class U>
- using rebind = detail::unspecified;
- };
- #endif
- } // namespace boost::openmethod
- #ifdef _MSC_VER
- #pragma warning(pop)
- #endif
- #endif
|