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 | |
34 | namespace folly { |
35 | /// \cond |
36 | namespace detail { |
37 | template <class I> |
38 | struct PolyRoot; |
39 | |
40 | using RRef_ = MetaQuoteTrait<std::add_rvalue_reference>; |
41 | using LRef_ = MetaQuoteTrait<std::add_lvalue_reference>; |
42 | |
43 | template <typename T> |
44 | struct XRef_ : Type<MetaQuoteTrait<Type>> {}; |
45 | template <typename T> |
46 | using XRef = _t<XRef_<T>>; |
47 | template <typename T> |
48 | struct XRef_<T&&> : Type<MetaCompose<RRef_, XRef<T>>> {}; |
49 | template <typename T> |
50 | struct XRef_<T&> : Type<MetaCompose<LRef_, XRef<T>>> {}; |
51 | template <typename T> |
52 | struct XRef_<T const> : Type<MetaQuoteTrait<std::add_const>> {}; |
53 | |
54 | template <class A, class B> |
55 | using AddCvrefOf = MetaApply<XRef<B>, A>; |
56 | } // namespace detail |
57 | /// \endcond |
58 | |
59 | template <class I> |
60 | struct Poly; |
61 | |
62 | template <class T, class I> |
63 | detail::AddCvrefOf<T, I>& poly_cast(detail::PolyRoot<I>&); |
64 | |
65 | template <class T, class I> |
66 | detail::AddCvrefOf<T, I> const& poly_cast(detail::PolyRoot<I> const&); |
67 | |
68 | #if !defined(__cpp_template_auto) |
69 | #define FOLLY_AUTO class |
70 | template <class... Ts> |
71 | using PolyMembers = detail::TypeList<Ts...>; |
72 | #else |
73 | #define FOLLY_AUTO auto |
74 | template <auto...> |
75 | struct PolyMembers; |
76 | #endif |
77 | |
78 | /// \cond |
79 | namespace detail { |
80 | /* ***************************************************************************** |
81 | * IMPLEMENTATION NOTES |
82 | * |
83 | |
84 | Building the Interface |
85 | ---------------------- |
86 | |
87 | Here is a high-level description of how Poly works. Users write an interface |
88 | such 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 | |
101 | Then they instantiate Poly<Mine>, which will have an Exec member function |
102 | of the correct signature. The Exec member function comes from |
103 | Mine::Interface<PolyNode<Mine, PolyRoot<Mine>>>, from which Poly<Mine> inherits. |
104 | Here'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 |
114 | correctly bound member function for this interface. In the context of an |
115 | interface 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 | |
123 | In the case of interface extension -- for instance, if interface Mine extended |
124 | interface Yours by inheriting from PolyExtends<Yours> -- then interface Mine |
125 | will have a list of base interfaces in a typelist called "Subsumptions". |
126 | Poly<Mine> will fold all the subsumed interfaces together, linearly inheriting |
127 | from them. To take the example of an interface Mine that extends Yours, |
128 | Poly<Mine> would inherit from this type: |
129 | |
130 | Mine::Interface< |
131 | PolyNode<Mine, |
132 | Your::Interface< |
133 | PolyNode<Your, PolyRoot<Mine>>>>> |
134 | |
135 | Through linear inheritance, Poly<Mine> ends up with the public member functions |
136 | of both interfaces, Mine and Yours. |
137 | |
138 | VTables |
139 | ------- |
140 | |
141 | As mentioned above, PolyRoot<I> stores a vtable pointer for interface I. The |
142 | vtable is a struct whose members are function pointers. How are the types of |
143 | those function pointers computed from the interface? A dummy type is created, |
144 | Archetype<I>, in much the same way that Poly<I>'s base type is computed. Instead |
145 | of PolyNode and PolyRoot, there is ArchetypeNode and ArchetypeRoot. These types |
146 | also provide hooks for folly::poly_call, but they are dummy hooks that do |
147 | nothing. (Actually, they call std::terminate; they will never be called.) Once |
148 | Archetype<I> has been constructed, it is a concrete type that has all the |
149 | member functions of the interface and its subsumed interfaces. That type is |
150 | passed to Mine::Members, which takes the address of Archetype<I>::Exec and |
151 | inspects the resulting member function type. This is done for each member in the |
152 | interface. From a list of [member] function pointers, it is a simple matter of |
153 | metaprogramming to build a struct of function pointers. std::tuple is used for |
154 | this. |
155 | |
156 | An extra field is added to the tuple for a function that handles all of the |
157 | "special" operations: destruction, copying, moving, getting the type |
158 | information, getting the address of the stored object, and fetching a fixed-up |
159 | vtable pointer for reference conversions (e.g., I -> I&, I& -> I const&, etc). |
160 | |
161 | Subsumed interfaces are handled by having VTable<IDerived> inherit from |
162 | BasePtr<IBase>, where BasePtr<IBase> has only one member of type |
163 | VTable<IBase> const*. |
164 | |
165 | Now that the type of VTable<I> is computed, how are the fields populated? |
166 | Poly<I> default-constructs to an empty state. Its vtable pointer points to a |
167 | vtable whose fields are initialized with the addresses of functions that do |
168 | nothing but throw a BadPolyAccess exception. That way, if you call a member |
169 | function on an empty Poly, you get an exception. The function pointer |
170 | corresponding to the "special" operations points to a no-op function; copying, |
171 | moving and destroying an empty Poly does nothing. |
172 | |
173 | On the other hand, when you pass an object of type T satisfying interface I to |
174 | Poly<I>'s constructor or assignment operator, a vtable for {I,T} is reified by |
175 | passing type T to I::Members, thereby creating a list of bindings for T's member |
176 | functions. The address of this vtable gets stored in the PolyRoot<I> subobject, |
177 | imbuing the Poly object with the behaviors of type T. The T object itself gets |
178 | stored either on the heap or in the aligned storage within the Poly object |
179 | itself, depending on the size of T and whether or not it has a noexcept move |
180 | constructor. |
181 | */ |
182 | |
183 | template <class T> |
184 | using Uncvref = std::remove_cv_t<std::remove_reference_t<T>>; |
185 | |
186 | template <class T, template <class...> class U> |
187 | struct IsInstanceOf : std::false_type {}; |
188 | |
189 | template <class... Ts, template <class...> class U> |
190 | struct IsInstanceOf<U<Ts...>, U> : std::true_type {}; |
191 | |
192 | template <class T> |
193 | using Not = Bool<!T::value>; |
194 | |
195 | template <class T> |
196 | struct StaticConst { |
197 | static constexpr T value{}; |
198 | }; |
199 | |
200 | template <class T> |
201 | constexpr T StaticConst<T>::value; |
202 | |
203 | template <class Then> |
204 | decltype(auto) if_constexpr(std::true_type, Then then) { |
205 | return then(Identity{}); |
206 | } |
207 | |
208 | template <class Then> |
209 | void if_constexpr(std::false_type, Then) {} |
210 | |
211 | template <class Then, class Else> |
212 | decltype(auto) if_constexpr(std::true_type, Then then, Else) { |
213 | return then(Identity{}); |
214 | } |
215 | |
216 | template <class Then, class Else> |
217 | decltype(auto) if_constexpr(std::false_type, Then, Else else_) { |
218 | return else_(Identity{}); |
219 | } |
220 | |
221 | enum class Op : short { eNuke, eMove, eCopy, eType, eAddr, eRefr }; |
222 | |
223 | enum class RefType : std::uintptr_t { eRvalue, eLvalue, eConstLvalue }; |
224 | |
225 | struct Data; |
226 | |
227 | template <class I> |
228 | struct PolyVal; |
229 | |
230 | template <class I> |
231 | struct PolyRef; |
232 | |
233 | struct PolyAccess; |
234 | |
235 | template <class T> |
236 | using 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. |
241 | template <class I, class T> |
242 | using 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. |
246 | template <class I, class T> |
247 | using InterfaceOf = typename I::template Interface<T>; |
248 | |
249 | #if !defined(__cpp_template_auto) |
250 | template <class T, T V> |
251 | using Member = std::integral_constant<T, V>; |
252 | |
253 | template <class M> |
254 | using MemberType = typename M::value_type; |
255 | |
256 | template <class M> |
257 | inline constexpr MemberType<M> memberValue() noexcept { |
258 | return M::value; |
259 | } |
260 | |
261 | template <class... Ts> |
262 | struct MakeMembers { |
263 | template <Ts... Vs> |
264 | using Members = PolyMembers<Member<Ts, Vs>...>; |
265 | }; |
266 | |
267 | template <class... Ts> |
268 | MakeMembers<Ts...> deduceMembers(Ts...); |
269 | |
270 | template <class Member, class = MemberType<Member>> |
271 | struct MemberDef; |
272 | |
273 | template <class Member, class R, class D, class... As> |
274 | struct 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 | |
280 | template <class Member, class R, class D, class... As> |
281 | struct 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 |
288 | template <auto M> |
289 | using MemberType = decltype(M); |
290 | |
291 | template <auto M> |
292 | inline constexpr MemberType<M> memberValue() noexcept { |
293 | return M; |
294 | } |
295 | #endif |
296 | |
297 | struct PolyBase {}; |
298 | |
299 | template <class I, class = void> |
300 | struct SubsumptionsOf_ { |
301 | using type = TypeList<>; |
302 | }; |
303 | |
304 | template <class I> |
305 | using InclusiveSubsumptionsOf = TypePushFront<_t<SubsumptionsOf_<I>>, I>; |
306 | |
307 | template <class I> |
308 | struct SubsumptionsOf_<I, void_t<typename I::Subsumptions>> { |
309 | using type = TypeJoin<TypeTransform< |
310 | typename I::Subsumptions, |
311 | MetaQuote<InclusiveSubsumptionsOf>>>; |
312 | }; |
313 | |
314 | template <class I> |
315 | using SubsumptionsOf = TypeReverseUnique<_t<SubsumptionsOf_<I>>>; |
316 | |
317 | struct Bottom { |
318 | template <class T> |
319 | [[noreturn]] /* implicit */ operator T &&() const { |
320 | std::terminate(); |
321 | } |
322 | }; |
323 | |
324 | using ArchetypeNode = MetaQuote<InterfaceOf>; |
325 | |
326 | template <class I> |
327 | struct ArchetypeRoot; |
328 | |
329 | template <class I> |
330 | using Archetype = |
331 | TypeFold<InclusiveSubsumptionsOf<I>, ArchetypeRoot<I>, ArchetypeNode>; |
332 | |
333 | struct 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 | |
368 | template <class I> |
369 | struct ArchetypeRoot : ArchetypeBase { |
370 | template <class Node, class Tfx> |
371 | using _polySelf_ = Archetype<AddCvrefOf<MetaApply<Tfx, I>, Node>>; |
372 | using _polyInterface_ = I; |
373 | }; |
374 | |
375 | struct 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 | |
388 | template <class U, class I> |
389 | using Arg = |
390 | If<std::is_same<Uncvref<U>, Archetype<I>>::value, |
391 | Poly<AddCvrefOf<I, U const&>>, |
392 | U>; |
393 | |
394 | template <class U, class I> |
395 | using Ret = |
396 | If<std::is_same<Uncvref<U>, Archetype<I>>::value, |
397 | AddCvrefOf<Poly<I>, U>, |
398 | U>; |
399 | |
400 | template <class Member, class I> |
401 | struct SignatureOf_; |
402 | |
403 | template <class R, class C, class... As, class I> |
404 | struct SignatureOf_<R (C::*)(As...), I> { |
405 | using type = Ret<R, I> (*)(Data&, Arg<As, I>...); |
406 | }; |
407 | |
408 | template <class R, class C, class... As, class I> |
409 | struct SignatureOf_<R (C::*)(As...) const, I> { |
410 | using type = Ret<R, I> (*)(Data const&, Arg<As, I>...); |
411 | }; |
412 | |
413 | template <class R, class This, class... As, class I> |
414 | struct SignatureOf_<R (*)(This&, As...), I> { |
415 | using type = Ret<R, I> (*)(Data&, Arg<As, I>...); |
416 | }; |
417 | |
418 | template <class R, class This, class... As, class I> |
419 | struct SignatureOf_<R (*)(This const&, As...), I> { |
420 | using type = Ret<R, I> (*)(Data const&, Arg<As, I>...); |
421 | }; |
422 | |
423 | template <FOLLY_AUTO Arch, class I> |
424 | using SignatureOf = _t<SignatureOf_<MemberType<Arch>, I>>; |
425 | |
426 | template <FOLLY_AUTO User, class I, class Sig = SignatureOf<User, I>> |
427 | struct ArgTypes_; |
428 | |
429 | template <FOLLY_AUTO User, class I, class Ret, class Data, class... Args> |
430 | struct ArgTypes_<User, I, Ret (*)(Data, Args...)> { |
431 | using type = TypeList<Args...>; |
432 | }; |
433 | |
434 | template <FOLLY_AUTO User, class I> |
435 | using ArgTypes = _t<ArgTypes_<User, I>>; |
436 | |
437 | template <class R, class... Args> |
438 | using FnPtr = R (*)(Args...); |
439 | |
440 | struct 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 | |
452 | inline constexpr ThrowThunk throw_() noexcept { |
453 | return ThrowThunk{}; |
454 | } |
455 | |
456 | template <class T> |
457 | inline 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 | |
463 | template <class T> |
464 | T& 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 | |
472 | template <class T> |
473 | T 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 | |
481 | enum class State : short { eEmpty, eInSitu, eOnHeap }; |
482 | |
483 | template <class T> |
484 | struct IsPolyRef : std::false_type {}; |
485 | |
486 | template <class T> |
487 | struct IsPolyRef<Poly<T&>> : std::true_type {}; |
488 | |
489 | template <class Arg, class U> |
490 | decltype(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 | |
501 | template <class Fun> |
502 | struct IsConstMember : std::false_type {}; |
503 | |
504 | template <class R, class C, class... As> |
505 | struct IsConstMember<R (C::*)(As...) const> : std::true_type {}; |
506 | |
507 | template <class R, class C, class... As> |
508 | struct IsConstMember<R (*)(C const&, As...)> : std::true_type {}; |
509 | |
510 | template < |
511 | class T, |
512 | FOLLY_AUTO User, |
513 | class I, |
514 | class = ArgTypes<User, I>, |
515 | class = Bool<true>> |
516 | struct 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 | |
523 | template <class T, FOLLY_AUTO User, class I, class... Args> |
524 | struct 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 | |
546 | template < |
547 | class I, |
548 | class = MembersOf<I, Archetype<I>>, |
549 | class = SubsumptionsOf<I>> |
550 | struct VTable; |
551 | |
552 | template <class T, FOLLY_AUTO User, class I> |
553 | inline constexpr ThunkFn<T, User, I> thunk() noexcept { |
554 | return ThunkFn<T, User, I>{}; |
555 | } |
556 | |
557 | template <class I> |
558 | constexpr VTable<I> const* vtable() noexcept { |
559 | return &StaticConst<VTable<I>>::value; |
560 | } |
561 | |
562 | template <class I, class T> |
563 | struct VTableFor : VTable<I> { |
564 | constexpr VTableFor() noexcept : VTable<I>{Type<T>{}} {} |
565 | }; |
566 | |
567 | template <class I, class T> |
568 | constexpr VTable<I> const* vtableFor() noexcept { |
569 | return &StaticConst<VTableFor<I, T>>::value; |
570 | } |
571 | |
572 | template <class I, class T> |
573 | constexpr 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 | |
585 | template < |
586 | class I, |
587 | class T, |
588 | std::enable_if_t<std::is_reference<T>::value, int> = 0> |
589 | void* 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 | |
611 | template < |
612 | class I, |
613 | class T, |
614 | std::enable_if_t<Not<std::is_reference<T>>::value, int> = 0> |
615 | void* 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 | |
642 | template <class I, class T> |
643 | void* 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 | |
673 | inline 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 | |
679 | template <class I> |
680 | struct BasePtr { |
681 | VTable<I> const* vptr_; |
682 | }; |
683 | |
684 | template <class I, class T, std::enable_if_t<inSitu<T>(), int> = 0> |
685 | constexpr void* (*getOps() noexcept)(Op, Data*, void*) { |
686 | return &execInSitu<I, T>; |
687 | } |
688 | |
689 | template <class I, class T, std::enable_if_t<!inSitu<T>(), int> = 0> |
690 | constexpr void* (*getOps() noexcept)(Op, Data*, void*) { |
691 | return &execOnHeap<I, T>; |
692 | } |
693 | |
694 | template <class I, FOLLY_AUTO... Arch, class... S> |
695 | struct 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 | |
721 | template <class I> |
722 | constexpr VTable<I> const& select(VTable<_t<Type<I>>> const& vtbl) noexcept { |
723 | return vtbl; |
724 | } |
725 | |
726 | template <class I> |
727 | constexpr VTable<I> const& select(BasePtr<_t<Type<I>>> const& base) noexcept { |
728 | return *base.vptr_; |
729 | } |
730 | |
731 | struct 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 | |
798 | template <class I, class Tail> |
799 | struct 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 | |
816 | struct MakePolyNode { |
817 | template <class I, class State> |
818 | using apply = InterfaceOf<I, PolyNode<I, State>>; |
819 | }; |
820 | |
821 | template <class I> |
822 | struct 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 | |
841 | template <class I> |
842 | using 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. |
847 | template <class Fun> // Fun = R(As...) const |
848 | struct 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. |
861 | template <class R> |
862 | struct 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 | |
872 | template <class R, class... As> |
873 | struct 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. |
891 | template <class R, class... As> |
892 | struct 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. |
897 | template <class R, class A, class... As> |
898 | struct 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 | |
907 | template < |
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>> |
915 | std::true_type modelsInterface_(int); |
916 | template <class T, class I> |
917 | std::false_type modelsInterface_(long); |
918 | |
919 | template <class T, class I> |
920 | struct ModelsInterface : decltype(modelsInterface_<T, I>(0)) {}; |
921 | |
922 | template <class I1, class I2> |
923 | struct 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: |
927 | template <class I1> |
928 | struct ValueCompatible<I1, I1> : std::false_type {}; |
929 | |
930 | template <class I1, class I2, class I2Ref> |
931 | struct 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: |
935 | template <class I1, class I2Ref> |
936 | struct ReferenceCompatible<I1, I1, I2Ref> : std::false_type {}; |
937 | |
938 | } // namespace detail |
939 | /// \endcond |
940 | } // namespace folly |
941 | |
942 | #undef FOLLY_AUTO |
943 | |