/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <functional> #include <new> #include <tuple> #include <type_traits> #include <typeinfo> #include <utility> #include <folly/PolyException.h> #include <folly/Portability.h> #include <folly/Traits.h> #include <folly/Utility.h> #include <folly/detail/TypeList.h> #include <folly/functional/Invoke.h> #include <folly/lang/Exception.h> #include <folly/lang/StaticConst.h> #if defined(__cpp_template_auto) || \ defined(__cpp_nontype_template_parameter_auto) #define FOLLY_POLY_NTTP_AUTO 1 #else #define FOLLY_POLY_NTTP_AUTO 0 #endif namespace folly { /// \cond namespace detail { template <class I> struct PolyRoot; using RRef_ = MetaQuoteTrait<std::add_rvalue_reference>; using LRef_ = MetaQuoteTrait<std::add_lvalue_reference>; template <typename T> struct XRef_ : Type<MetaQuoteTrait<Type>> {}; template <typename T> using XRef = _t<XRef_<T>>; template <typename T> struct XRef_<T&&> : Type<MetaCompose<RRef_, XRef<T>>> {}; template <typename T> struct XRef_<T&> : Type<MetaCompose<LRef_, XRef<T>>> {}; template <typename T> struct XRef_<T const> : Type<MetaQuoteTrait<std::add_const>> {}; template <class A, class B> using AddCvrefOf = MetaApply<XRef<B>, A>; } // namespace detail /// \endcond template <class I> struct Poly; template <class T, class I> detail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>&); template <class T, class I> detail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const&); #if !FOLLY_POLY_NTTP_AUTO #define FOLLY_AUTO class template <class... Ts> using PolyMembers = detail::TypeList<Ts...>; #else #define FOLLY_AUTO auto template <auto...> struct PolyMembers; #endif /// \cond namespace detail { /* ***************************************************************************** * IMPLEMENTATION NOTES * Building the Interface ---------------------- Here is a high-level description of how Poly works. Users write an interface such as: struct Mine { template <class Base> struct Interface { int Exec() const { return folly::poly_call<0>(*this); } } template <class T> using Members = folly::PolyMembers<&T::Exec>; }; Then they instantiate Poly<Mine>, which will have an Exec member function of the correct signature. The Exec member function comes from Mine::Interface<PolyNode<Mine, PolyRoot<Mine>>>, from which Poly<Mine> inherits. Here's what each piece means: - PolyRoot<I>: stores Data, which is a union of a void* (used when the Poly is storing an object on the heap or a reference) and some aligned storage (used when the Poly is storing an object in-situ). PolyRoot also stores a vtable pointer for interface I, which is a pointer to a struct containing function pointers. The function pointers are bound member functions (e.g., SomeType::Exec). More on the vtable pointer and how it is generated below. - PolyNode: provides the hooks used by folly::poly_call to dispatch to the correctly bound member function for this interface. In the context of an interface I, folly::poly_call<K>(*this, args...) will: 1. Fetch the vtable pointer from PolyRoot, 2. Select the I portion of that vtable (which, in the case of interface extension, may only be a part of the total vtable), 3. Fetch the K-th function pointer from that vtable part, 4. Call through that function pointer, passing Data (from PolyRoot) and any additional arguments in the folly::poly_call<K> invocation. In the case of interface extension -- for instance, if interface Mine extended interface Yours by inheriting from PolyExtends<Yours> -- then interface Mine will have a list of base interfaces in a typelist called "Subsumptions". Poly<Mine> will fold all the subsumed interfaces together, linearly inheriting from them. To take the example of an interface Mine that extends Yours, Poly<Mine> would inherit from this type: Mine::Interface< PolyNode<Mine, Your::Interface< PolyNode<Your, PolyRoot<Mine>>>>> Through linear inheritance, Poly<Mine> ends up with the public member functions of both interfaces, Mine and Yours. VTables ------- As mentioned above, PolyRoot<I> stores a vtable pointer for interface I. The vtable is a struct whose members are function pointers. How are the types of those function pointers computed from the interface? A dummy type is created, Archetype<I>, in much the same way that Poly<I>'s base type is computed. Instead of PolyNode and PolyRoot, there is ArchetypeNode and ArchetypeRoot. These types also provide hooks for folly::poly_call, but they are dummy hooks that do nothing. (Actually, they call std::terminate; they will never be called.) Once Archetype<I> has been constructed, it is a concrete type that has all the member functions of the interface and its subsumed interfaces. That type is passed to Mine::Members, which takes the address of Archetype<I>::Exec and inspects the resulting member function type. This is done for each member in the interface. From a list of [member] function pointers, it is a simple matter of metaprogramming to build a struct of function pointers. std::tuple is used for this. An extra field is added to the tuple for a function that handles all of the "special" operations: destruction, copying, moving, getting the type information, getting the address of the stored object, and fetching a fixed-up vtable pointer for reference conversions (e.g., I -> I&, I& -> I const&, etc). Subsumed interfaces are handled by having VTable<IDerived> inherit from BasePtr<IBase>, where BasePtr<IBase> has only one member of type VTable<IBase> const*. Now that the type of VTable<I> is computed, how are the fields populated? Poly<I> default-constructs to an empty state. Its vtable pointer points to a vtable whose fields are initialized with the addresses of functions that do nothing but throw a BadPolyAccess exception. That way, if you call a member function on an empty Poly, you get an exception. The function pointer corresponding to the "special" operations points to a no-op function; copying, moving and destroying an empty Poly does nothing. On the other hand, when you pass an object of type T satisfying interface I to Poly<I>'s constructor or assignment operator, a vtable for {I,T} is reified by passing type T to I::Members, thereby creating a list of bindings for T's member functions. The address of this vtable gets stored in the PolyRoot<I> subobject, imbuing the Poly object with the behaviors of type T. The T object itself gets stored either on the heap or in the aligned storage within the Poly object itself, depending on the size of T and whether or not it has a noexcept move constructor. */ template <class T, template <class...> class U> struct IsInstanceOf : std::false_type {}; template <class... Ts, template <class...> class U> struct IsInstanceOf<U<Ts...>, U> : std::true_type {}; template <class Then> decltype(auto) if_constexpr(std::true_type, Then then) { return then(Identity{}); } template <class Then> void if_constexpr(std::false_type, Then) {} template <class Then, class Else> decltype(auto) if_constexpr(std::true_type, Then then, Else) { return then(Identity{}); } template <class Then, class Else> decltype(auto) if_constexpr(std::false_type, Then, Else else_) { return else_(Identity{}); } enum class Op : short { eNuke, eMove, eCopy, eType, eAddr, eRefr }; enum class RefType : std::uintptr_t { eRvalue, eLvalue, eConstLvalue }; struct Data; template <class I> struct PolyVal; template <class I> struct PolyRef; struct PolyAccess; template <class T> using IsPoly = IsInstanceOf<remove_cvref_t<T>, Poly>; // Given an interface I and a concrete type T that satisfies the interface // I, create a list of member function bindings from members of T to members // of I. template <class I, class T> using MembersOf = typename I::template Members<remove_cvref_t<T>>; // Given an interface I and a base type T, create a type that implements // the interface I in terms of the capabilities of T. template <class I, class T> using InterfaceOf = typename I::template Interface<T>; #if !FOLLY_POLY_NTTP_AUTO template <class T, T V> using Member = std::integral_constant<T, V>; template <class M> using MemberType = typename M::value_type; template <class M> inline constexpr MemberType<M> memberValue() noexcept { return M::value; } template <class... Ts> struct MakeMembers { template <Ts... Vs> using Members = PolyMembers<Member<Ts, Vs>...>; }; template <class... Ts> MakeMembers<Ts...> deduceMembers(Ts...); template <class Member, class = MemberType<Member>> struct MemberDef; template <class Member, class R, class D, class... As> struct MemberDef<Member, R (D::*)(As...)> { static R value(D& d, As... as) { return folly::invoke(memberValue<Member>(), d, static_cast<As&&>(as)...); } }; template <class Member, class R, class D, class... As> struct MemberDef<Member, R (D::*)(As...) const> { static R value(D const& d, As... as) { return folly::invoke(memberValue<Member>(), d, static_cast<As&&>(as)...); } }; #else template <auto M> using MemberType = decltype(M); template <auto M> inline constexpr MemberType<M> memberValue() noexcept { return M; } #endif struct PolyBase {}; template <class I, class = void> struct SubsumptionsOf_ { using type = TypeList<>; }; template <class I> using InclusiveSubsumptionsOf = TypePushFront<_t<SubsumptionsOf_<I>>, I>; template <class I> struct SubsumptionsOf_<I, void_t<typename I::Subsumptions>> { using type = TypeJoin<TypeTransform< typename I::Subsumptions, MetaQuote<InclusiveSubsumptionsOf>>>; }; template <class I> using SubsumptionsOf = TypeReverseUnique<_t<SubsumptionsOf_<I>>>; struct Bottom { template <class T> [[noreturn]] /* implicit */ operator T&&() const { std::terminate(); } }; using ArchetypeNode = MetaQuote<InterfaceOf>; template <class I> struct ArchetypeRoot; template <class I> using Archetype = TypeFold<InclusiveSubsumptionsOf<I>, ArchetypeRoot<I>, ArchetypeNode>; struct ArchetypeBase : Bottom { ArchetypeBase() = default; template <class T> /* implicit */ ArchetypeBase(T&&); template <std::size_t, class... As> [[noreturn]] Bottom _polyCall_(As&&...) const { std::terminate(); } friend bool operator==(ArchetypeBase const&, ArchetypeBase const&); friend bool operator!=(ArchetypeBase const&, ArchetypeBase const&); friend bool operator<(ArchetypeBase const&, ArchetypeBase const&); friend bool operator<=(ArchetypeBase const&, ArchetypeBase const&); friend bool operator>(ArchetypeBase const&, ArchetypeBase const&); friend bool operator>=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator++(ArchetypeBase const&); friend Bottom operator++(ArchetypeBase const&, int); friend Bottom operator--(ArchetypeBase const&); friend Bottom operator--(ArchetypeBase const&, int); friend Bottom operator+(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator+=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator-(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator-=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator*(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator*=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator/(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator/=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator%(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator%=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator<<(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator<<=(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator>>(ArchetypeBase const&, ArchetypeBase const&); friend Bottom operator>>=(ArchetypeBase const&, ArchetypeBase const&); }; template <class I> struct ArchetypeRoot : ArchetypeBase { template <class Node, class Tfx> using _polySelf_ = Archetype<AddCvrefOf<MetaApply<Tfx, I>, Node>>; using _polyInterface_ = I; }; struct Data { Data() = default; // Suppress compiler-generated copy ops to not copy anything: Data(Data const&) {} Data& operator=(Data const&) { return *this; } union { void* pobj_ = nullptr; std::aligned_storage_t<sizeof(double[2])> buff_; }; }; template <class U, class I> using Arg = If<std::is_same<remove_cvref_t<U>, Archetype<I>>::value, Poly<AddCvrefOf<I, U const&>>, U>; template <class U, class I> using Ret = If<std::is_same<remove_cvref_t<U>, Archetype<I>>::value, AddCvrefOf<Poly<I>, U>, U>; template <class Member, class I> struct SignatureOf_; template <class R, class C, class... As, class I> struct SignatureOf_<R (C::*)(As...), I> { using type = Ret<R, I> (*)(Data&, Arg<As, I>...); }; template <class R, class C, class... As, class I> struct SignatureOf_<R (C::*)(As...) const, I> { using type = Ret<R, I> (*)(Data const&, Arg<As, I>...); }; #if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE template <class R, class C, class... As, class I> struct SignatureOf_<R (C::*)(As...) noexcept, I> { using type = std::add_pointer_t<Ret<R, I>(Data&, Arg<As, I>...) noexcept>; }; template <class R, class C, class... As, class I> struct SignatureOf_<R (C::*)(As...) const noexcept, I> { using type = std::add_pointer_t<Ret<R, I>(Data const&, Arg<As, I>...) noexcept>; }; #endif template <class R, class This, class... As, class I> struct SignatureOf_<R (*)(This&, As...), I> { using type = Ret<R, I> (*)(Data&, Arg<As, I>...); }; template <class R, class This, class... As, class I> struct SignatureOf_<R (*)(This const&, As...), I> { using type = Ret<R, I> (*)(Data const&, Arg<As, I>...); }; template <FOLLY_AUTO Arch, class I> using SignatureOf = _t<SignatureOf_<MemberType<Arch>, I>>; template <FOLLY_AUTO User, class I, class Sig = SignatureOf<User, I>> struct ArgTypes_; template <FOLLY_AUTO User, class I, class Ret, class Data, class... Args> struct ArgTypes_<User, I, Ret (*)(Data, Args...)> { using type = TypeList<Args...>; }; #if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE template <FOLLY_AUTO User, class I, class Ret, class Data, class... Args> struct ArgTypes_<User, I, Ret (*)(Data, Args...) noexcept> { using type = TypeList<Args...>; }; #endif template <FOLLY_AUTO User, class I> using ArgTypes = _t<ArgTypes_<User, I>>; template <class R, class... Args> using FnPtr = R (*)(Args...); struct ThrowThunk { template <class R, class... Args> constexpr /* implicit */ operator FnPtr<R, Args...>() const noexcept { struct _ { static R call(Args...) { throw_exception<BadPolyAccess>(); } }; return &_::call; } }; inline constexpr ThrowThunk throw_() noexcept { return ThrowThunk{}; } template <class T> inline constexpr bool inSitu() noexcept { return !std::is_reference<T>::value && sizeof(std::decay_t<T>) <= sizeof(Data) && std::is_nothrow_move_constructible<std::decay_t<T>>::value; } template <class T> T& get(Data& d) noexcept { if (inSitu<T>()) { return *(std::add_pointer_t<T>)static_cast<void*>(&d.buff_); } else { return *static_cast<std::add_pointer_t<T>>(d.pobj_); } } template <class T> T const& get(Data const& d) noexcept { if (inSitu<T>()) { return *(std::add_pointer_t<T const>)static_cast<void const*>(&d.buff_); } else { return *static_cast<std::add_pointer_t<T const>>(d.pobj_); } } enum class State : short { eEmpty, eInSitu, eOnHeap }; template <class T> struct IsPolyRef : std::false_type {}; template <class T> struct IsPolyRef<Poly<T&>> : std::true_type {}; template <class Arg, class U> decltype(auto) convert(U&& u) { return detail::if_constexpr( StrictConjunction< IsPolyRef<remove_cvref_t<U>>, Negation<std::is_convertible<U, Arg>>>(), [&](auto id) -> decltype(auto) { return poly_cast<remove_cvref_t<Arg>>(id(u).get()); }, [&](auto id) -> U&& { return static_cast<U&&>(id(u)); }); } template <class Fun> struct IsConstMember : std::false_type {}; template <class R, class C, class... As> struct IsConstMember<R (C::*)(As...) const> : std::true_type {}; template <class R, class C, class... As> struct IsConstMember<R (*)(C const&, As...)> : std::true_type {}; #if FOLLY_HAVE_NOEXCEPT_FUNCTION_TYPE template <class R, class C, class... As> struct IsConstMember<R (C::*)(As...) const noexcept> : std::true_type {}; template <class R, class C, class... As> struct IsConstMember<R (*)(C const&, As...) noexcept> : std::true_type {}; #endif template < class T, FOLLY_AUTO User, class I, class = ArgTypes<User, I>, class = Bool<true>> struct ThunkFn { template <class R, class D, class... As> constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept { return nullptr; } }; template <class T, FOLLY_AUTO User, class I, class... Args> struct ThunkFn< T, User, I, TypeList<Args...>, Bool< !std::is_const<std::remove_reference_t<T>>::value || IsConstMember<MemberType<User>>::value>> { template <class R, class D, class... As> constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept { struct _ { static R call(D& d, As... as) { return folly::invoke( memberValue<User>(), get<T>(d), convert<Args>(static_cast<As&&>(as))...); } }; return &_::call; } }; template < class I, class = MembersOf<I, Archetype<I>>, class = SubsumptionsOf<I>> struct VTable; template <class T, FOLLY_AUTO User, class I> inline constexpr ThunkFn<T, User, I> thunk() noexcept { return ThunkFn<T, User, I>{}; } template <class I> constexpr VTable<I> const* vtable() noexcept { return &StaticConst<VTable<I>>::value; } template <class I, class T> struct VTableFor : VTable<I> { constexpr VTableFor() noexcept : VTable<I>{Type<T>{}} {} }; template <class I, class T> constexpr VTable<I> const* vtableFor() noexcept { return &StaticConst<VTableFor<I, T>>::value; } template <class I, class T> constexpr void* vtableForRef(RefType ref) { switch (ref) { case RefType::eRvalue: return const_cast<VTable<I>*>(vtableFor<I, T&&>()); case RefType::eLvalue: return const_cast<VTable<I>*>(vtableFor<I, T&>()); case RefType::eConstLvalue: return const_cast<VTable<I>*>(vtableFor<I, T const&>()); } return nullptr; } template < class I, class T, std::enable_if_t<std::is_reference<T>::value, int> = 0> void* execOnHeap(Op op, Data* from, void* to) { switch (op) { case Op::eNuke: break; case Op::eMove: case Op::eCopy: static_cast<Data*>(to)->pobj_ = from->pobj_; break; case Op::eType: return const_cast<void*>(static_cast<void const*>(&typeid(T))); case Op::eAddr: if (*static_cast<std::type_info const*>(to) == typeid(T)) { return from->pobj_; } throw_exception<BadPolyCast>(); case Op::eRefr: return vtableForRef<I, remove_cvref_t<T>>( static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to))); } return nullptr; } template < class I, class T, std::enable_if_t<Negation<std::is_reference<T>>::value, int> = 0> void* execOnHeap(Op op, Data* from, void* to) { switch (op) { case Op::eNuke: delete &get<T>(*from); break; case Op::eMove: static_cast<Data*>(to)->pobj_ = std::exchange(from->pobj_, nullptr); break; case Op::eCopy: detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) { static_cast<Data*>(to)->pobj_ = new T(id(get<T>(*from))); }); break; case Op::eType: return const_cast<void*>(static_cast<void const*>(&typeid(T))); case Op::eAddr: if (*static_cast<std::type_info const*>(to) == typeid(T)) { return from->pobj_; } throw_exception<BadPolyCast>(); case Op::eRefr: return vtableForRef<I, remove_cvref_t<T>>( static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to))); } return nullptr; } template <class I, class T> void* execInSitu(Op op, Data* from, void* to) { switch (op) { case Op::eNuke: get<T>(*from).~T(); break; case Op::eMove: ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_)) T(std::move(get<T>(*from))); get<T>(*from).~T(); break; case Op::eCopy: detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) { ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_)) T(id(get<T>(*from))); }); break; case Op::eType: return const_cast<void*>(static_cast<void const*>(&typeid(T))); case Op::eAddr: if (*static_cast<std::type_info const*>(to) == typeid(T)) { return &from->buff_; } throw_exception<BadPolyCast>(); case Op::eRefr: return vtableForRef<I, remove_cvref_t<T>>( static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to))); } return nullptr; } inline void* noopExec(Op op, Data*, void*) { if (op == Op::eAddr) throw_exception<BadPolyAccess>(); return const_cast<void*>(static_cast<void const*>(&typeid(void))); } template <class I> struct BasePtr { VTable<I> const* vptr_; }; template <class I, class T> constexpr void* (*getOpsImpl(std::true_type) noexcept)(Op, Data*, void*) { return &execInSitu<I, T>; } template <class I, class T> constexpr void* (*getOpsImpl(std::false_type) noexcept)(Op, Data*, void*) { return &execOnHeap<I, T>; } template <class I, class T> constexpr void* (*getOps() noexcept)(Op, Data*, void*) { return getOpsImpl<I, T>(std::integral_constant<bool, inSitu<T>()>{}); } template <class I, FOLLY_AUTO... Arch, class... S> struct VTable<I, PolyMembers<Arch...>, TypeList<S...>> : BasePtr<S>..., std::tuple<SignatureOf<Arch, I>...> { private: template <class T, FOLLY_AUTO... User> constexpr VTable(Type<T>, PolyMembers<User...>) noexcept : BasePtr<S>{vtableFor<S, T>()}..., std::tuple<SignatureOf<Arch, I>...>{thunk<T, User, I>()...}, state_{inSitu<T>() ? State::eInSitu : State::eOnHeap}, ops_{getOps<I, T>()} {} public: constexpr VTable() noexcept : BasePtr<S>{vtable<S>()}..., std::tuple<SignatureOf<Arch, I>...>{ static_cast<SignatureOf<Arch, I>>(throw_())...}, state_{State::eEmpty}, ops_{&noopExec} {} template <class T> explicit constexpr VTable(Type<T>) noexcept : VTable{Type<T>{}, MembersOf<I, T>{}} {} State state_; void* (*ops_)(Op, Data*, void*); }; template <class I> constexpr VTable<I> const& select(VTable<_t<Type<I>>> const& vtbl) noexcept { return vtbl; } template <class I> constexpr VTable<I> const& select(BasePtr<_t<Type<I>>> const& base) noexcept { return *base.vptr_; } struct PolyAccess { template <std::size_t N, typename This, typename... As> static auto call(This&& _this, As&&... args) -> decltype(static_cast<This&&>(_this).template _polyCall_<N>( static_cast<As&&>(args)...)) { static_assert( !IsInstanceOf<std::decay_t<This>, Poly>::value, "When passing a Poly<> object to call(), you must explicitly " "say which Interface to dispatch to, as in " "call<0, MyInterface>(self, args...)"); return static_cast<This&&>(_this).template _polyCall_<N>( static_cast<As&&>(args)...); } template <class Poly> using Iface = typename remove_cvref_t<Poly>::_polyInterface_; template <class Node, class Tfx = MetaIdentity> static typename remove_cvref_t<Node>::template _polySelf_<Node, Tfx> self_(); template <class T, class Poly, class I = Iface<Poly>> static decltype(auto) cast(Poly&& _this) { using Ret = AddCvrefOf<AddCvrefOf<T, I>, Poly&&>; return static_cast<Ret>( *static_cast<std::add_pointer_t<Ret>>(_this.vptr_->ops_( Op::eAddr, const_cast<Data*>(static_cast<Data const*>(&_this)), const_cast<void*>(static_cast<void const*>(&typeid(T)))))); } template <class Poly> static decltype(auto) root(Poly&& _this) noexcept { return static_cast<Poly&&>(_this)._polyRoot_(); } template <class I> static std::type_info const& type(PolyRoot<I> const& _this) noexcept { return *static_cast<std::type_info const*>( _this.vptr_->ops_(Op::eType, nullptr, nullptr)); } template <class I> static VTable<remove_cvref_t<I>> const* vtable( PolyRoot<I> const& _this) noexcept { return _this.vptr_; } template <class I> static Data* data(PolyRoot<I>& _this) noexcept { return &_this; } template <class I> static Data const* data(PolyRoot<I> const& _this) noexcept { return &_this; } template <class I> static Poly<I&&> move(PolyRoot<I&> const& _this) noexcept { return Poly<I&&>{_this, Type<I&>{}}; } template <class I> static Poly<I const&> move(PolyRoot<I const&> const& _this) noexcept { return Poly<I const&>{_this, Type<I const&>{}}; } }; template <class I, class Tail> struct PolyNode : Tail { private: friend PolyAccess; using Tail::Tail; template <std::size_t K, typename... As> decltype(auto) _polyCall_(As&&... as) { return std::get<K>(select<I>(*PolyAccess::vtable(*this)))( *PolyAccess::data(*this), static_cast<As&&>(as)...); } template <std::size_t K, typename... As> decltype(auto) _polyCall_(As&&... as) const { return std::get<K>(select<I>(*PolyAccess::vtable(*this)))( *PolyAccess::data(*this), static_cast<As&&>(as)...); } }; struct MakePolyNode { template <class I, class State> using apply = InterfaceOf<I, PolyNode<I, State>>; }; template <class I> struct PolyRoot : private PolyBase, private Data { friend PolyAccess; friend Poly<I>; friend PolyVal<I>; friend PolyRef<I>; template <class Node, class Tfx> using _polySelf_ = Poly<AddCvrefOf<MetaApply<Tfx, I>, Node>>; using _polyInterface_ = I; private: PolyRoot& _polyRoot_() noexcept { return *this; } PolyRoot const& _polyRoot_() const noexcept { return *this; } VTable<std::decay_t<I>> const* vptr_ = vtable<std::decay_t<I>>(); }; template <class I> using PolyImpl = TypeFold< InclusiveSubsumptionsOf<remove_cvref_t<I>>, PolyRoot<I>, MakePolyNode>; // A const-qualified function type means the user is trying to disambiguate // a member function pointer. template <class Fun> // Fun = R(As...) const struct Sig { template <class T> constexpr Fun T::*operator()(Fun T::*t) const /* nolint */ volatile noexcept { return t; } template <class F, class T> constexpr F T::*operator()(F T::*t) const /* nolint */ volatile noexcept { return t; } }; // A function type with no arguments means the user is trying to disambiguate // a member function pointer. template <class R> struct Sig<R()> : Sig<R() const> { using Fun = R(); using Sig<R() const>::operator(); template <class T> constexpr Fun T::*operator()(Fun T::*t) const noexcept { return t; } }; template <class R, class... As> struct SigImpl : Sig<R(As...) const> { using Fun = R(As...); using Sig<R(As...) const>::operator(); template <class T> constexpr Fun T::*operator()(Fun T::*t) const noexcept { return t; } constexpr Fun* operator()(Fun* t) const noexcept { return t; } template <class F> constexpr F* operator()(F* t) const noexcept { return t; } }; // The user could be trying to disambiguate either a member or a free function. template <class R, class... As> struct Sig<R(As...)> : SigImpl<R, As...> {}; // This case is like the one above, except we want to add an overload that // handles the case of a free function where the first argument is more // const-qualified than the user explicitly specified. template <class R, class A, class... As> struct Sig<R(A&, As...)> : SigImpl<R, A&, As...> { using CCFun = R(A const&, As...); using SigImpl<R, A&, As...>::operator(); constexpr CCFun* operator()(CCFun* t) const /* nolint */ volatile noexcept { return t; } }; template <class T, class I, class = void> struct ModelsInterface2_ : std::false_type {}; template <class T, class I> struct ModelsInterface2_< T, I, void_t< std::enable_if_t< std::is_constructible<AddCvrefOf<std::decay_t<T>, I>, T>::value>, MembersOf<std::decay_t<I>, std::decay_t<T>>>> : std::true_type {}; template <class T, class I, class = void> struct ModelsInterface_ : std::false_type {}; template <class T, class I> struct ModelsInterface_< T, I, std::enable_if_t< Negation<std::is_base_of<PolyBase, std::decay_t<T>>>::value>> : ModelsInterface2_<T, I> {}; template <class T, class I> struct ModelsInterface : ModelsInterface_<T, I> {}; template <class I1, class I2> struct ValueCompatible : std::is_base_of<I1, I2> {}; // This prevents PolyRef's converting constructors and assignment operators // from being considered as copy constructors and assignment operators: template <class I1> struct ValueCompatible<I1, I1> : std::false_type {}; template <class I1, class I2, class I2Ref> struct ReferenceCompatible : std::is_constructible<I1, I2Ref> {}; // This prevents PolyRef's converting constructors and assignment operators // from being considered as copy constructors and assignment operators: template <class I1, class I2Ref> struct ReferenceCompatible<I1, I1, I2Ref> : std::false_type {}; } // namespace detail /// \endcond } // namespace folly #undef FOLLY_AUTO