1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
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 namespace folly {
18 namespace detail {
19 
20 template <class I>
PolyVal(PolyVal && that)21 inline PolyVal<I>::PolyVal(PolyVal&& that) noexcept {
22   that.vptr_->ops_(Op::eMove, &that, static_cast<Data*>(this));
23   vptr_ = std::exchange(that.vptr_, vtable<I>());
24 }
25 
26 template <class I>
PolyVal(PolyOrNonesuch const & that)27 inline PolyVal<I>::PolyVal(PolyOrNonesuch const& that) {
28   that.vptr_->ops_(
29       Op::eCopy, const_cast<Data*>(that._data_()), PolyAccess::data(*this));
30   vptr_ = that.vptr_;
31 }
32 
33 template <class I>
~PolyVal()34 inline PolyVal<I>::~PolyVal() {
35   vptr_->ops_(Op::eNuke, this, nullptr);
36 }
37 
38 template <class I>
39 inline Poly<I>& PolyVal<I>::operator=(PolyVal that) noexcept {
40   vptr_->ops_(Op::eNuke, _data_(), nullptr);
41   that.vptr_->ops_(Op::eMove, that._data_(), _data_());
42   vptr_ = std::exchange(that.vptr_, vtable<I>());
43   return static_cast<Poly<I>&>(*this);
44 }
45 
46 template <class I>
47 template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
PolyVal(T && t)48 inline PolyVal<I>::PolyVal(T&& t) {
49   using U = std::decay_t<T>;
50   static_assert(
51       std::is_copy_constructible<U>::value || !Copyable::value,
52       "This Poly<> requires copyability, and the source object is not "
53       "copyable");
54   // The static and dynamic types should match; otherwise, this will slice.
55   assert(typeid(t) == typeid(std::decay_t<T>) ||
56        !"Dynamic and static exception types don't match. Object would "
57         "be sliced when storing in Poly.");
58   if (inSitu<U>()) {
59     auto const buff = static_cast<void*>(&_data_()->buff_);
60     ::new (buff) U(static_cast<T&&>(t));
61   } else {
62     _data_()->pobj_ = new U(static_cast<T&&>(t));
63   }
64   vptr_ = vtableFor<I, U>();
65 }
66 
67 template <class I>
68 template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>>
PolyVal(Poly<I2> that)69 inline PolyVal<I>::PolyVal(Poly<I2> that) {
70   static_assert(
71       !Copyable::value || std::is_copy_constructible<Poly<I2>>::value,
72       "This Poly<> requires copyability, and the source object is not "
73       "copyable");
74   auto* that_vptr = PolyAccess::vtable(that);
75   if (that_vptr->state_ != State::eEmpty) {
76     that_vptr->ops_(Op::eMove, PolyAccess::data(that), _data_());
77     vptr_ = &select<I>(*std::exchange(that_vptr, vtable<std::decay_t<I2>>()));
78   }
79 }
80 
81 template <class I>
82 template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
83 inline Poly<I>& PolyVal<I>::operator=(T&& t) {
84   *this = PolyVal(static_cast<T&&>(t));
85   return static_cast<Poly<I>&>(*this);
86 }
87 
88 template <class I>
89 template <class I2, std::enable_if_t<ValueCompatible<I, I2>::value, int>>
90 inline Poly<I>& PolyVal<I>::operator=(Poly<I2> that) {
91   *this = PolyVal(std::move(that));
92   return static_cast<Poly<I>&>(*this);
93 }
94 
95 template <class I>
swap(Poly<I> & that)96 inline void PolyVal<I>::swap(Poly<I>& that) noexcept {
97   switch (vptr_->state_) {
98     case State::eEmpty:
99       *this = std::move(that);
100       break;
101     case State::eOnHeap:
102       if (State::eOnHeap == that.vptr_->state_) {
103         std::swap(_data_()->pobj_, that._data_()->pobj_);
104         std::swap(vptr_, that.vptr_);
105         return;
106       }
107       FOLLY_FALLTHROUGH;
108     case State::eInSitu:
109       std::swap(
110           *this, static_cast<PolyVal<I>&>(that)); // NOTE: qualified, not ADL
111   }
112 }
113 
114 template <class I>
_polyRoot_()115 inline AddCvrefOf<PolyRoot<I>, I>& PolyRef<I>::_polyRoot_() const noexcept {
116   return const_cast<AddCvrefOf<PolyRoot<I>, I>&>(
117       static_cast<PolyRoot<I> const&>(*this));
118 }
119 
120 template <class I>
refType()121 constexpr RefType PolyRef<I>::refType() noexcept {
122   using J = std::remove_reference_t<I>;
123   return std::is_rvalue_reference<I>::value ? RefType::eRvalue
124       : std::is_const<J>::value             ? RefType::eConstLvalue
125                                             : RefType::eLvalue;
126 }
127 
128 template <class I>
129 template <class That, class I2>
PolyRef(That && that,Type<I2>)130 inline PolyRef<I>::PolyRef(That&& that, Type<I2>) {
131   auto* that_vptr = PolyAccess::vtable(PolyAccess::root(that));
132   detail::State const that_state = that_vptr->state_;
133   if (that_state == State::eEmpty) {
134     throw BadPolyAccess();
135   }
136   auto* that_data = PolyAccess::data(PolyAccess::root(that));
137   _data_()->pobj_ = that_state == State::eInSitu
138       ? const_cast<void*>(static_cast<void const*>(&that_data->buff_))
139       : that_data->pobj_;
140   this->vptr_ = &select<std::decay_t<I>>(
141       *static_cast<VTable<std::decay_t<I2>> const*>(that_vptr->ops_(
142           Op::eRefr, nullptr, reinterpret_cast<void*>(refType()))));
143 }
144 
145 template <class I>
PolyRef(PolyRef const & that)146 inline PolyRef<I>::PolyRef(PolyRef const& that) noexcept {
147   _data_()->pobj_ = that._data_()->pobj_;
148   this->vptr_ = that.vptr_;
149 }
150 
151 template <class I>
152 inline Poly<I>& PolyRef<I>::operator=(PolyRef const& that) noexcept {
153   _data_()->pobj_ = that._data_()->pobj_;
154   this->vptr_ = that.vptr_;
155   return static_cast<Poly<I>&>(*this);
156 }
157 
158 template <class I>
159 template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
PolyRef(T && t)160 inline PolyRef<I>::PolyRef(T&& t) noexcept {
161   _data_()->pobj_ =
162       const_cast<void*>(static_cast<void const*>(std::addressof(t)));
163   this->vptr_ = vtableFor<std::decay_t<I>, AddCvrefOf<std::decay_t<T>, I>>();
164 }
165 
166 template <class I>
167 template <
168     class I2,
169     std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>>
PolyRef(Poly<I2> && that)170 inline PolyRef<I>::PolyRef(Poly<I2>&& that) noexcept(
171     std::is_reference<I2>::value)
172     : PolyRef{that, Type<I2>{}} {
173   static_assert(
174       Disjunction<std::is_reference<I2>, std::is_rvalue_reference<I>>::value,
175       "Attempting to construct a Poly that is a reference to a temporary. "
176       "This is probably a mistake.");
177 }
178 
179 template <class I>
180 template <class T, std::enable_if_t<ModelsInterface<T, I>::value, int>>
181 inline Poly<I>& PolyRef<I>::operator=(T&& t) noexcept {
182   *this = PolyRef(static_cast<T&&>(t));
183   return static_cast<Poly<I>&>(*this);
184 }
185 
186 template <class I>
187 template <
188     class I2,
189     std::enable_if_t<ReferenceCompatible<I, I2, I2&&>::value, int>>
noexcept(std::is_reference<I2>::value)190 inline Poly<I>& PolyRef<I>::operator=(Poly<I2>&& that) noexcept(
191     std::is_reference<I2>::value) {
192   *this = PolyRef(std::move(that));
193   return static_cast<Poly<I>&>(*this);
194 }
195 
196 template <class I>
197 template <
198     class I2,
199     std::enable_if_t<ReferenceCompatible<I, I2, I2&>::value, int>>
noexcept(std::is_reference<I2>::value)200 inline Poly<I>& PolyRef<I>::operator=(Poly<I2>& that) noexcept(
201     std::is_reference<I2>::value) {
202   *this = PolyRef(that);
203   return static_cast<Poly<I>&>(*this);
204 }
205 
206 template <class I>
207 template <
208     class I2,
209     std::enable_if_t<ReferenceCompatible<I, I2, I2 const&>::value, int>>
noexcept(std::is_reference<I2>::value)210 inline Poly<I>& PolyRef<I>::operator=(Poly<I2> const& that) noexcept(
211     std::is_reference<I2>::value) {
212   *this = PolyRef(that);
213   return static_cast<Poly<I>&>(*this);
214 }
215 
216 template <class I>
swap(Poly<I> & that)217 inline void PolyRef<I>::swap(Poly<I>& that) noexcept {
218   std::swap(_data_()->pobj_, that._data_()->pobj_);
219   std::swap(this->vptr_, that.vptr_);
220 }
221 
222 template <class I>
get()223 inline AddCvrefOf<PolyImpl<I>, I>& PolyRef<I>::get() const noexcept {
224   return const_cast<AddCvrefOf<PolyImpl<I>, I>&>(
225       static_cast<PolyImpl<I> const&>(*this));
226 }
227 
228 } // namespace detail
229 } // namespace folly
230