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