Add <new> header for placement new
[folly.git] / folly / Function-inl.h
1 /*
2  * Copyright 2016 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 namespace folly {
20 namespace detail {
21 namespace function {
22
23 // ---------------------------------------------------------------------------
24 // HELPER TYPES
25
26 enum class AllocationStatus { EMPTY, EMBEDDED, ALLOCATED };
27
28 // ---------------------------------------------------------------------------
29 // EXECUTOR CLASSES
30
31 // function::ExecutorIf
32 template <typename FunctionType>
33 class Executors<FunctionType>::ExecutorIf
34     : public Executors<FunctionType>::Traits::ExecutorMixin {
35  protected:
36   ExecutorIf(InvokeFunctionPtr invoke_ptr)
37       : Traits::ExecutorMixin(invoke_ptr){};
38
39  public:
40   // executors are neither copyable nor movable
41   ExecutorIf(ExecutorIf const&) = delete;
42   ExecutorIf& operator=(ExecutorIf const&) = delete;
43   ExecutorIf(ExecutorIf&&) = delete;
44   ExecutorIf& operator=(ExecutorIf&&) = delete;
45
46   virtual ~ExecutorIf() {}
47   virtual detail::function::AllocationStatus getAllocationStatus() const
48       noexcept = 0;
49   virtual std::pair<std::type_info const&, void*> target() const noexcept = 0;
50
51   // moveTo: move this executor to a different place
52   // preconditions:
53   // * *this is a valid executor object (derived from ExecutorIf)
54   // * the memory at [dest; dest+size) may be overwritten
55   // postconditions:
56   // * *this is an EmptyExecutor
57   // * *dest is a valid executor object (derived from ExecutorIf)
58   // You can move this executor into one for a non-const or const
59   // function.
60   virtual void moveTo(
61       typename NonConstFunctionExecutors::ExecutorIf* dest,
62       size_t size,
63       FunctionMoveCtor throws) = 0;
64   virtual void moveTo(
65       typename ConstFunctionExecutors::ExecutorIf* dest,
66       size_t size,
67       FunctionMoveCtor throws) = 0;
68 };
69
70 // function::EmptyExecutor
71 template <typename FunctionType>
72 class Executors<FunctionType>::EmptyExecutor final
73     : public Executors<FunctionType>::ExecutorIf {
74  public:
75   EmptyExecutor() noexcept : ExecutorIf(&EmptyExecutor::invokeEmpty) {}
76   ~EmptyExecutor() {}
77   detail::function::AllocationStatus getAllocationStatus() const noexcept {
78     return detail::function::AllocationStatus::EMPTY;
79   }
80
81   std::pair<std::type_info const&, void*> target() const noexcept {
82     return {typeid(void), nullptr};
83   }
84
85   template <typename DestinationExecutors>
86   void moveToImpl(typename DestinationExecutors::ExecutorIf* dest) noexcept {
87     new (dest) typename DestinationExecutors::EmptyExecutor();
88   }
89
90   void moveTo(
91       typename NonConstFunctionExecutors::ExecutorIf* dest,
92       size_t /*size*/,
93       FunctionMoveCtor /*throws*/) noexcept {
94     moveToImpl<Executors<typename Traits::NonConstFunctionType>>(dest);
95   }
96   void moveTo(
97       typename ConstFunctionExecutors::ExecutorIf* dest,
98       size_t /*size*/,
99       FunctionMoveCtor /*throws*/) noexcept {
100     moveToImpl<Executors<typename Traits::ConstFunctionType>>(dest);
101   }
102 };
103
104 // function::FunctorPtrExecutor
105 template <typename FunctionType>
106 template <typename F, typename SelectFunctionTag>
107 class Executors<FunctionType>::FunctorPtrExecutor final
108     : public Executors<FunctionType>::ExecutorIf {
109  public:
110   FunctorPtrExecutor(F&& f)
111       : ExecutorIf(
112             &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
113         functorPtr_(new F(std::move(f))) {}
114   FunctorPtrExecutor(F const& f)
115       : ExecutorIf(
116             &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
117         functorPtr_(new F(f)) {}
118   FunctorPtrExecutor(std::unique_ptr<F> f)
119       : ExecutorIf(
120             &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
121         functorPtr_(std::move(f)) {}
122   ~FunctorPtrExecutor() {}
123   detail::function::AllocationStatus getAllocationStatus() const noexcept {
124     return detail::function::AllocationStatus::ALLOCATED;
125   }
126
127   static auto getFunctor(
128       typename Traits::template QualifiedPointer<ExecutorIf> self) ->
129       typename SelectFunctionTag::template QualifiedPointer<F> {
130     return FunctorPtrExecutor::selectFunctionHelper(
131         static_cast<
132             typename Traits::template QualifiedPointer<FunctorPtrExecutor>>(
133             self)
134             ->functorPtr_.get(),
135         SelectFunctionTag());
136   }
137
138   std::pair<std::type_info const&, void*> target() const noexcept {
139     return {typeid(F), const_cast<F*>(functorPtr_.get())};
140   }
141
142   template <typename DestinationExecutors>
143   void moveToImpl(typename DestinationExecutors::ExecutorIf* dest) noexcept {
144     new (dest) typename DestinationExecutors::
145         template FunctorPtrExecutor<F, SelectFunctionTag>(
146             std::move(functorPtr_));
147     this->~FunctorPtrExecutor();
148     new (this) EmptyExecutor();
149   }
150
151   void moveTo(
152       typename NonConstFunctionExecutors::ExecutorIf* dest,
153       size_t /*size*/,
154       FunctionMoveCtor /*throws*/) noexcept {
155     moveToImpl<Executors<typename Traits::NonConstFunctionType>>(dest);
156   }
157   void moveTo(
158       typename ConstFunctionExecutors::ExecutorIf* dest,
159       size_t /*size*/,
160       FunctionMoveCtor /*throws*/) noexcept {
161     moveToImpl<Executors<typename Traits::ConstFunctionType>>(dest);
162   }
163
164  private:
165   std::unique_ptr<F> functorPtr_;
166 };
167
168 // function::FunctorExecutor
169 template <typename FunctionType>
170 template <typename F, typename SelectFunctionTag>
171 class Executors<FunctionType>::FunctorExecutor final
172     : public Executors<FunctionType>::ExecutorIf {
173  public:
174   static constexpr bool kFunctorIsNTM =
175       std::is_nothrow_move_constructible<F>::value;
176
177   FunctorExecutor(F&& f)
178       : ExecutorIf(&FunctorExecutor::template invokeFunctor<FunctorExecutor>),
179         functor_(std::move(f)) {}
180   FunctorExecutor(F const& f)
181       : ExecutorIf(&FunctorExecutor::template invokeFunctor<FunctorExecutor>),
182         functor_(f) {}
183   ~FunctorExecutor() {}
184   detail::function::AllocationStatus getAllocationStatus() const noexcept {
185     return detail::function::AllocationStatus::EMBEDDED;
186   }
187
188   static auto getFunctor(
189       typename Traits::template QualifiedPointer<ExecutorIf> self) ->
190       typename SelectFunctionTag::template QualifiedPointer<F> {
191     return FunctorExecutor::selectFunctionHelper(
192         &static_cast<
193              typename Traits::template QualifiedPointer<FunctorExecutor>>(self)
194              ->functor_,
195         SelectFunctionTag());
196   }
197
198   std::pair<std::type_info const&, void*> target() const noexcept {
199     return {typeid(F), const_cast<F*>(&functor_)};
200   }
201
202   template <typename DestinationExecutors>
203   void moveToImpl(
204       typename DestinationExecutors::ExecutorIf* dest,
205       size_t size,
206       FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
207     if ((kFunctorIsNTM || throws == FunctionMoveCtor::MAY_THROW) &&
208         size >= sizeof(*this)) {
209       // Either functor_ is no-except-movable or no-except-movability is
210       // not requested *and* functor_ fits into destination
211       // => functor_ will be moved into a FunctorExecutor at dest
212       new (dest) typename DestinationExecutors::
213           template FunctorExecutor<F, SelectFunctionTag>(std::move(functor_));
214     } else {
215       // Either functor_ may throw when moved and no-except-movabilty is
216       // requested *or* the functor is too big to fit into destination
217       // => functor_ will be moved into a FunctorPtrExecutor. This will
218       // move functor_ onto the heap. The FunctorPtrExecutor object
219       // contains a unique_ptr.
220       new (dest) typename DestinationExecutors::
221           template FunctorPtrExecutor<F, SelectFunctionTag>(
222               std::move(functor_));
223     }
224     this->~FunctorExecutor();
225     new (this) EmptyExecutor();
226   }
227   void moveTo(
228       typename NonConstFunctionExecutors::ExecutorIf* dest,
229       size_t size,
230       FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
231     moveToImpl<Executors<typename Traits::NonConstFunctionType>>(
232         dest, size, throws);
233   }
234   void moveTo(
235       typename ConstFunctionExecutors::ExecutorIf* dest,
236       size_t size,
237       FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
238     moveToImpl<Executors<typename Traits::ConstFunctionType>>(
239         dest, size, throws);
240   }
241
242  private:
243   F functor_;
244 };
245 } // namespace function
246 } // namespace detail
247
248 // ---------------------------------------------------------------------------
249 // MOVE CONSTRUCTORS & MOVE ASSIGNMENT OPERATORS
250
251 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
252 Function<FunctionType, NTM, EmbedFunctorSize>::Function(
253     Function&& other) noexcept(hasNoExceptMoveCtor()) {
254   other.access<ExecutorIf>()->moveTo(access<ExecutorIf>(), kStorageSize, NTM);
255 }
256
257 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
258 Function<FunctionType, NTM, EmbedFunctorSize>&
259 Function<FunctionType, NTM, EmbedFunctorSize>::operator=(
260     Function&& rhs) noexcept(hasNoExceptMoveCtor()) {
261   destroyExecutor();
262   SCOPE_FAIL {
263     initializeEmptyExecutor();
264   };
265   rhs.access<ExecutorIf>()->moveTo(access<ExecutorIf>(), kStorageSize, NTM);
266   return *this;
267 }
268
269 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
270 template <
271     typename OtherFunctionType,
272     FunctionMoveCtor OtherNTM,
273     size_t OtherEmbedFunctorSize>
274 Function<FunctionType, NTM, EmbedFunctorSize>::Function(
275     Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&& other,
276     typename std::enable_if<std::is_same<
277         typename Traits::NonConstFunctionType,
278         typename detail::function::FunctionTypeTraits<
279             OtherFunctionType>::NonConstFunctionType>::value>::
280         type*) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW &&
281         EmbedFunctorSize >= OtherEmbedFunctorSize) {
282   using OtherFunction =
283       Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
284
285   static_assert(
286       !Traits::IsConst::value || OtherFunction::Traits::IsConst::value,
287       "Function: cannot move Function<R(Args...)> into "
288       "Function<R(Args...) const>; "
289       "use folly::constCastFunction!");
290
291   other.template access<typename OtherFunction::ExecutorIf>()->moveTo(
292       access<ExecutorIf>(), kStorageSize, NTM);
293 }
294
295 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
296 template <
297     typename OtherFunctionType,
298     FunctionMoveCtor OtherNTM,
299     size_t OtherEmbedFunctorSize>
300 Function<FunctionType, NTM, EmbedFunctorSize>&
301 Function<FunctionType, NTM, EmbedFunctorSize>::operator=(
302     Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&&
303         rhs) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW) {
304   using OtherFunction =
305       Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
306
307   static_assert(
308       std::is_same<
309           typename Traits::NonConstFunctionType,
310           typename OtherFunction::Traits::NonConstFunctionType>::value,
311       "Function: cannot move into a Function with different "
312       "parameter signature");
313   static_assert(
314       !Traits::IsConst::value || OtherFunction::Traits::IsConst::value,
315       "Function: cannot move Function<R(Args...)> into "
316       "Function<R(Args...) const>; "
317       "use folly::constCastFunction!");
318
319   destroyExecutor();
320   SCOPE_FAIL {
321     initializeEmptyExecutor();
322   };
323   rhs.template access<typename OtherFunction::ExecutorIf>()->moveTo(
324       access<ExecutorIf>(), kStorageSize, NTM);
325   return *this;
326 }
327
328 // ---------------------------------------------------------------------------
329 // PUBLIC METHODS
330
331 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
332 template <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
333 inline void Function<FunctionType, NTM, EmbedFunctorSize>::
334     swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
335         hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW) {
336   Function<FunctionType, NTM, EmbedFunctorSize> tmp(std::move(*this));
337   *this = std::move(o);
338   o = std::move(tmp);
339 }
340
341 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
342 Function<FunctionType, NTM, EmbedFunctorSize>::operator bool() const noexcept {
343   return access<ExecutorIf>()->getAllocationStatus() !=
344       detail::function::AllocationStatus::EMPTY;
345 }
346
347 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
348 inline bool Function<FunctionType, NTM, EmbedFunctorSize>::hasAllocatedMemory()
349     const noexcept {
350   return access<ExecutorIf>()->getAllocationStatus() ==
351       detail::function::AllocationStatus::ALLOCATED;
352 }
353
354 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
355 inline std::type_info const&
356 Function<FunctionType, NTM, EmbedFunctorSize>::target_type() const noexcept {
357   return access<ExecutorIf>()->target().first;
358 }
359
360 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
361 template <typename T>
362 T* Function<FunctionType, NTM, EmbedFunctorSize>::target() noexcept {
363   auto type_target_pair = access<ExecutorIf>()->target();
364   if (type_target_pair.first == typeid(T)) {
365     return static_cast<T*>(type_target_pair.second);
366   }
367   return nullptr;
368 }
369
370 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
371 template <typename T>
372 T const* Function<FunctionType, NTM, EmbedFunctorSize>::target() const
373     noexcept {
374   auto type_target_pair = access<ExecutorIf>()->target();
375   if (type_target_pair.first == typeid(T)) {
376     return static_cast<T const*>(type_target_pair.second);
377   }
378   return nullptr;
379 }
380
381 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
382     Function<
383         typename detail::function::FunctionTypeTraits<
384             FunctionType>::ConstFunctionType,
385         NTM,
386         EmbedFunctorSize>
387     Function<FunctionType, NTM, EmbedFunctorSize>::castToConstFunction() &&
388     noexcept(hasNoExceptMoveCtor()) {
389   using ReturnType =
390       Function<typename Traits::ConstFunctionType, NTM, EmbedFunctorSize>;
391
392   ReturnType result;
393   result.destroyExecutor();
394   SCOPE_FAIL {
395     result.initializeEmptyExecutor();
396   };
397   access<ExecutorIf>()->moveTo(
398       result.template access<typename ReturnType::ExecutorIf>(),
399       kStorageSize,
400       NTM);
401   return result;
402 }
403
404 // ---------------------------------------------------------------------------
405 // PRIVATE METHODS
406
407 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
408 template <typename T>
409 T* Function<FunctionType, NTM, EmbedFunctorSize>::access() {
410   static_assert(
411       std::is_base_of<ExecutorIf, T>::value,
412       "Function::access<T>: ExecutorIf must be base class of T "
413       "(this is a bug in the Function implementation)");
414   static_assert(
415       sizeof(T) <= kStorageSize,
416       "Requested access to object not fitting into ExecutorStore "
417       "(this is a bug in the Function implementation)");
418
419   return reinterpret_cast<T*>(&data_);
420 }
421
422 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
423 template <typename T>
424 T const* Function<FunctionType, NTM, EmbedFunctorSize>::access() const {
425   static_assert(
426       std::is_base_of<ExecutorIf, T>::value,
427       "Function::access<T>: ExecutorIf must be base class of T "
428       "(this is a bug in the Function implementation)");
429   static_assert(
430       sizeof(T) <= kStorageSize,
431       "Requested access to object not fitting into ExecutorStore "
432       "(this is a bug in the Function implementation)");
433
434   return reinterpret_cast<T const*>(&data_);
435 }
436
437 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
438 void Function<FunctionType, NTM, EmbedFunctorSize>::
439     initializeEmptyExecutor() noexcept {
440   new (access<EmptyExecutor>()) EmptyExecutor;
441 }
442
443 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
444 template <typename F>
445 void Function<FunctionType, NTM, EmbedFunctorSize>::
446     createExecutor(F&& f) noexcept(
447         noexcept(typename std::decay<F>::type(std::forward<F>(f)))) {
448   using ValueType = typename std::decay<F>::type;
449   static constexpr bool kFunctorIsNTM =
450       std::is_nothrow_move_constructible<ValueType>::value;
451   using ExecutorType = typename std::conditional<
452       (sizeof(FunctorExecutor<
453               ValueType,
454               typename Traits::DefaultSelectFunctionTag>) > kStorageSize ||
455        (hasNoExceptMoveCtor() && !kFunctorIsNTM)),
456       FunctorPtrExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>,
457       FunctorExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>>::
458       type;
459   new (access<ExecutorType>()) ExecutorType(std::forward<F>(f));
460 }
461
462 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
463 void Function<FunctionType, NTM, EmbedFunctorSize>::destroyExecutor() noexcept {
464   access<ExecutorIf>()->~ExecutorIf();
465 }
466
467 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
468 struct Function<FunctionType, NTM, EmbedFunctorSize>::MinStorageSize {
469   using NotEmbeddedFunctor =
470       FunctorPtrExecutor<void(void), detail::function::SelectConstFunctionTag>;
471
472   using EmbeddedFunctor = FunctorExecutor<
473       typename std::aligned_storage<
474           constexpr_max(EmbedFunctorSize, sizeof(void (*)(void)))>::type,
475       detail::function::SelectConstFunctionTag>;
476
477   static constexpr size_t value =
478       constexpr_max(sizeof(NotEmbeddedFunctor), sizeof(EmbeddedFunctor));
479
480   static_assert(
481       sizeof(EmptyExecutor) <= value,
482       "Internal error in Function: EmptyExecutor does not fit "
483       "in storage");
484 };
485
486 } // namespace folly