1/*
2 * Copyright 2017-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <functional>
20#include <new>
21#include <tuple>
22#include <type_traits>
23#include <typeinfo>
24#include <utility>
25
26#include <folly/Traits.h>
27#include <folly/Utility.h>
28#include <folly/detail/TypeList.h>
29#include <folly/functional/Invoke.h>
30#include <folly/lang/Exception.h>
31
32#include <folly/PolyException.h>
33
34namespace folly {
35/// \cond
36namespace detail {
37template <class I>
38struct PolyRoot;
39
40using RRef_ = MetaQuoteTrait<std::add_rvalue_reference>;
41using LRef_ = MetaQuoteTrait<std::add_lvalue_reference>;
42
43template <typename T>
44struct XRef_ : Type<MetaQuoteTrait<Type>> {};
45template <typename T>
46using XRef = _t<XRef_<T>>;
47template <typename T>
48struct XRef_<T&&> : Type<MetaCompose<RRef_, XRef<T>>> {};
49template <typename T>
50struct XRef_<T&> : Type<MetaCompose<LRef_, XRef<T>>> {};
51template <typename T>
52struct XRef_<T const> : Type<MetaQuoteTrait<std::add_const>> {};
53
54template <class A, class B>
55using AddCvrefOf = MetaApply<XRef<B>, A>;
56} // namespace detail
57/// \endcond
58
59template <class I>
60struct Poly;
61
62template <class T, class I>
63detail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>&);
64
65template <class T, class I>
66detail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const&);
67
68#if !defined(__cpp_template_auto)
69#define FOLLY_AUTO class
70template <class... Ts>
71using PolyMembers = detail::TypeList<Ts...>;
72#else
73#define FOLLY_AUTO auto
74template <auto...>
75struct PolyMembers;
76#endif
77
78/// \cond
79namespace detail {
80/* *****************************************************************************
81 * IMPLEMENTATION NOTES
82 *
83
84Building the Interface
85----------------------
86
87Here is a high-level description of how Poly works. Users write an interface
88such as:
89
90 struct Mine {
91 template <class Base>
92 struct Interface {
93 int Exec() const {
94 return folly::poly_call<0>(*this);
95 }
96 }
97 template <class T>
98 using Members = folly::PolyMembers<&T::Exec>;
99 };
100
101Then they instantiate Poly<Mine>, which will have an Exec member function
102of the correct signature. The Exec member function comes from
103Mine::Interface<PolyNode<Mine, PolyRoot<Mine>>>, from which Poly<Mine> inherits.
104Here's what each piece means:
105
106- PolyRoot<I>: stores Data, which is a union of a void* (used when the Poly is
107 storing an object on the heap or a reference) and some aligned storage (used
108 when the Poly is storing an object in-situ). PolyRoot also stores a vtable
109 pointer for interface I, which is a pointer to a struct containing function
110 pointers. The function pointers are bound member functions (e.g.,
111 SomeType::Exec). More on the vtable pointer and how it is generated below.
112
113- PolyNode: provides the hooks used by folly::poly_call to dispatch to the
114correctly bound member function for this interface. In the context of an
115interface I, folly::poly_call<K>(*this, args...) will:
116 1. Fetch the vtable pointer from PolyRoot,
117 2. Select the I portion of that vtable (which, in the case of interface
118 extension, may only be a part of the total vtable),
119 3. Fetch the K-th function pointer from that vtable part,
120 4. Call through that function pointer, passing Data (from PolyRoot) and any
121 additional arguments in the folly::poly_call<K> invocation.
122
123In the case of interface extension -- for instance, if interface Mine extended
124interface Yours by inheriting from PolyExtends<Yours> -- then interface Mine
125will have a list of base interfaces in a typelist called "Subsumptions".
126Poly<Mine> will fold all the subsumed interfaces together, linearly inheriting
127from them. To take the example of an interface Mine that extends Yours,
128Poly<Mine> would inherit from this type:
129
130 Mine::Interface<
131 PolyNode<Mine,
132 Your::Interface<
133 PolyNode<Your, PolyRoot<Mine>>>>>
134
135Through linear inheritance, Poly<Mine> ends up with the public member functions
136of both interfaces, Mine and Yours.
137
138VTables
139-------
140
141As mentioned above, PolyRoot<I> stores a vtable pointer for interface I. The
142vtable is a struct whose members are function pointers. How are the types of
143those function pointers computed from the interface? A dummy type is created,
144Archetype<I>, in much the same way that Poly<I>'s base type is computed. Instead
145of PolyNode and PolyRoot, there is ArchetypeNode and ArchetypeRoot. These types
146also provide hooks for folly::poly_call, but they are dummy hooks that do
147nothing. (Actually, they call std::terminate; they will never be called.) Once
148Archetype<I> has been constructed, it is a concrete type that has all the
149member functions of the interface and its subsumed interfaces. That type is
150passed to Mine::Members, which takes the address of Archetype<I>::Exec and
151inspects the resulting member function type. This is done for each member in the
152interface. From a list of [member] function pointers, it is a simple matter of
153metaprogramming to build a struct of function pointers. std::tuple is used for
154this.
155
156An extra field is added to the tuple for a function that handles all of the
157"special" operations: destruction, copying, moving, getting the type
158information, getting the address of the stored object, and fetching a fixed-up
159vtable pointer for reference conversions (e.g., I -> I&, I& -> I const&, etc).
160
161Subsumed interfaces are handled by having VTable<IDerived> inherit from
162BasePtr<IBase>, where BasePtr<IBase> has only one member of type
163VTable<IBase> const*.
164
165Now that the type of VTable<I> is computed, how are the fields populated?
166Poly<I> default-constructs to an empty state. Its vtable pointer points to a
167vtable whose fields are initialized with the addresses of functions that do
168nothing but throw a BadPolyAccess exception. That way, if you call a member
169function on an empty Poly, you get an exception. The function pointer
170corresponding to the "special" operations points to a no-op function; copying,
171moving and destroying an empty Poly does nothing.
172
173On the other hand, when you pass an object of type T satisfying interface I to
174Poly<I>'s constructor or assignment operator, a vtable for {I,T} is reified by
175passing type T to I::Members, thereby creating a list of bindings for T's member
176functions. The address of this vtable gets stored in the PolyRoot<I> subobject,
177imbuing the Poly object with the behaviors of type T. The T object itself gets
178stored either on the heap or in the aligned storage within the Poly object
179itself, depending on the size of T and whether or not it has a noexcept move
180constructor.
181*/
182
183template <class T>
184using Uncvref = std::remove_cv_t<std::remove_reference_t<T>>;
185
186template <class T, template <class...> class U>
187struct IsInstanceOf : std::false_type {};
188
189template <class... Ts, template <class...> class U>
190struct IsInstanceOf<U<Ts...>, U> : std::true_type {};
191
192template <class T>
193using Not = Bool<!T::value>;
194
195template <class T>
196struct StaticConst {
197 static constexpr T value{};
198};
199
200template <class T>
201constexpr T StaticConst<T>::value;
202
203template <class Then>
204decltype(auto) if_constexpr(std::true_type, Then then) {
205 return then(Identity{});
206}
207
208template <class Then>
209void if_constexpr(std::false_type, Then) {}
210
211template <class Then, class Else>
212decltype(auto) if_constexpr(std::true_type, Then then, Else) {
213 return then(Identity{});
214}
215
216template <class Then, class Else>
217decltype(auto) if_constexpr(std::false_type, Then, Else else_) {
218 return else_(Identity{});
219}
220
221enum class Op : short { eNuke, eMove, eCopy, eType, eAddr, eRefr };
222
223enum class RefType : std::uintptr_t { eRvalue, eLvalue, eConstLvalue };
224
225struct Data;
226
227template <class I>
228struct PolyVal;
229
230template <class I>
231struct PolyRef;
232
233struct PolyAccess;
234
235template <class T>
236using IsPoly = IsInstanceOf<Uncvref<T>, Poly>;
237
238// Given an interface I and a concrete type T that satisfies the interface
239// I, create a list of member function bindings from members of T to members
240// of I.
241template <class I, class T>
242using MembersOf = typename I::template Members<Uncvref<T>>;
243
244// Given an interface I and a base type T, create a type that implements
245// the interface I in terms of the capabilities of T.
246template <class I, class T>
247using InterfaceOf = typename I::template Interface<T>;
248
249#if !defined(__cpp_template_auto)
250template <class T, T V>
251using Member = std::integral_constant<T, V>;
252
253template <class M>
254using MemberType = typename M::value_type;
255
256template <class M>
257inline constexpr MemberType<M> memberValue() noexcept {
258 return M::value;
259}
260
261template <class... Ts>
262struct MakeMembers {
263 template <Ts... Vs>
264 using Members = PolyMembers<Member<Ts, Vs>...>;
265};
266
267template <class... Ts>
268MakeMembers<Ts...> deduceMembers(Ts...);
269
270template <class Member, class = MemberType<Member>>
271struct MemberDef;
272
273template <class Member, class R, class D, class... As>
274struct MemberDef<Member, R (D::*)(As...)> {
275 static R value(D& d, As... as) {
276 return folly::invoke(memberValue<Member>(), d, static_cast<As&&>(as)...);
277 }
278};
279
280template <class Member, class R, class D, class... As>
281struct MemberDef<Member, R (D::*)(As...) const> {
282 static R value(D const& d, As... as) {
283 return folly::invoke(memberValue<Member>(), d, static_cast<As&&>(as)...);
284 }
285};
286
287#else
288template <auto M>
289using MemberType = decltype(M);
290
291template <auto M>
292inline constexpr MemberType<M> memberValue() noexcept {
293 return M;
294}
295#endif
296
297struct PolyBase {};
298
299template <class I, class = void>
300struct SubsumptionsOf_ {
301 using type = TypeList<>;
302};
303
304template <class I>
305using InclusiveSubsumptionsOf = TypePushFront<_t<SubsumptionsOf_<I>>, I>;
306
307template <class I>
308struct SubsumptionsOf_<I, void_t<typename I::Subsumptions>> {
309 using type = TypeJoin<TypeTransform<
310 typename I::Subsumptions,
311 MetaQuote<InclusiveSubsumptionsOf>>>;
312};
313
314template <class I>
315using SubsumptionsOf = TypeReverseUnique<_t<SubsumptionsOf_<I>>>;
316
317struct Bottom {
318 template <class T>
319 [[noreturn]] /* implicit */ operator T &&() const {
320 std::terminate();
321 }
322};
323
324using ArchetypeNode = MetaQuote<InterfaceOf>;
325
326template <class I>
327struct ArchetypeRoot;
328
329template <class I>
330using Archetype =
331 TypeFold<InclusiveSubsumptionsOf<I>, ArchetypeRoot<I>, ArchetypeNode>;
332
333struct ArchetypeBase : Bottom {
334 ArchetypeBase() = default;
335 template <class T>
336 /* implicit */ ArchetypeBase(T&&);
337 template <std::size_t, class... As>
338 [[noreturn]] Bottom _polyCall_(As&&...) const {
339 std::terminate();
340 }
341
342 friend bool operator==(ArchetypeBase const&, ArchetypeBase const&);
343 friend bool operator!=(ArchetypeBase const&, ArchetypeBase const&);
344 friend bool operator<(ArchetypeBase const&, ArchetypeBase const&);
345 friend bool operator<=(ArchetypeBase const&, ArchetypeBase const&);
346 friend bool operator>(ArchetypeBase const&, ArchetypeBase const&);
347 friend bool operator>=(ArchetypeBase const&, ArchetypeBase const&);
348 friend Bottom operator++(ArchetypeBase const&);
349 friend Bottom operator++(ArchetypeBase const&, int);
350 friend Bottom operator--(ArchetypeBase const&);
351 friend Bottom operator--(ArchetypeBase const&, int);
352 friend Bottom operator+(ArchetypeBase const&, ArchetypeBase const&);
353 friend Bottom operator+=(ArchetypeBase const&, ArchetypeBase const&);
354 friend Bottom operator-(ArchetypeBase const&, ArchetypeBase const&);
355 friend Bottom operator-=(ArchetypeBase const&, ArchetypeBase const&);
356 friend Bottom operator*(ArchetypeBase const&, ArchetypeBase const&);
357 friend Bottom operator*=(ArchetypeBase const&, ArchetypeBase const&);
358 friend Bottom operator/(ArchetypeBase const&, ArchetypeBase const&);
359 friend Bottom operator/=(ArchetypeBase const&, ArchetypeBase const&);
360 friend Bottom operator%(ArchetypeBase const&, ArchetypeBase const&);
361 friend Bottom operator%=(ArchetypeBase const&, ArchetypeBase const&);
362 friend Bottom operator<<(ArchetypeBase const&, ArchetypeBase const&);
363 friend Bottom operator<<=(ArchetypeBase const&, ArchetypeBase const&);
364 friend Bottom operator>>(ArchetypeBase const&, ArchetypeBase const&);
365 friend Bottom operator>>=(ArchetypeBase const&, ArchetypeBase const&);
366};
367
368template <class I>
369struct ArchetypeRoot : ArchetypeBase {
370 template <class Node, class Tfx>
371 using _polySelf_ = Archetype<AddCvrefOf<MetaApply<Tfx, I>, Node>>;
372 using _polyInterface_ = I;
373};
374
375struct Data {
376 Data() = default;
377 // Suppress compiler-generated copy ops to not copy anything:
378 Data(Data const&) {}
379 Data& operator=(Data const&) {
380 return *this;
381 }
382 union {
383 void* pobj_ = nullptr;
384 std::aligned_storage_t<sizeof(double[2])> buff_;
385 };
386};
387
388template <class U, class I>
389using Arg =
390 If<std::is_same<Uncvref<U>, Archetype<I>>::value,
391 Poly<AddCvrefOf<I, U const&>>,
392 U>;
393
394template <class U, class I>
395using Ret =
396 If<std::is_same<Uncvref<U>, Archetype<I>>::value,
397 AddCvrefOf<Poly<I>, U>,
398 U>;
399
400template <class Member, class I>
401struct SignatureOf_;
402
403template <class R, class C, class... As, class I>
404struct SignatureOf_<R (C::*)(As...), I> {
405 using type = Ret<R, I> (*)(Data&, Arg<As, I>...);
406};
407
408template <class R, class C, class... As, class I>
409struct SignatureOf_<R (C::*)(As...) const, I> {
410 using type = Ret<R, I> (*)(Data const&, Arg<As, I>...);
411};
412
413template <class R, class This, class... As, class I>
414struct SignatureOf_<R (*)(This&, As...), I> {
415 using type = Ret<R, I> (*)(Data&, Arg<As, I>...);
416};
417
418template <class R, class This, class... As, class I>
419struct SignatureOf_<R (*)(This const&, As...), I> {
420 using type = Ret<R, I> (*)(Data const&, Arg<As, I>...);
421};
422
423template <FOLLY_AUTO Arch, class I>
424using SignatureOf = _t<SignatureOf_<MemberType<Arch>, I>>;
425
426template <FOLLY_AUTO User, class I, class Sig = SignatureOf<User, I>>
427struct ArgTypes_;
428
429template <FOLLY_AUTO User, class I, class Ret, class Data, class... Args>
430struct ArgTypes_<User, I, Ret (*)(Data, Args...)> {
431 using type = TypeList<Args...>;
432};
433
434template <FOLLY_AUTO User, class I>
435using ArgTypes = _t<ArgTypes_<User, I>>;
436
437template <class R, class... Args>
438using FnPtr = R (*)(Args...);
439
440struct ThrowThunk {
441 template <class R, class... Args>
442 constexpr /* implicit */ operator FnPtr<R, Args...>() const noexcept {
443 struct _ {
444 static R call(Args...) {
445 throw_exception<BadPolyAccess>();
446 }
447 };
448 return &_::call;
449 }
450};
451
452inline constexpr ThrowThunk throw_() noexcept {
453 return ThrowThunk{};
454}
455
456template <class T>
457inline constexpr bool inSitu() noexcept {
458 return !std::is_reference<T>::value &&
459 sizeof(std::decay_t<T>) <= sizeof(Data) &&
460 std::is_nothrow_move_constructible<std::decay_t<T>>::value;
461}
462
463template <class T>
464T& get(Data& d) noexcept {
465 if (inSitu<T>()) {
466 return *(std::add_pointer_t<T>)static_cast<void*>(&d.buff_);
467 } else {
468 return *static_cast<std::add_pointer_t<T>>(d.pobj_);
469 }
470}
471
472template <class T>
473T const& get(Data const& d) noexcept {
474 if (inSitu<T>()) {
475 return *(std::add_pointer_t<T const>)static_cast<void const*>(&d.buff_);
476 } else {
477 return *static_cast<std::add_pointer_t<T const>>(d.pobj_);
478 }
479}
480
481enum class State : short { eEmpty, eInSitu, eOnHeap };
482
483template <class T>
484struct IsPolyRef : std::false_type {};
485
486template <class T>
487struct IsPolyRef<Poly<T&>> : std::true_type {};
488
489template <class Arg, class U>
490decltype(auto) convert(U&& u) {
491 return detail::if_constexpr(
492 StrictConjunction<
493 IsPolyRef<Uncvref<U>>,
494 Negation<std::is_convertible<U, Arg>>>(),
495 [&](auto id) -> decltype(auto) {
496 return poly_cast<Uncvref<Arg>>(id(u).get());
497 },
498 [&](auto id) -> U&& { return static_cast<U&&>(id(u)); });
499}
500
501template <class Fun>
502struct IsConstMember : std::false_type {};
503
504template <class R, class C, class... As>
505struct IsConstMember<R (C::*)(As...) const> : std::true_type {};
506
507template <class R, class C, class... As>
508struct IsConstMember<R (*)(C const&, As...)> : std::true_type {};
509
510template <
511 class T,
512 FOLLY_AUTO User,
513 class I,
514 class = ArgTypes<User, I>,
515 class = Bool<true>>
516struct ThunkFn {
517 template <class R, class D, class... As>
518 constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept {
519 return nullptr;
520 }
521};
522
523template <class T, FOLLY_AUTO User, class I, class... Args>
524struct ThunkFn<
525 T,
526 User,
527 I,
528 TypeList<Args...>,
529 Bool<
530 !std::is_const<std::remove_reference_t<T>>::value ||
531 IsConstMember<MemberType<User>>::value>> {
532 template <class R, class D, class... As>
533 constexpr /* implicit */ operator FnPtr<R, D&, As...>() const noexcept {
534 struct _ {
535 static R call(D& d, As... as) {
536 return folly::invoke(
537 memberValue<User>(),
538 get<T>(d),
539 convert<Args>(static_cast<As&&>(as))...);
540 }
541 };
542 return &_::call;
543 }
544};
545
546template <
547 class I,
548 class = MembersOf<I, Archetype<I>>,
549 class = SubsumptionsOf<I>>
550struct VTable;
551
552template <class T, FOLLY_AUTO User, class I>
553inline constexpr ThunkFn<T, User, I> thunk() noexcept {
554 return ThunkFn<T, User, I>{};
555}
556
557template <class I>
558constexpr VTable<I> const* vtable() noexcept {
559 return &StaticConst<VTable<I>>::value;
560}
561
562template <class I, class T>
563struct VTableFor : VTable<I> {
564 constexpr VTableFor() noexcept : VTable<I>{Type<T>{}} {}
565};
566
567template <class I, class T>
568constexpr VTable<I> const* vtableFor() noexcept {
569 return &StaticConst<VTableFor<I, T>>::value;
570}
571
572template <class I, class T>
573constexpr void* vtableForRef(RefType ref) {
574 switch (ref) {
575 case RefType::eRvalue:
576 return const_cast<VTable<I>*>(vtableFor<I, T&&>());
577 case RefType::eLvalue:
578 return const_cast<VTable<I>*>(vtableFor<I, T&>());
579 case RefType::eConstLvalue:
580 return const_cast<VTable<I>*>(vtableFor<I, T const&>());
581 }
582 return nullptr;
583}
584
585template <
586 class I,
587 class T,
588 std::enable_if_t<std::is_reference<T>::value, int> = 0>
589void* execOnHeap(Op op, Data* from, void* to) {
590 switch (op) {
591 case Op::eNuke:
592 break;
593 case Op::eMove:
594 case Op::eCopy:
595 static_cast<Data*>(to)->pobj_ = from->pobj_;
596 break;
597 case Op::eType:
598 return const_cast<void*>(static_cast<void const*>(&typeid(T)));
599 case Op::eAddr:
600 if (*static_cast<std::type_info const*>(to) == typeid(T)) {
601 return from->pobj_;
602 }
603 throw_exception<BadPolyCast>();
604 case Op::eRefr:
605 return vtableForRef<I, Uncvref<T>>(
606 static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to)));
607 }
608 return nullptr;
609}
610
611template <
612 class I,
613 class T,
614 std::enable_if_t<Not<std::is_reference<T>>::value, int> = 0>
615void* execOnHeap(Op op, Data* from, void* to) {
616 switch (op) {
617 case Op::eNuke:
618 delete &get<T>(*from);
619 break;
620 case Op::eMove:
621 static_cast<Data*>(to)->pobj_ = std::exchange(from->pobj_, nullptr);
622 break;
623 case Op::eCopy:
624 detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) {
625 static_cast<Data*>(to)->pobj_ = new T(id(get<T>(*from)));
626 });
627 break;
628 case Op::eType:
629 return const_cast<void*>(static_cast<void const*>(&typeid(T)));
630 case Op::eAddr:
631 if (*static_cast<std::type_info const*>(to) == typeid(T)) {
632 return from->pobj_;
633 }
634 throw_exception<BadPolyCast>();
635 case Op::eRefr:
636 return vtableForRef<I, Uncvref<T>>(
637 static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to)));
638 }
639 return nullptr;
640}
641
642template <class I, class T>
643void* execInSitu(Op op, Data* from, void* to) {
644 switch (op) {
645 case Op::eNuke:
646 get<T>(*from).~T();
647 break;
648 case Op::eMove:
649 ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_))
650 T(std::move(get<T>(*from)));
651 get<T>(*from).~T();
652 break;
653 case Op::eCopy:
654 detail::if_constexpr(std::is_copy_constructible<T>(), [&](auto id) {
655 ::new (static_cast<void*>(&static_cast<Data*>(to)->buff_))
656 T(id(get<T>(*from)));
657 });
658 break;
659 case Op::eType:
660 return const_cast<void*>(static_cast<void const*>(&typeid(T)));
661 case Op::eAddr:
662 if (*static_cast<std::type_info const*>(to) == typeid(T)) {
663 return &from->buff_;
664 }
665 throw_exception<BadPolyCast>();
666 case Op::eRefr:
667 return vtableForRef<I, Uncvref<T>>(
668 static_cast<RefType>(reinterpret_cast<std::uintptr_t>(to)));
669 }
670 return nullptr;
671}
672
673inline void* noopExec(Op op, Data*, void*) {
674 if (op == Op::eAddr)
675 throw_exception<BadPolyAccess>();
676 return const_cast<void*>(static_cast<void const*>(&typeid(void)));
677}
678
679template <class I>
680struct BasePtr {
681 VTable<I> const* vptr_;
682};
683
684template <class I, class T, std::enable_if_t<inSitu<T>(), int> = 0>
685constexpr void* (*getOps() noexcept)(Op, Data*, void*) {
686 return &execInSitu<I, T>;
687}
688
689template <class I, class T, std::enable_if_t<!inSitu<T>(), int> = 0>
690constexpr void* (*getOps() noexcept)(Op, Data*, void*) {
691 return &execOnHeap<I, T>;
692}
693
694template <class I, FOLLY_AUTO... Arch, class... S>
695struct VTable<I, PolyMembers<Arch...>, TypeList<S...>>
696 : BasePtr<S>..., std::tuple<SignatureOf<Arch, I>...> {
697 private:
698 template <class T, FOLLY_AUTO... User>
699 constexpr VTable(Type<T>, PolyMembers<User...>) noexcept
700 : BasePtr<S>{vtableFor<S, T>()}...,
701 std::tuple<SignatureOf<Arch, I>...>{thunk<T, User, I>()...},
702 state_{inSitu<T>() ? State::eInSitu : State::eOnHeap},
703 ops_{getOps<I, T>()} {}
704
705 public:
706 constexpr VTable() noexcept
707 : BasePtr<S>{vtable<S>()}...,
708 std::tuple<SignatureOf<Arch, I>...>{
709 static_cast<SignatureOf<Arch, I>>(throw_())...},
710 state_{State::eEmpty},
711 ops_{&noopExec} {}
712
713 template <class T>
714 explicit constexpr VTable(Type<T>) noexcept
715 : VTable{Type<T>{}, MembersOf<I, T>{}} {}
716
717 State state_;
718 void* (*ops_)(Op, Data*, void*);
719};
720
721template <class I>
722constexpr VTable<I> const& select(VTable<_t<Type<I>>> const& vtbl) noexcept {
723 return vtbl;
724}
725
726template <class I>
727constexpr VTable<I> const& select(BasePtr<_t<Type<I>>> const& base) noexcept {
728 return *base.vptr_;
729}
730
731struct PolyAccess {
732 template <std::size_t N, typename This, typename... As>
733 static auto call(This&& _this, As&&... args)
734 -> decltype(static_cast<This&&>(_this).template _polyCall_<N>(
735 static_cast<As&&>(args)...)) {
736 static_assert(
737 !IsInstanceOf<std::decay_t<This>, Poly>::value,
738 "When passing a Poly<> object to call(), you must explicitly "
739 "say which Interface to dispatch to, as in "
740 "call<0, MyInterface>(self, args...)");
741 return static_cast<This&&>(_this).template _polyCall_<N>(
742 static_cast<As&&>(args)...);
743 }
744
745 template <class Poly>
746 using Iface = typename Uncvref<Poly>::_polyInterface_;
747
748 template <class Node, class Tfx = MetaIdentity>
749 static typename Uncvref<Node>::template _polySelf_<Node, Tfx> self_();
750
751 template <class T, class Poly, class I = Iface<Poly>>
752 static decltype(auto) cast(Poly&& _this) {
753 using Ret = AddCvrefOf<AddCvrefOf<T, I>, Poly&&>;
754 return static_cast<Ret>(
755 *static_cast<std::add_pointer_t<Ret>>(_this.vptr_->ops_(
756 Op::eAddr,
757 const_cast<Data*>(static_cast<Data const*>(&_this)),
758 const_cast<void*>(static_cast<void const*>(&typeid(T))))));
759 }
760
761 template <class Poly>
762 static decltype(auto) root(Poly&& _this) noexcept {
763 return static_cast<Poly&&>(_this)._polyRoot_();
764 }
765
766 template <class I>
767 static std::type_info const& type(PolyRoot<I> const& _this) noexcept {
768 return *static_cast<std::type_info const*>(
769 _this.vptr_->ops_(Op::eType, nullptr, nullptr));
770 }
771
772 template <class I>
773 static VTable<Uncvref<I>> const* vtable(PolyRoot<I> const& _this) noexcept {
774 return _this.vptr_;
775 }
776
777 template <class I>
778 static Data* data(PolyRoot<I>& _this) noexcept {
779 return &_this;
780 }
781
782 template <class I>
783 static Data const* data(PolyRoot<I> const& _this) noexcept {
784 return &_this;
785 }
786
787 template <class I>
788 static Poly<I&&> move(PolyRoot<I&> const& _this) noexcept {
789 return Poly<I&&>{_this, Type<I&>{}};
790 }
791
792 template <class I>
793 static Poly<I const&> move(PolyRoot<I const&> const& _this) noexcept {
794 return Poly<I const&>{_this, Type<I const&>{}};
795 }
796};
797
798template <class I, class Tail>
799struct PolyNode : Tail {
800 private:
801 friend PolyAccess;
802 using Tail::Tail;
803
804 template <std::size_t K, typename... As>
805 decltype(auto) _polyCall_(As&&... as) {
806 return std::get<K>(select<I>(*PolyAccess::vtable(*this)))(
807 *PolyAccess::data(*this), static_cast<As&&>(as)...);
808 }
809 template <std::size_t K, typename... As>
810 decltype(auto) _polyCall_(As&&... as) const {
811 return std::get<K>(select<I>(*PolyAccess::vtable(*this)))(
812 *PolyAccess::data(*this), static_cast<As&&>(as)...);
813 }
814};
815
816struct MakePolyNode {
817 template <class I, class State>
818 using apply = InterfaceOf<I, PolyNode<I, State>>;
819};
820
821template <class I>
822struct PolyRoot : private PolyBase, private Data {
823 friend PolyAccess;
824 friend Poly<I>;
825 friend PolyVal<I>;
826 friend PolyRef<I>;
827 template <class Node, class Tfx>
828 using _polySelf_ = Poly<AddCvrefOf<MetaApply<Tfx, I>, Node>>;
829 using _polyInterface_ = I;
830
831 private:
832 PolyRoot& _polyRoot_() noexcept {
833 return *this;
834 }
835 PolyRoot const& _polyRoot_() const noexcept {
836 return *this;
837 }
838 VTable<std::decay_t<I>> const* vptr_ = vtable<std::decay_t<I>>();
839};
840
841template <class I>
842using PolyImpl =
843 TypeFold<InclusiveSubsumptionsOf<Uncvref<I>>, PolyRoot<I>, MakePolyNode>;
844
845// A const-qualified function type means the user is trying to disambiguate
846// a member function pointer.
847template <class Fun> // Fun = R(As...) const
848struct Sig {
849 template <class T>
850 constexpr Fun T::*operator()(Fun T::*t) const /* nolint */ volatile noexcept {
851 return t;
852 }
853 template <class F, class T>
854 constexpr F T::*operator()(F T::*t) const /* nolint */ volatile noexcept {
855 return t;
856 }
857};
858
859// A functon type with no arguments means the user is trying to disambiguate
860// a member function pointer.
861template <class R>
862struct Sig<R()> : Sig<R() const> {
863 using Fun = R();
864 using Sig<R() const>::operator();
865
866 template <class T>
867 constexpr Fun T::*operator()(Fun T::*t) const noexcept {
868 return t;
869 }
870};
871
872template <class R, class... As>
873struct SigImpl : Sig<R(As...) const> {
874 using Fun = R(As...);
875 using Sig<R(As...) const>::operator();
876
877 template <class T>
878 constexpr Fun T::*operator()(Fun T::*t) const noexcept {
879 return t;
880 }
881 constexpr Fun* operator()(Fun* t) const noexcept {
882 return t;
883 }
884 template <class F>
885 constexpr F* operator()(F* t) const noexcept {
886 return t;
887 }
888};
889
890// The user could be trying to disambiguate either a member or a free function.
891template <class R, class... As>
892struct Sig<R(As...)> : SigImpl<R, As...> {};
893
894// This case is like the one above, except we want to add an overload that
895// handles the case of a free function where the first argument is more
896// const-qualified than the user explicitly specified.
897template <class R, class A, class... As>
898struct Sig<R(A&, As...)> : SigImpl<R, A&, As...> {
899 using CCFun = R(A const&, As...);
900 using SigImpl<R, A&, As...>::operator();
901
902 constexpr CCFun* operator()(CCFun* t) const /* nolint */ volatile noexcept {
903 return t;
904 }
905};
906
907template <
908 class T,
909 class I,
910 class U = std::decay_t<T>,
911 std::enable_if_t<Not<std::is_base_of<PolyBase, U>>::value, int> = 0,
912 std::enable_if_t<std::is_constructible<AddCvrefOf<U, I>, T>::value, int> =
913 0,
914 class = MembersOf<std::decay_t<I>, U>>
915std::true_type modelsInterface_(int);
916template <class T, class I>
917std::false_type modelsInterface_(long);
918
919template <class T, class I>
920struct ModelsInterface : decltype(modelsInterface_<T, I>(0)) {};
921
922template <class I1, class I2>
923struct ValueCompatible : std::is_base_of<I1, I2> {};
924
925// This prevents PolyRef's converting constructors and assignment operators
926// from being considered as copy constructors and assignment operators:
927template <class I1>
928struct ValueCompatible<I1, I1> : std::false_type {};
929
930template <class I1, class I2, class I2Ref>
931struct ReferenceCompatible : std::is_constructible<I1, I2Ref> {};
932
933// This prevents PolyRef's converting constructors and assignment operators
934// from being considered as copy constructors and assignment operators:
935template <class I1, class I2Ref>
936struct ReferenceCompatible<I1, I1, I2Ref> : std::false_type {};
937
938} // namespace detail
939/// \endcond
940} // namespace folly
941
942#undef FOLLY_AUTO
943