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>::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>;
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!");
291 other.template access<typename OtherFunction::ExecutorIf>()->moveTo(
292 access<ExecutorIf>(), kStorageSize, NTM);
295 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
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>;
309 typename Traits::NonConstFunctionType,
310 typename OtherFunction::Traits::NonConstFunctionType>::value,
311 "Function: cannot move into a Function with different "
312 "parameter signature");
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!");
321 initializeEmptyExecutor();
323 rhs.template access<typename OtherFunction::ExecutorIf>()->moveTo(
324 access<ExecutorIf>(), kStorageSize, NTM);
328 // ---------------------------------------------------------------------------
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);
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;
347 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
348 inline bool Function<FunctionType, NTM, EmbedFunctorSize>::hasAllocatedMemory()
350 return access<ExecutorIf>()->getAllocationStatus() ==
351 detail::function::AllocationStatus::ALLOCATED;
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;
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);
370 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
371 template <typename T>
372 T const* Function<FunctionType, NTM, EmbedFunctorSize>::target() const
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);
381 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
383 typename detail::function::FunctionTypeTraits<
384 FunctionType>::ConstFunctionType,
387 Function<FunctionType, NTM, EmbedFunctorSize>::castToConstFunction() &&
388 noexcept(hasNoExceptMoveCtor()) {
390 Function<typename Traits::ConstFunctionType, NTM, EmbedFunctorSize>;
393 result.destroyExecutor();
395 result.initializeEmptyExecutor();
397 access<ExecutorIf>()->moveTo(
398 result.template access<typename ReturnType::ExecutorIf>(),
404 // ---------------------------------------------------------------------------
407 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
408 template <typename T>
409 T* Function<FunctionType, NTM, EmbedFunctorSize>::access() {
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)");
415 sizeof(T) <= kStorageSize,
416 "Requested access to object not fitting into ExecutorStore "
417 "(this is a bug in the Function implementation)");
419 return reinterpret_cast<T*>(&data_);
422 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
423 template <typename T>
424 T const* Function<FunctionType, NTM, EmbedFunctorSize>::access() const {
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)");
430 sizeof(T) <= kStorageSize,
431 "Requested access to object not fitting into ExecutorStore "
432 "(this is a bug in the Function implementation)");
434 return reinterpret_cast<T const*>(&data_);
437 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
438 void Function<FunctionType, NTM, EmbedFunctorSize>::
439 initializeEmptyExecutor() noexcept {
440 new (access<EmptyExecutor>()) EmptyExecutor;
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<
454 typename Traits::DefaultSelectFunctionTag>) > kStorageSize ||
455 (hasNoExceptMoveCtor() && !kFunctorIsNTM)),
456 FunctorPtrExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>,
457 FunctorExecutor<ValueType, typename Traits::DefaultSelectFunctionTag>>::
459 new (access<ExecutorType>()) ExecutorType(std::forward<F>(f));
462 template <typename FunctionType, FunctionMoveCtor NTM, size_t EmbedFunctorSize>
463 void Function<FunctionType, NTM, EmbedFunctorSize>::destroyExecutor() noexcept {
464 access<ExecutorIf>()->~ExecutorIf();
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>;
472 using EmbeddedFunctor = FunctorExecutor<
473 typename std::aligned_storage<
474 constexpr_max(EmbedFunctorSize, sizeof(void (*)(void)))>::type,
475 detail::function::SelectConstFunctionTag>;
477 static constexpr size_t value =
478 constexpr_max(sizeof(NotEmbeddedFunctor), sizeof(EmbeddedFunctor));
481 sizeof(EmptyExecutor) <= value,
482 "Internal error in Function: EmptyExecutor does not fit "