2 * Copyright 2016 Facebook, Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 // ---------------------------------------------------------------------------
26 enum class AllocationStatus { EMPTY, EMBEDDED, ALLOCATED };
28 // ---------------------------------------------------------------------------
31 // function::ExecutorIf
32 template <typename FunctionType>
33 class Executors<FunctionType>::ExecutorIf
34 : public Executors<FunctionType>::Traits::ExecutorMixin {
36 ExecutorIf(InvokeFunctionPtr invoke_ptr)
37 : Traits::ExecutorMixin(invoke_ptr){};
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;
46 virtual ~ExecutorIf() {}
47 virtual detail::function::AllocationStatus getAllocationStatus() const
49 virtual std::pair<std::type_info const&, void*> target() const noexcept = 0;
51 // moveTo: move this executor to a different place
53 // * *this is a valid executor object (derived from ExecutorIf)
54 // * the memory at [dest; dest+size) may be overwritten
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
61 typename NonConstFunctionExecutors::ExecutorIf* dest,
63 FunctionMoveCtor throws) = 0;
65 typename ConstFunctionExecutors::ExecutorIf* dest,
67 FunctionMoveCtor throws) = 0;
70 // function::EmptyExecutor
71 template <typename FunctionType>
72 class Executors<FunctionType>::EmptyExecutor final
73 : public Executors<FunctionType>::ExecutorIf {
75 EmptyExecutor() noexcept : ExecutorIf(&EmptyExecutor::invokeEmpty) {}
77 detail::function::AllocationStatus getAllocationStatus() const noexcept {
78 return detail::function::AllocationStatus::EMPTY;
81 std::pair<std::type_info const&, void*> target() const noexcept {
82 return {typeid(void), nullptr};
85 template <typename DestinationExecutors>
86 void moveToImpl(typename DestinationExecutors::ExecutorIf* dest) noexcept {
87 new (dest) typename DestinationExecutors::EmptyExecutor();
91 typename NonConstFunctionExecutors::ExecutorIf* dest,
93 FunctionMoveCtor /*throws*/) noexcept {
94 moveToImpl<Executors<typename Traits::NonConstFunctionType>>(dest);
97 typename ConstFunctionExecutors::ExecutorIf* dest,
99 FunctionMoveCtor /*throws*/) noexcept {
100 moveToImpl<Executors<typename Traits::ConstFunctionType>>(dest);
104 // function::FunctorPtrExecutor
105 template <typename FunctionType>
106 template <typename F, typename SelectFunctionTag>
107 class Executors<FunctionType>::FunctorPtrExecutor final
108 : public Executors<FunctionType>::ExecutorIf {
110 FunctorPtrExecutor(F&& f)
112 &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
113 functorPtr_(new F(std::move(f))) {}
114 FunctorPtrExecutor(F const& f)
116 &FunctorPtrExecutor::template invokeFunctor<FunctorPtrExecutor>),
117 functorPtr_(new F(f)) {}
118 FunctorPtrExecutor(std::unique_ptr<F> f)
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;
127 static auto getFunctor(
128 typename Traits::template QualifiedPointer<ExecutorIf> self) ->
129 typename SelectFunctionTag::template QualifiedPointer<F> {
130 return FunctorPtrExecutor::selectFunctionHelper(
132 typename Traits::template QualifiedPointer<FunctorPtrExecutor>>(
135 SelectFunctionTag());
138 std::pair<std::type_info const&, void*> target() const noexcept {
139 return {typeid(F), const_cast<F*>(functorPtr_.get())};
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();
152 typename NonConstFunctionExecutors::ExecutorIf* dest,
154 FunctionMoveCtor /*throws*/) noexcept {
155 moveToImpl<Executors<typename Traits::NonConstFunctionType>>(dest);
158 typename ConstFunctionExecutors::ExecutorIf* dest,
160 FunctionMoveCtor /*throws*/) noexcept {
161 moveToImpl<Executors<typename Traits::ConstFunctionType>>(dest);
165 std::unique_ptr<F> functorPtr_;
168 // function::FunctorExecutor
169 template <typename FunctionType>
170 template <typename F, typename SelectFunctionTag>
171 class Executors<FunctionType>::FunctorExecutor final
172 : public Executors<FunctionType>::ExecutorIf {
174 static constexpr bool kFunctorIsNTM =
175 std::is_nothrow_move_constructible<F>::value;
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>),
183 ~FunctorExecutor() {}
184 detail::function::AllocationStatus getAllocationStatus() const noexcept {
185 return detail::function::AllocationStatus::EMBEDDED;
188 static auto getFunctor(
189 typename Traits::template QualifiedPointer<ExecutorIf> self) ->
190 typename SelectFunctionTag::template QualifiedPointer<F> {
191 return FunctorExecutor::selectFunctionHelper(
193 typename Traits::template QualifiedPointer<FunctorExecutor>>(self)
195 SelectFunctionTag());
198 std::pair<std::type_info const&, void*> target() const noexcept {
199 return {typeid(F), const_cast<F*>(&functor_)};
202 template <typename DestinationExecutors>
204 typename DestinationExecutors::ExecutorIf* dest,
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_));
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_));
224 this->~FunctorExecutor();
225 new (this) EmptyExecutor();
228 typename NonConstFunctionExecutors::ExecutorIf* dest,
230 FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
231 moveToImpl<Executors<typename Traits::NonConstFunctionType>>(
235 typename ConstFunctionExecutors::ExecutorIf* dest,
237 FunctionMoveCtor throws) noexcept(kFunctorIsNTM) {
238 moveToImpl<Executors<typename Traits::ConstFunctionType>>(
245 } // namespace function
246 } // namespace detail
248 // ---------------------------------------------------------------------------
249 // MOVE CONSTRUCTORS & MOVE ASSIGNMENT OPERATORS
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);
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()) {
263 initializeEmptyExecutor();
265 rhs.access<ExecutorIf>()->moveTo(access<ExecutorIf>(), kStorageSize, NTM);
269 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
271 typename OtherFunctionType,
272 FunctionMoveCtor OtherNTM,
273 size_t OtherEmbedFunctorSize>
274 Function<FunctionType, NTM, EmbedFunctorSize>::
276 Function<OtherFunctionType,
278 OtherEmbedFunctorSize>&& other) noexcept(
279 OtherNTM == FunctionMoveCtor::NO_THROW &&
280 EmbedFunctorSize >= OtherEmbedFunctorSize) {
281 using OtherFunction =
282 Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
286 typename Traits::NonConstFunctionType,
287 typename OtherFunction::Traits::NonConstFunctionType>::value,
288 "Function: cannot move into a Function with different "
289 "parameter signature");
291 !Traits::IsConst::value || OtherFunction::Traits::IsConst::value,
292 "Function: cannot move Function<R(Args...)> into "
293 "Function<R(Args...) const>; "
294 "use folly::constCastFunction!");
296 other.template access<typename OtherFunction::ExecutorIf>()->moveTo(
297 access<ExecutorIf>(), kStorageSize, NTM);
300 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
302 typename OtherFunctionType,
303 FunctionMoveCtor OtherNTM,
304 size_t OtherEmbedFunctorSize>
305 Function<FunctionType, NTM, EmbedFunctorSize>&
306 Function<FunctionType, NTM, EmbedFunctorSize>::operator=(
307 Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>&&
308 rhs) noexcept(OtherNTM == FunctionMoveCtor::NO_THROW) {
309 using OtherFunction =
310 Function<OtherFunctionType, OtherNTM, OtherEmbedFunctorSize>;
314 typename Traits::NonConstFunctionType,
315 typename OtherFunction::Traits::NonConstFunctionType>::value,
316 "Function: cannot move into a Function with different "
317 "parameter signature");
319 !Traits::IsConst::value || OtherFunction::Traits::IsConst::value,
320 "Function: cannot move Function<R(Args...)> into "
321 "Function<R(Args...) const>; "
322 "use folly::constCastFunction!");
326 initializeEmptyExecutor();
328 rhs.template access<typename OtherFunction::ExecutorIf>()->moveTo(
329 access<ExecutorIf>(), kStorageSize, NTM);
333 // ---------------------------------------------------------------------------
336 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
337 template <FunctionMoveCtor OtherNTM, size_t OtherEmbedFunctorSize>
338 inline void Function<FunctionType, NTM, EmbedFunctorSize>::
339 swap(Function<FunctionType, OtherNTM, OtherEmbedFunctorSize>& o) noexcept(
340 hasNoExceptMoveCtor() && OtherNTM == FunctionMoveCtor::NO_THROW) {
341 Function<FunctionType, NTM, EmbedFunctorSize> tmp(std::move(*this));
342 *this = std::move(o);
346 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
347 Function<FunctionType, NTM, EmbedFunctorSize>::operator bool() const noexcept {
348 return access<ExecutorIf>()->getAllocationStatus() !=
349 detail::function::AllocationStatus::EMPTY;
352 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
353 inline bool Function<FunctionType, NTM, EmbedFunctorSize>::hasAllocatedMemory()
355 return access<ExecutorIf>()->getAllocationStatus() ==
356 detail::function::AllocationStatus::ALLOCATED;
359 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
360 inline std::type_info const&
361 Function<FunctionType, NTM, EmbedFunctorSize>::target_type() const noexcept {
362 return access<ExecutorIf>()->target().first;
365 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
366 template <typename T>
367 T* Function<FunctionType, NTM, EmbedFunctorSize>::target() noexcept {
368 auto type_target_pair = access<ExecutorIf>()->target();
369 if (type_target_pair.first == typeid(T)) {
370 return static_cast<T*>(type_target_pair.second);
375 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
376 template <typename T>
377 T const* Function<FunctionType, NTM, EmbedFunctorSize>::target() const
379 auto type_target_pair = access<ExecutorIf>()->target();
380 if (type_target_pair.first == typeid(T)) {
381 return static_cast<T const*>(type_target_pair.second);
386 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
388 typename detail::function::FunctionTypeTraits<
389 FunctionType>::ConstFunctionType,
392 Function<FunctionType, NTM, EmbedFunctorSize>::castToConstFunction() &&
393 noexcept(hasNoExceptMoveCtor()) {
395 Function<typename Traits::ConstFunctionType, NTM, EmbedFunctorSize>;
398 result.destroyExecutor();
400 result.initializeEmptyExecutor();
402 access<ExecutorIf>()->moveTo(
403 result.template access<typename ReturnType::ExecutorIf>(),
409 // ---------------------------------------------------------------------------
412 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
413 template <typename T>
414 T* Function<FunctionType, NTM, EmbedFunctorSize>::access() {
416 std::is_base_of<ExecutorIf, T>::value,
417 "Function::access<T>: ExecutorIf must be base class of T "
418 "(this is a bug in the Function implementation)");
420 sizeof(T) <= kStorageSize,
421 "Requested access to object not fitting into ExecutorStore "
422 "(this is a bug in the Function implementation)");
424 return reinterpret_cast<T*>(&data_);
427 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
428 template <typename T>
429 T const* Function<FunctionType, NTM, EmbedFunctorSize>::access() const {
431 std::is_base_of<ExecutorIf, T>::value,
432 "Function::access<T>: ExecutorIf must be base class of T "
433 "(this is a bug in the Function implementation)");
435 sizeof(T) <= kStorageSize,
436 "Requested access to object not fitting into ExecutorStore "
437 "(this is a bug in the Function implementation)");
439 return reinterpret_cast<T const*>(&data_);
442 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
443 void Function<FunctionType, NTM, EmbedFunctorSize>::
444 initializeEmptyExecutor() noexcept {
445 new (access<EmptyExecutor>()) EmptyExecutor;
448 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
449 template <typename F>
450 void Function<FunctionType, NTM, EmbedFunctorSize>::
451 createExecutor(F&& f) noexcept(
452 noexcept(typename std::decay<F>::type(std::forward<F>(f)))) {
453 using ValueType = typename std::decay<F>::type;
454 static constexpr bool kFunctorIsNTM =
455 std::is_nothrow_move_constructible<ValueType>::value;
456 using ExecutorType = typename std::conditional<
457 (sizeof(FunctorExecutor<
459 typename Traits::DefaultSelectFunctionTag>) > kStorageSize ||
460 (hasNoExceptMoveCtor() && !kFunctorIsNTM)),
461 FunctorPtrExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>,
462 FunctorExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>>::
464 new (access<ExecutorType>()) ExecutorType(std::forward<F>(f));
467 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
468 void Function<FunctionType, NTM, EmbedFunctorSize>::destroyExecutor() noexcept {
469 access<ExecutorIf>()->~ExecutorIf();
472 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
473 struct Function<FunctionType, NTM, EmbedFunctorSize>::MinStorageSize {
474 using NotEmbeddedFunctor =
475 FunctorPtrExecutor<void(void), detail::function::SelectConstFunctionTag>;
477 using EmbeddedFunctor = FunctorExecutor<
478 typename std::aligned_storage<
479 constexpr_max(EmbedFunctorSize, sizeof(void (*)(void)))>::type,
480 detail::function::SelectConstFunctionTag>;
482 static constexpr size_t value =
483 constexpr_max(sizeof(NotEmbeddedFunctor), sizeof(EmbeddedFunctor));
486 sizeof(EmptyExecutor) <= value,
487 "Internal error in Function: EmptyExecutor does not fit "