VLink  2.0.0
A high-performance communication middleware
functional.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file functional.h
26  * @brief Pool-backed type-erased callables: copyable @c vlink::Function and move-only @c vlink::MoveFunction.
27  *
28  * @details
29  * @c vlink::Function tracks the public surface of @c std::function while @c vlink::MoveFunction
30  * tracks @c std::move_only_function. Both store the target in a configurable small-buffer
31  * region (64 bytes by default) and spill to @c vlink::MemoryPool::global_instance when the
32  * target does not fit. This eliminates the heap-allocation surprise common to @c std::function
33  * on hot paths while still permitting arbitrarily large closures.
34  *
35  * @par Comparison vs the standard library
36  *
37  * | Property | std::function | std::move_only_function | vlink::Function | vlink::MoveFunction |
38  * | -------------------- | ---------------- | ----------------------- | ------------------ | ------------------- |
39  * | Copyable | yes | no | yes | no |
40  * | Movable | yes | yes | yes | yes |
41  * | Default SBO size | ~16-24 B (impl) | ~16-24 B (impl) | 64 B (tunable) | 64 B (tunable) |
42  * | Spill allocator | ::operator new | ::operator new | @c MemoryPool tier | @c MemoryPool tier |
43  * | Empty-call behaviour | throws bad_func | UB | throws bad_func | throws bad_func |
44  * | RTTI inspection | @c target / type | not provided | @c target / type | @c target / type |
45  *
46  * @par Storage predicate (kIsInline)
47  * A target @c FunctorT is held inline only when all three hold simultaneously:
48  * @c sizeof(FunctorT) @c <= @c SboSizeT, @c alignof(FunctorT) @c <= @c alignof(std::max_align_t)
49  * and @c FunctorT is nothrow move-constructible. Anything else goes to the heap path through
50  * @c MemoryPool::global_instance; the original @c sizeof / @c alignof values are passed back to
51  * @c deallocate so the block returns to its source tier. This mirrors libstdc++'s
52  * @c __stored_locally rule.
53  *
54  * @par SBO sizing
55  * Both wrappers expose @c SboSizeT as the second non-type template argument. Convenience
56  * aliases @c vlink::LargeFunction / @c vlink::LargeMoveFunction pick @c 256 bytes; the bound
57  * @c SboSizeT @c >= @c sizeof(void*) is enforced by @c static_assert so the heap-fallback
58  * pointer always fits in the SBO. Distinct @c SboSizeT instantiations are distinct types but
59  * may still convert through the generic functor path -- a @c Function<Sig, @c 64> serves as a
60  * regular callable target for @c Function<Sig, @c 256>.
61  *
62  * @par Empty-state propagation
63  * Constructing or assigning from any of these sources yields an empty wrapper with no vtable
64  * and no allocation:
65  * - an empty function-wrapper source (@c std::function, @c vlink::Function, and -- when the
66  * target is @c MoveFunction -- @c std::move_only_function / @c vlink::MoveFunction);
67  * - a null raw function pointer;
68  * - a null pointer-to-member.
69  *
70  * @par Exception safety
71  * Copy operations use copy-and-swap and offer the strong guarantee; @c swap, move construction
72  * and move assignment are @c noexcept. The @c kIsInline predicate guarantees that inline-path
73  * moves never throw, while the heap path simply moves a pointer. Inline construction uses
74  * placement-new directly into the SBO; heap construction is wrapped in @c try / @c catch so the
75  * pool block is returned on a constructor exception.
76  *
77  * @par Object-lifetime model
78  * Inline targets are placement-new'd into the SBO. Heap storage holds a @c FunctorT*; the
79  * pointer object's lifetime is started explicitly via @c ::new(dst) @c FunctorT*(p) at every
80  * site that introduces a new slot, avoiding reliance on the C++20 @c [intro.object]/10
81  * implicit-object rule and keeping the code well-defined under C++17. Subsequent accesses go
82  * through @c std::launder to satisfy @c [basic.life]/8.
83  *
84  * @par RTTI surface
85  * When @c __cpp_rtti is defined both wrappers expose @c target_type() and
86  * @c target<FunctorT>() with @c std::function semantics. This is an extension over
87  * @c std::move_only_function which omits target inspection.
88  *
89  * @par Example
90  * @code
91  * vlink::Function<int(int)> f = [](int x) { return x * 2; };
92  * int y = f(21); // == 42
93  *
94  * auto pkg = std::packaged_task<int()>([] { return 7; });
95  * vlink::MoveFunction<void()> mf = std::move(pkg); // move-only target
96  * mf(); // runs the task
97  *
98  * vlink::LargeMoveFunction<void()> heavy = ...heavy capture...; // 256-byte SBO
99  * @endcode
100  *
101  * @note Only the unqualified @c ReturnT(ArgsT...) signature is specialised; cv / ref /
102  * @c noexcept-qualified function types resolve to the undefined primary template (hard
103  * error). This header self-defines @c VLINK_ENABLE_BASE_FUNCTIONAL when absent so normal
104  * builds always select the VLink implementation; the standard-library alias block at the
105  * bottom is not exposed via a compile-command opt-out.
106  */
107 
108 #pragma once
109 
110 #include <functional>
111 
112 #if !defined(VLINK_ENABLE_BASE_FUNCTIONAL)
113 #define VLINK_ENABLE_BASE_FUNCTIONAL
114 #endif
115 
116 #ifdef VLINK_ENABLE_BASE_FUNCTIONAL
117 #include <cstddef>
118 #include <new>
119 #include <type_traits>
120 #include <typeinfo>
121 #include <utility>
122 
123 #include "./macros.h"
124 #include "./memory_pool.h"
125 
126 namespace vlink {
127 
128 [[maybe_unused]] static constexpr bool kIsSupportMoveFunction = true;
129 
130 template <typename SignatureT, size_t SboSizeT = 64U>
131 class Function;
132 
133 template <typename SignatureT, size_t SboSizeT = 64U>
135 
136 template <typename SignatureT>
138 
139 template <typename SignatureT>
141 
142 template <typename SignatureT>
143 using function = Function<SignatureT>;
144 
145 template <typename SignatureT>
147 
148 namespace detail {
149 
150 template <typename TypeT>
151 struct IsStdFunction : std::false_type {};
152 
153 template <typename SignatureT>
154 struct IsStdFunction<std::function<SignatureT>> : std::true_type {};
155 
156 template <typename TypeT>
157 struct IsVlinkFunction : std::false_type {};
158 
159 template <typename SignatureT, size_t SboSizeT>
160 struct IsVlinkFunction<Function<SignatureT, SboSizeT>> : std::true_type {};
161 
162 template <typename TypeT>
163 struct IsVlinkMoveFunction : std::false_type {};
164 
165 template <typename SignatureT, size_t SboSizeT>
166 struct IsVlinkMoveFunction<MoveFunction<SignatureT, SboSizeT>> : std::true_type {};
167 
168 #if defined(__cpp_lib_move_only_function) && __cpp_lib_move_only_function >= 202110L
169 template <typename TypeT>
170 struct IsStdMoveOnlyFunction : std::false_type {};
171 
172 template <typename SignatureT>
173 struct IsStdMoveOnlyFunction<std::move_only_function<SignatureT>> : std::true_type {};
174 #endif
175 
177 
178 } // namespace detail
179 
180 /**
181  * @class Function
182  * @brief Copyable type-erased callable analogue of @c std::function with a tunable SBO and pool spill.
183  *
184  * @details
185  * Holds any copy-constructible target invocable as @c ReturnT(ArgsT...). Default-instantiated
186  * @c Function uses a 64-byte SBO; @c Function<Sig, @c N> picks an arbitrary inline budget.
187  * @c operator() is @c const (matching @c std::function 's logical-const convention -- the
188  * placement-newed target itself is non-const, so the @c const_cast pattern inside the invoker is
189  * well-defined). Empty construction, @c nullptr-from-nullptr, and the @c std::bad_function_call
190  * empty-call exception all match @c std::function. Copy operations follow copy-and-swap and
191  * provide the strong exception guarantee; move construction, move assignment and @c swap are
192  * @c noexcept.
193  *
194  * @tparam ReturnT Result type of the invocable target.
195  * @tparam ArgsT Argument types of the invocable target.
196  * @tparam SboSizeT Inline storage budget in bytes; must be @c >= @c sizeof(void*).
197  */
198 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
199 class Function<ReturnT(ArgsT...), SboSizeT> {
200  static_assert(SboSizeT >= sizeof(void*),
201  "Function: SboSizeT must be at least sizeof(void*) so the heap-fallback "
202  "pointer can fit in the inline storage.");
203 
204  public:
205  /**
206  * @brief Inline storage budget in bytes before the heap-pool fallback kicks in.
207  */
208  static constexpr size_t kSboSize = SboSizeT;
209 
210  /**
211  * @brief Result type alias matching @c std::function.
212  */
213  using result_type = ReturnT;
214 
215  /**
216  * @brief Constructs an empty wrapper with no stored target.
217  */
218  Function() noexcept = default;
219 
220  /**
221  * @brief Constructs an empty wrapper from a null pointer literal.
222  */
223  Function(std::nullptr_t) noexcept; // NOLINT(google-explicit-constructor)
224 
225  /**
226  * @brief Copy constructor; deep-copies the stored target or stays empty.
227  */
228  Function(const Function& other);
229 
230  /**
231  * @brief Move constructor; transfers the target and leaves @p other empty.
232  */
233  Function(Function&& other) noexcept;
234 
235  /**
236  * @brief Constructs from any compatible copy-constructible callable.
237  *
238  * @details
239  * Null function pointers, null pointer-to-members and empty function wrappers all yield an
240  * empty result rather than storing a tombstone callable.
241  *
242  * @tparam FunctorT Callable type compatible with @c std::invoke.
243  */
244  template <typename FunctorT, typename DecayFunctorT = std::decay_t<FunctorT>,
245  // NOLINTNEXTLINE(modernize-use-constraints)
246  typename = std::enable_if_t<std::conjunction_v<std::negation<std::is_same<DecayFunctorT, Function>>,
247  std::negation<std::is_same<DecayFunctorT, std::nullptr_t>>,
248  std::is_invocable_r<ReturnT, DecayFunctorT&, ArgsT...>,
249  std::is_copy_constructible<DecayFunctorT>>>>
250  Function(FunctorT&& f); // NOLINT(google-explicit-constructor)
251 
252  /**
253  * @brief Copy assignment; replaces the target with a deep copy of @p other.
254  */
255  Function& operator=(const Function& other);
256 
257  /**
258  * @brief Move assignment; replaces the target with @p other 's target.
259  */
260  Function& operator=(Function&& other) noexcept;
261 
262  /**
263  * @brief Clears the wrapper to the empty state.
264  */
265  Function& operator=(std::nullptr_t) noexcept;
266 
267  /**
268  * @brief Replaces the target with a compatible copy-constructible callable.
269  *
270  * @tparam FunctorT Callable type compatible with @c std::invoke.
271  */
272  template <typename FunctorT, typename DecayFunctorT = std::decay_t<FunctorT>,
273  // NOLINTNEXTLINE(modernize-use-constraints)
274  typename = std::enable_if_t<std::conjunction_v<std::negation<std::is_same<DecayFunctorT, Function>>,
275  std::negation<std::is_same<DecayFunctorT, std::nullptr_t>>,
276  std::is_invocable_r<ReturnT, DecayFunctorT&, ArgsT...>,
277  std::is_copy_constructible<DecayFunctorT>>>>
278  Function& operator=(FunctorT&& f);
279 
280  /**
281  * @brief Destructor; releases inline or pooled storage.
282  */
283  ~Function();
284 
285  /**
286  * @brief Invokes the stored callable.
287  *
288  * @throws std::bad_function_call if the wrapper is empty.
289  */
290  ReturnT operator()(ArgsT... args) const;
291 
292  /**
293  * @brief Reports whether a callable target is currently stored.
294  */
295  explicit operator bool() const noexcept;
296 
297 #if defined(__cpp_rtti)
298  /**
299  * @brief Returns the dynamic type of the stored target, or @c typeid(void) when empty.
300  */
301  const std::type_info& target_type() const noexcept;
302 
303  /**
304  * @brief Returns the stored target when its dynamic type is exactly @c FunctorT.
305  *
306  * @tparam FunctorT Expected target type.
307  */
308  template <typename FunctorT>
309  FunctorT* target() noexcept;
310 
311  /**
312  * @brief Const overload of @c target<FunctorT>.
313  *
314  * @tparam FunctorT Expected target type.
315  */
316  template <typename FunctorT>
317  const FunctorT* target() const noexcept;
318 #endif
319 
320  /**
321  * @brief Swaps the stored target with @p other.
322  */
323  void swap(Function& other) noexcept;
324 
325  private:
326  template <typename FunctorT>
327  static constexpr bool kIsPointerLike = std::is_pointer_v<FunctorT> || std::is_member_pointer_v<FunctorT> ||
328  std::is_function_v<std::remove_pointer_t<FunctorT>>;
329 
330  template <typename FunctorT>
331  static constexpr bool kIsFunctionWrapper =
333 
334  template <typename FunctorT>
335  static constexpr bool kIsInline = sizeof(FunctorT) <= kSboSize && alignof(FunctorT) <= alignof(std::max_align_t) &&
336  std::is_nothrow_move_constructible_v<FunctorT>;
337 
338  struct VTable final {
339  ReturnT (*invoke)(const void* storage, ArgsT... args);
340 
341  void (*copy_construct)(void* dst, const void* src);
342 
343  void (*move_construct)(void* dst, void* src) noexcept;
344 
345  void (*destroy)(void* storage) noexcept;
346 
347 #if defined(__cpp_rtti)
348  const std::type_info& (*target_type)() noexcept;
349 
350  void* (*target)(void* storage) noexcept;
351 
352  const void* (*target_const)(const void* storage) noexcept;
353 #endif
354  };
355 
356  template <typename FunctorT>
357  struct InlineVTable {
358  static ReturnT invoke(const void* storage, ArgsT... args);
359 
360  static void copy_construct(void* dst, const void* src);
361 
362  static void move_construct(void* dst, void* src) noexcept;
363 
364  static void destroy(void* storage) noexcept;
365 
366 #if defined(__cpp_rtti)
367  static const std::type_info& target_type() noexcept;
368 
369  static void* target(void* storage) noexcept;
370 
371  static const void* target_const(const void* storage) noexcept;
372 #endif
373  };
374 
375  template <typename FunctorT>
376  struct HeapVTable {
377  static ReturnT invoke(const void* storage, ArgsT... args);
378 
379  static void copy_construct(void* dst, const void* src);
380 
381  static void move_construct(void* dst, void* src) noexcept;
382 
383  static void destroy(void* storage) noexcept;
384 
385 #if defined(__cpp_rtti)
386  static const std::type_info& target_type() noexcept;
387 
388  static void* target(void* storage) noexcept;
389 
390  static const void* target_const(const void* storage) noexcept;
391 #endif
392  };
393 
394  template <typename FunctorT>
395  static const VTable* get_vtable() noexcept;
396 
397  template <typename FunctorT, typename SourceT>
398  void construct_from(SourceT&& src);
399 
400  void copy_from(const Function& other);
401 
402  void move_from(Function&& other) noexcept;
403 
404  void reset() noexcept;
405 
406  alignas(std::max_align_t) std::byte storage_[SboSizeT]{};
407 
408  const VTable* vtable_{nullptr};
409 };
410 
411 /**
412  * @brief Free-function swap; defers to the member @c swap of @p lhs.
413  */
414 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
415 void swap(Function<ReturnT(ArgsT...), SboSizeT>& lhs, Function<ReturnT(ArgsT...), SboSizeT>& rhs) noexcept;
416 
417 /**
418  * @brief Equality with @c nullptr; @c true when @p cb has no stored target.
419  */
420 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
421 bool operator==(const Function<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept;
422 
423 /**
424  * @brief Commutative overload of @c operator==(Function, nullptr_t).
425  */
426 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
427 bool operator==(std::nullptr_t, const Function<ReturnT(ArgsT...), SboSizeT>& cb) noexcept;
428 
429 /**
430  * @brief Inequality with @c nullptr; @c true when @p cb stores a target.
431  */
432 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
433 bool operator!=(const Function<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept;
434 
435 /**
436  * @brief Commutative overload of @c operator!=(Function, nullptr_t).
437  */
438 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
439 bool operator!=(std::nullptr_t, const Function<ReturnT(ArgsT...), SboSizeT>& cb) noexcept;
440 
441 /**
442  * @class MoveFunction
443  * @brief Move-only type-erased callable analogue of @c std::move_only_function with pool spill.
444  *
445  * @details
446  * Holds any move-constructible target invocable as @c ReturnT(ArgsT...). Shares the SBO and
447  * @c MemoryPool fallback used by @c Function. Diverges from @c std::move_only_function in two
448  * deliberate ways and extends it in one: empty calls throw @c std::bad_function_call instead of
449  * being UB; only the unqualified signature is specialised (cv / ref / @c noexcept forms hard-
450  * fail); and RTTI inspection through @c target_type / @c target is provided. @c operator() is
451  * non-@c const so mutating targets such as @c std::packaged_task work directly without the
452  * logical-const dance. Move operations and @c swap are @c noexcept; copy is deleted.
453  *
454  * @tparam ReturnT Result type of the invocable target.
455  * @tparam ArgsT Argument types of the invocable target.
456  * @tparam SboSizeT Inline storage budget in bytes; must be @c >= @c sizeof(void*).
457  */
458 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
459 class MoveFunction<ReturnT(ArgsT...), SboSizeT> {
460  static_assert(SboSizeT >= sizeof(void*),
461  "MoveFunction: SboSizeT must be at least sizeof(void*) so the "
462  "heap-fallback pointer can fit in the inline storage.");
463 
464  public:
465  /**
466  * @brief Inline storage budget in bytes before the heap-pool fallback kicks in.
467  */
468  static constexpr size_t kSboSize = SboSizeT;
469 
470  /**
471  * @brief Result type alias matching @c std::move_only_function.
472  */
473  using result_type = ReturnT;
474 
475  /**
476  * @brief Constructs an empty wrapper with no stored target.
477  */
478  MoveFunction() noexcept = default;
479 
480  /**
481  * @brief Constructs an empty wrapper from a null pointer literal.
482  */
483  MoveFunction(std::nullptr_t) noexcept; // NOLINT(google-explicit-constructor)
484 
485  MoveFunction(const MoveFunction&) = delete;
486 
487  MoveFunction& operator=(const MoveFunction&) = delete;
488 
489  /**
490  * @brief Move constructor; transfers the target and leaves @p other empty.
491  */
492  MoveFunction(MoveFunction&& other) noexcept;
493 
494  /**
495  * @brief Constructs from any compatible move-constructible callable.
496  *
497  * @details
498  * Null function pointers, null pointer-to-members and empty function wrappers all yield an
499  * empty result rather than storing a tombstone callable.
500  *
501  * @tparam FunctorT Callable type compatible with @c std::invoke.
502  */
503  template <typename FunctorT, typename DecayFunctorT = std::decay_t<FunctorT>,
504  // NOLINTNEXTLINE(modernize-use-constraints)
505  typename = std::enable_if_t<std::conjunction_v<std::negation<std::is_same<DecayFunctorT, MoveFunction>>,
506  std::negation<std::is_same<DecayFunctorT, std::nullptr_t>>,
507  std::is_invocable_r<ReturnT, DecayFunctorT&, ArgsT...>,
508  std::is_constructible<DecayFunctorT, FunctorT>,
509  std::is_move_constructible<DecayFunctorT>>>>
510  MoveFunction(FunctorT&& f); // NOLINT(google-explicit-constructor)
511 
512  /**
513  * @brief Move assignment; replaces the target with @p other 's target.
514  */
515  MoveFunction& operator=(MoveFunction&& other) noexcept;
516 
517  /**
518  * @brief Clears the wrapper to the empty state.
519  */
520  MoveFunction& operator=(std::nullptr_t) noexcept;
521 
522  /**
523  * @brief Replaces the target with a compatible move-constructible callable.
524  *
525  * @tparam FunctorT Callable type compatible with @c std::invoke.
526  */
527  template <typename FunctorT, typename DecayFunctorT = std::decay_t<FunctorT>,
528  // NOLINTNEXTLINE(modernize-use-constraints)
529  typename = std::enable_if_t<std::conjunction_v<std::negation<std::is_same<DecayFunctorT, MoveFunction>>,
530  std::negation<std::is_same<DecayFunctorT, std::nullptr_t>>,
531  std::is_invocable_r<ReturnT, DecayFunctorT&, ArgsT...>,
532  std::is_constructible<DecayFunctorT, FunctorT>,
533  std::is_move_constructible<DecayFunctorT>>>>
534  MoveFunction& operator=(FunctorT&& f);
535 
536  /**
537  * @brief Destructor; releases inline or pooled storage.
538  */
539  ~MoveFunction();
540 
541  /**
542  * @brief Invokes the stored callable.
543  *
544  * @throws std::bad_function_call if the wrapper is empty.
545  */
546  ReturnT operator()(ArgsT... args);
547 
548  /**
549  * @brief Reports whether a callable target is currently stored.
550  */
551  explicit operator bool() const noexcept;
552 
553 #if defined(__cpp_rtti)
554  /**
555  * @brief Returns the dynamic type of the stored target, or @c typeid(void) when empty.
556  */
557  const std::type_info& target_type() const noexcept;
558 
559  /**
560  * @brief Returns the stored target when its dynamic type is exactly @c FunctorT.
561  *
562  * @tparam FunctorT Expected target type.
563  */
564  template <typename FunctorT>
565  FunctorT* target() noexcept;
566 
567  /**
568  * @brief Const overload of @c target<FunctorT>.
569  *
570  * @tparam FunctorT Expected target type.
571  */
572  template <typename FunctorT>
573  const FunctorT* target() const noexcept;
574 #endif
575 
576  /**
577  * @brief Swaps the stored target with @p other.
578  */
579  void swap(MoveFunction& other) noexcept;
580 
581  private:
582  template <typename FunctorT>
583  static constexpr bool kIsPointerLike = std::is_pointer_v<FunctorT> || std::is_member_pointer_v<FunctorT> ||
584  std::is_function_v<std::remove_pointer_t<FunctorT>>;
585 
586  template <typename FunctorT>
587  static constexpr bool kIsFunctionWrapper =
589 #if defined(__cpp_lib_move_only_function) && __cpp_lib_move_only_function >= 202110L
590  detail::IsVlinkMoveFunction<FunctorT>::value || detail::IsStdMoveOnlyFunction<FunctorT>::value;
591 #else
593 #endif
594 
595  template <typename FunctorT>
596  static constexpr bool kIsInline = sizeof(FunctorT) <= kSboSize && alignof(FunctorT) <= alignof(std::max_align_t) &&
597  std::is_nothrow_move_constructible_v<FunctorT>;
598 
599  struct VTable final {
600  ReturnT (*invoke)(void* storage, ArgsT... args);
601 
602  void (*move_construct)(void* dst, void* src) noexcept;
603 
604  void (*destroy)(void* storage) noexcept;
605 
606 #if defined(__cpp_rtti)
607  const std::type_info& (*target_type)() noexcept;
608 
609  void* (*target)(void* storage) noexcept;
610 
611  const void* (*target_const)(const void* storage) noexcept;
612 #endif
613  };
614 
615  template <typename FunctorT>
616  struct InlineVTable {
617  static ReturnT invoke(void* storage, ArgsT... args);
618 
619  static void move_construct(void* dst, void* src) noexcept;
620 
621  static void destroy(void* storage) noexcept;
622 
623 #if defined(__cpp_rtti)
624  static const std::type_info& target_type() noexcept;
625 
626  static void* target(void* storage) noexcept;
627 
628  static const void* target_const(const void* storage) noexcept;
629 #endif
630  };
631 
632  template <typename FunctorT>
633  struct HeapVTable {
634  static ReturnT invoke(void* storage, ArgsT... args);
635 
636  static void move_construct(void* dst, void* src) noexcept;
637 
638  static void destroy(void* storage) noexcept;
639 
640 #if defined(__cpp_rtti)
641  static const std::type_info& target_type() noexcept;
642 
643  static void* target(void* storage) noexcept;
644 
645  static const void* target_const(const void* storage) noexcept;
646 #endif
647  };
648 
649  template <typename FunctorT>
650  static const VTable* get_vtable() noexcept;
651 
652  template <typename FunctorT, typename SourceT>
653  void construct_from(SourceT&& src);
654 
655  void move_from(MoveFunction&& other) noexcept;
656 
657  void reset() noexcept;
658 
659  alignas(std::max_align_t) std::byte storage_[SboSizeT]{};
660 
661  const VTable* vtable_{nullptr};
662 };
663 
664 /**
665  * @brief Free-function swap; defers to the member @c swap of @p lhs.
666  */
667 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
668 void swap(MoveFunction<ReturnT(ArgsT...), SboSizeT>& lhs, MoveFunction<ReturnT(ArgsT...), SboSizeT>& rhs) noexcept;
669 
670 /**
671  * @brief Equality with @c nullptr; @c true when @p cb has no stored target.
672  */
673 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
674 bool operator==(const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept;
675 
676 /**
677  * @brief Commutative overload of @c operator==(MoveFunction, nullptr_t).
678  */
679 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
680 bool operator==(std::nullptr_t, const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb) noexcept;
681 
682 /**
683  * @brief Inequality with @c nullptr; @c true when @p cb stores a target.
684  */
685 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
686 bool operator!=(const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept;
687 
688 /**
689  * @brief Commutative overload of @c operator!=(MoveFunction, nullptr_t).
690  */
691 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
692 bool operator!=(std::nullptr_t, const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb) noexcept;
693 
694 ////////////////////////////////////////////////////////////////
695 /// Details
696 ////////////////////////////////////////////////////////////////
697 
698 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
699 inline Function<ReturnT(ArgsT...), SboSizeT>::Function(std::nullptr_t) noexcept {}
700 
701 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
702 inline Function<ReturnT(ArgsT...), SboSizeT>::Function(const Function& other) {
703  copy_from(other);
704 }
705 
706 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
707 inline Function<ReturnT(ArgsT...), SboSizeT>::Function(Function&& other) noexcept {
708  move_from(std::move(other));
709 }
710 
711 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
712 template <typename FunctorT, typename DecayFunctorT, typename>
713 inline Function<ReturnT(ArgsT...), SboSizeT>::Function(FunctorT&& f) {
714  if constexpr (kIsFunctionWrapper<DecayFunctorT>) {
715  if VUNLIKELY (!f) {
716  return;
717  }
718  } else if constexpr (kIsPointerLike<DecayFunctorT>) {
719  if constexpr (std::is_pointer_v<std::remove_reference_t<FunctorT>> ||
720  std::is_member_pointer_v<std::remove_reference_t<FunctorT>>) {
721  if VUNLIKELY (f == nullptr) {
722  return;
723  }
724  }
725  }
726 
727  construct_from<DecayFunctorT>(std::forward<FunctorT>(f));
728 }
729 
730 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
731 // NOLINTNEXTLINE(bugprone-unhandled-self-assignment)
732 inline Function<ReturnT(ArgsT...), SboSizeT>& Function<ReturnT(ArgsT...), SboSizeT>::operator=(const Function& other) {
733  if VLIKELY (this != &other) {
734  Function tmp(other);
735  swap(tmp);
736  }
737 
738  return *this;
739 }
740 
741 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
742 inline Function<ReturnT(ArgsT...), SboSizeT>& Function<ReturnT(ArgsT...), SboSizeT>::operator=(
743  Function&& other) noexcept {
744  if VLIKELY (this != &other) {
745  reset();
746  move_from(std::move(other));
747  }
748 
749  return *this;
750 }
751 
752 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
753 inline Function<ReturnT(ArgsT...), SboSizeT>& Function<ReturnT(ArgsT...), SboSizeT>::operator=(
754  std::nullptr_t) noexcept {
755  reset();
756  return *this;
757 }
758 
759 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
760 template <typename FunctorT, typename DecayFunctorT, typename>
761 inline Function<ReturnT(ArgsT...), SboSizeT>& Function<ReturnT(ArgsT...), SboSizeT>::operator=(FunctorT&& f) {
762  Function tmp(std::forward<FunctorT>(f));
763  swap(tmp);
764  return *this;
765 }
766 
767 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
768 inline Function<ReturnT(ArgsT...), SboSizeT>::~Function() {
769  reset();
770 }
771 
772 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
773 inline ReturnT Function<ReturnT(ArgsT...), SboSizeT>::operator()(ArgsT... args) const {
774  if VUNLIKELY (vtable_ == nullptr) {
776  }
777 
778  return vtable_->invoke(&storage_, std::forward<ArgsT>(args)...);
779 }
780 
781 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
782 inline Function<ReturnT(ArgsT...), SboSizeT>::operator bool() const noexcept {
783  return vtable_ != nullptr;
784 }
785 
786 #if defined(__cpp_rtti)
787 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
788 inline const std::type_info& Function<ReturnT(ArgsT...), SboSizeT>::target_type() const noexcept {
789  if VLIKELY (vtable_ != nullptr) {
790  return vtable_->target_type();
791  }
792 
793  return typeid(void);
794 }
795 
796 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
797 template <typename FunctorT>
798 inline FunctorT* Function<ReturnT(ArgsT...), SboSizeT>::target() noexcept {
799  if constexpr (std::is_object_v<FunctorT>) {
800  if VLIKELY (vtable_ != nullptr && typeid(FunctorT) == target_type()) {
801  return static_cast<FunctorT*>(vtable_->target(&storage_));
802  }
803  }
804 
805  return nullptr;
806 }
807 
808 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
809 template <typename FunctorT>
810 inline const FunctorT* Function<ReturnT(ArgsT...), SboSizeT>::target() const noexcept {
811  if constexpr (std::is_object_v<FunctorT>) {
812  if VLIKELY (vtable_ != nullptr && typeid(FunctorT) == target_type()) {
813  return static_cast<const FunctorT*>(vtable_->target_const(&storage_));
814  }
815  }
816 
817  return nullptr;
818 }
819 #endif
820 
821 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
822 inline void Function<ReturnT(ArgsT...), SboSizeT>::swap(Function& other) noexcept {
823  if VUNLIKELY (this == &other) {
824  return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
825  }
826 
827  Function tmp(std::move(*this));
828  *this = std::move(other);
829  other = std::move(tmp);
830 }
831 
832 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
833 template <typename FunctorT>
834 inline ReturnT Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::invoke(const void* storage,
835  ArgsT... args) {
836  auto* f = std::launder(reinterpret_cast<FunctorT*>(const_cast<void*>(storage)));
837 
838  if constexpr (std::is_void_v<ReturnT>) {
839  std::invoke(*f, std::forward<ArgsT>(args)...);
840  } else {
841  return std::invoke(*f, std::forward<ArgsT>(args)...);
842  }
843 }
844 
845 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
846 template <typename FunctorT>
847 inline void Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::copy_construct(void* dst, const void* src) {
848  const auto* src_f = std::launder(reinterpret_cast<const FunctorT*>(src));
849  ::new (dst) FunctorT(*src_f);
850 }
851 
852 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
853 template <typename FunctorT>
854 inline void Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::move_construct(void* dst,
855  void* src) noexcept {
856  auto* src_f = std::launder(reinterpret_cast<FunctorT*>(src));
857  ::new (dst) FunctorT(std::move(*src_f));
858  src_f->~FunctorT();
859 }
860 
861 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
862 template <typename FunctorT>
863 inline void Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::destroy(void* storage) noexcept {
864  auto* f = std::launder(reinterpret_cast<FunctorT*>(storage));
865  f->~FunctorT();
866 }
867 
868 #if defined(__cpp_rtti)
869 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
870 template <typename FunctorT>
871 inline const std::type_info& Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::target_type() noexcept {
872  return typeid(FunctorT);
873 }
874 
875 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
876 template <typename FunctorT>
877 inline void* Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::target(void* storage) noexcept {
878  return storage;
879 }
880 
881 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
882 template <typename FunctorT>
883 inline const void* Function<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::target_const(
884  const void* storage) noexcept {
885  return storage;
886 }
887 #endif
888 
889 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
890 template <typename FunctorT>
891 inline ReturnT Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::invoke(const void* storage, ArgsT... args) {
892  FunctorT* f = *std::launder(static_cast<FunctorT* const*>(storage));
893 
894  if constexpr (std::is_void_v<ReturnT>) {
895  std::invoke(*f, std::forward<ArgsT>(args)...);
896  } else {
897  return std::invoke(*f, std::forward<ArgsT>(args)...);
898  }
899 }
900 
901 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
902 template <typename FunctorT>
903 inline void Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::copy_construct(void* dst, const void* src) {
904  FunctorT* src_f = *std::launder(static_cast<FunctorT* const*>(src));
905 
906  auto& pool = MemoryPool::global_instance();
907  void* mem = pool.allocate(sizeof(FunctorT), alignof(FunctorT));
908 
909  if VUNLIKELY (mem == nullptr) {
910  throw std::bad_alloc(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
911  }
912 
913  try {
914  auto* new_f = ::new (mem) FunctorT(*src_f);
915  ::new (dst) FunctorT*(new_f);
916  } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
917  pool.deallocate(mem, sizeof(FunctorT), alignof(FunctorT)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
918  throw; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
919  }
920 }
921 
922 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
923 template <typename FunctorT>
924 inline void Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::move_construct(void* dst, void* src) noexcept {
925  FunctorT** src_slot = std::launder(static_cast<FunctorT**>(src));
926  FunctorT* src_f = *src_slot;
927  ::new (dst) FunctorT*(src_f);
928  *src_slot = nullptr;
929 }
930 
931 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
932 template <typename FunctorT>
933 inline void Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::destroy(void* storage) noexcept {
934  FunctorT** slot = std::launder(static_cast<FunctorT**>(storage));
935  FunctorT* f = *slot;
936 
937  if VLIKELY (f != nullptr) {
938  f->~FunctorT();
939 
940  auto& pool = MemoryPool::global_instance();
941  pool.deallocate(f, sizeof(FunctorT), alignof(FunctorT));
942 
943  *slot = nullptr;
944  }
945 }
946 
947 #if defined(__cpp_rtti)
948 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
949 template <typename FunctorT>
950 inline const std::type_info& Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::target_type() noexcept {
951  return typeid(FunctorT);
952 }
953 
954 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
955 template <typename FunctorT>
956 inline void* Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::target(void* storage) noexcept {
957  return *std::launder(static_cast<FunctorT**>(storage));
958 }
959 
960 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
961 template <typename FunctorT>
962 inline const void* Function<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::target_const(
963  const void* storage) noexcept {
964  return *std::launder(static_cast<FunctorT* const*>(storage));
965 }
966 #endif
967 
968 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
969 template <typename FunctorT>
970 inline const typename Function<ReturnT(ArgsT...), SboSizeT>::VTable*
971 Function<ReturnT(ArgsT...), SboSizeT>::get_vtable() noexcept {
972  if constexpr (kIsInline<FunctorT>) {
973  static constexpr VTable kVTable = {
974  &InlineVTable<FunctorT>::invoke, &InlineVTable<FunctorT>::copy_construct,
975  &InlineVTable<FunctorT>::move_construct, &InlineVTable<FunctorT>::destroy,
976 #if defined(__cpp_rtti)
977  &InlineVTable<FunctorT>::target_type, &InlineVTable<FunctorT>::target,
978  &InlineVTable<FunctorT>::target_const,
979 #endif
980  };
981  return &kVTable;
982  } else {
983  static constexpr VTable kVTable = {
984  &HeapVTable<FunctorT>::invoke, &HeapVTable<FunctorT>::copy_construct,
985  &HeapVTable<FunctorT>::move_construct, &HeapVTable<FunctorT>::destroy,
986 #if defined(__cpp_rtti)
987  &HeapVTable<FunctorT>::target_type, &HeapVTable<FunctorT>::target,
988  &HeapVTable<FunctorT>::target_const,
989 #endif
990  };
991  return &kVTable;
992  }
993 }
994 
995 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
996 template <typename FunctorT, typename SourceT>
997 inline void Function<ReturnT(ArgsT...), SboSizeT>::construct_from(SourceT&& src) {
998  if constexpr (kIsInline<FunctorT>) {
999  ::new (&storage_) FunctorT(std::forward<SourceT>(src));
1000  } else {
1001  auto& pool = MemoryPool::global_instance();
1002  auto* mem = pool.allocate(sizeof(FunctorT), alignof(FunctorT));
1003 
1004  if VUNLIKELY (mem == nullptr) {
1005  throw std::bad_alloc(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1006  }
1007 
1008  try {
1009  auto* new_f = ::new (mem) FunctorT(std::forward<SourceT>(src));
1010  ::new (static_cast<void*>(&storage_)) FunctorT*(new_f);
1011  } catch (...) {
1012  pool.deallocate(mem, sizeof(FunctorT), alignof(FunctorT));
1013  throw;
1014  }
1015  }
1016 
1017  vtable_ = get_vtable<FunctorT>();
1018 }
1019 
1020 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1021 inline void Function<ReturnT(ArgsT...), SboSizeT>::copy_from(const Function& other) {
1022  if VLIKELY (other.vtable_ != nullptr) {
1023  other.vtable_->copy_construct(&storage_, &other.storage_);
1024  vtable_ = other.vtable_;
1025  }
1026 }
1027 
1028 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1029 inline void Function<ReturnT(ArgsT...), SboSizeT>::move_from(Function&& other) noexcept {
1030  if VLIKELY (other.vtable_ != nullptr) {
1031  other.vtable_->move_construct(&storage_, &other.storage_);
1032  vtable_ = other.vtable_;
1033  other.vtable_ = nullptr;
1034  }
1035 }
1036 
1037 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1038 inline void Function<ReturnT(ArgsT...), SboSizeT>::reset() noexcept {
1039  if VLIKELY (vtable_ != nullptr) {
1040  vtable_->destroy(&storage_);
1041  vtable_ = nullptr;
1042  }
1043 }
1044 
1045 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1046 inline void swap(Function<ReturnT(ArgsT...), SboSizeT>& lhs, Function<ReturnT(ArgsT...), SboSizeT>& rhs) noexcept {
1047  lhs.swap(rhs);
1048 }
1049 
1050 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1051 inline bool operator==(const Function<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept {
1052  return !cb;
1053 }
1054 
1055 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1056 inline bool operator==(std::nullptr_t, const Function<ReturnT(ArgsT...), SboSizeT>& cb) noexcept {
1057  return !cb;
1058 }
1059 
1060 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1061 inline bool operator!=(const Function<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept {
1062  return static_cast<bool>(cb);
1063 }
1064 
1065 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1066 inline bool operator!=(std::nullptr_t, const Function<ReturnT(ArgsT...), SboSizeT>& cb) noexcept {
1067  return static_cast<bool>(cb);
1068 }
1069 
1070 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1071 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>::MoveFunction(std::nullptr_t) noexcept {}
1072 
1073 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1074 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>::MoveFunction(MoveFunction&& other) noexcept {
1075  move_from(std::move(other));
1076 }
1077 
1078 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1079 template <typename FunctorT, typename DecayFunctorT, typename>
1080 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>::MoveFunction(FunctorT&& f) {
1081  if constexpr (kIsFunctionWrapper<DecayFunctorT>) {
1082  if VUNLIKELY (!f) {
1083  return;
1084  }
1085  } else if constexpr (kIsPointerLike<DecayFunctorT>) {
1086  if constexpr (std::is_pointer_v<std::remove_reference_t<FunctorT>> ||
1087  std::is_member_pointer_v<std::remove_reference_t<FunctorT>>) {
1088  if VUNLIKELY (f == nullptr) {
1089  return;
1090  }
1091  }
1092  }
1093 
1094  construct_from<DecayFunctorT>(std::forward<FunctorT>(f));
1095 }
1096 
1097 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1098 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>& MoveFunction<ReturnT(ArgsT...), SboSizeT>::operator=(
1099  MoveFunction&& other) noexcept {
1100  if VLIKELY (this != &other) {
1101  reset();
1102  move_from(std::move(other));
1103  }
1104 
1105  return *this;
1106 }
1107 
1108 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1109 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>& MoveFunction<ReturnT(ArgsT...), SboSizeT>::operator=(
1110  std::nullptr_t) noexcept {
1111  reset();
1112  return *this;
1113 }
1114 
1115 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1116 template <typename FunctorT, typename DecayFunctorT, typename>
1117 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>& MoveFunction<ReturnT(ArgsT...), SboSizeT>::operator=(FunctorT&& f) {
1118  MoveFunction tmp(std::forward<FunctorT>(f));
1119  swap(tmp);
1120  return *this;
1121 }
1122 
1123 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1124 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>::~MoveFunction() {
1125  reset();
1126 }
1127 
1128 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1129 inline ReturnT MoveFunction<ReturnT(ArgsT...), SboSizeT>::operator()(ArgsT... args) {
1130  if VUNLIKELY (vtable_ == nullptr) {
1132  }
1133 
1134  return vtable_->invoke(&storage_, std::forward<ArgsT>(args)...);
1135 }
1136 
1137 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1138 inline MoveFunction<ReturnT(ArgsT...), SboSizeT>::operator bool() const noexcept {
1139  return vtable_ != nullptr;
1140 }
1141 
1142 #if defined(__cpp_rtti)
1143 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1144 inline const std::type_info& MoveFunction<ReturnT(ArgsT...), SboSizeT>::target_type() const noexcept {
1145  if VLIKELY (vtable_ != nullptr) {
1146  return vtable_->target_type();
1147  }
1148 
1149  return typeid(void);
1150 }
1151 
1152 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1153 template <typename FunctorT>
1154 inline FunctorT* MoveFunction<ReturnT(ArgsT...), SboSizeT>::target() noexcept {
1155  if constexpr (std::is_object_v<FunctorT>) {
1156  if VLIKELY (vtable_ != nullptr && typeid(FunctorT) == target_type()) {
1157  return static_cast<FunctorT*>(vtable_->target(&storage_));
1158  }
1159  }
1160 
1161  return nullptr;
1162 }
1163 
1164 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1165 template <typename FunctorT>
1166 inline const FunctorT* MoveFunction<ReturnT(ArgsT...), SboSizeT>::target() const noexcept {
1167  if constexpr (std::is_object_v<FunctorT>) {
1168  if VLIKELY (vtable_ != nullptr && typeid(FunctorT) == target_type()) {
1169  return static_cast<const FunctorT*>(vtable_->target_const(&storage_));
1170  }
1171  }
1172 
1173  return nullptr;
1174 }
1175 #endif
1176 
1177 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1178 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::swap(MoveFunction& other) noexcept {
1179  if VUNLIKELY (this == &other) {
1180  return; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1181  }
1182 
1183  MoveFunction tmp(std::move(*this));
1184  *this = std::move(other);
1185  other = std::move(tmp);
1186 }
1187 
1188 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1189 template <typename FunctorT>
1190 inline ReturnT MoveFunction<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::invoke(void* storage, ArgsT... args) {
1191  auto* f = std::launder(reinterpret_cast<FunctorT*>(storage));
1192 
1193  if constexpr (std::is_void_v<ReturnT>) {
1194  std::invoke(*f, std::forward<ArgsT>(args)...);
1195  } else {
1196  return std::invoke(*f, std::forward<ArgsT>(args)...);
1197  }
1198 }
1199 
1200 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1201 template <typename FunctorT>
1202 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::move_construct(void* dst,
1203  void* src) noexcept {
1204  auto* src_f = std::launder(reinterpret_cast<FunctorT*>(src));
1205  ::new (dst) FunctorT(std::move(*src_f));
1206  src_f->~FunctorT();
1207 }
1208 
1209 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1210 template <typename FunctorT>
1211 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::destroy(void* storage) noexcept {
1212  auto* f = std::launder(reinterpret_cast<FunctorT*>(storage));
1213  f->~FunctorT();
1214 }
1215 
1216 #if defined(__cpp_rtti)
1217 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1218 template <typename FunctorT>
1219 inline const std::type_info& MoveFunction<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::target_type() noexcept {
1220  return typeid(FunctorT);
1221 }
1222 
1223 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1224 template <typename FunctorT>
1225 inline void* MoveFunction<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::target(void* storage) noexcept {
1226  return storage;
1227 }
1228 
1229 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1230 template <typename FunctorT>
1231 inline const void* MoveFunction<ReturnT(ArgsT...), SboSizeT>::InlineVTable<FunctorT>::target_const(
1232  const void* storage) noexcept {
1233  return storage;
1234 }
1235 #endif
1236 
1237 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1238 template <typename FunctorT>
1239 inline ReturnT MoveFunction<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::invoke(void* storage, ArgsT... args) {
1240  FunctorT* f = *std::launder(static_cast<FunctorT**>(storage));
1241 
1242  if constexpr (std::is_void_v<ReturnT>) {
1243  std::invoke(*f, std::forward<ArgsT>(args)...);
1244  } else {
1245  return std::invoke(*f, std::forward<ArgsT>(args)...);
1246  }
1247 }
1248 
1249 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1250 template <typename FunctorT>
1251 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::move_construct(void* dst,
1252  void* src) noexcept {
1253  FunctorT** src_slot = std::launder(static_cast<FunctorT**>(src));
1254  FunctorT* src_f = *src_slot;
1255  ::new (dst) FunctorT*(src_f);
1256  *src_slot = nullptr;
1257 }
1258 
1259 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1260 template <typename FunctorT>
1261 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::destroy(void* storage) noexcept {
1262  FunctorT** slot = std::launder(static_cast<FunctorT**>(storage));
1263  FunctorT* f = *slot;
1264 
1265  if VLIKELY (f != nullptr) {
1266  f->~FunctorT();
1267 
1268  auto& pool = MemoryPool::global_instance();
1269  pool.deallocate(f, sizeof(FunctorT), alignof(FunctorT));
1270 
1271  *slot = nullptr;
1272  }
1273 }
1274 
1275 #if defined(__cpp_rtti)
1276 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1277 template <typename FunctorT>
1278 inline const std::type_info& MoveFunction<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::target_type() noexcept {
1279  return typeid(FunctorT);
1280 }
1281 
1282 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1283 template <typename FunctorT>
1284 inline void* MoveFunction<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::target(void* storage) noexcept {
1285  return *std::launder(static_cast<FunctorT**>(storage));
1286 }
1287 
1288 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1289 template <typename FunctorT>
1290 inline const void* MoveFunction<ReturnT(ArgsT...), SboSizeT>::HeapVTable<FunctorT>::target_const(
1291  const void* storage) noexcept {
1292  return *std::launder(static_cast<FunctorT* const*>(storage));
1293 }
1294 #endif
1295 
1296 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1297 template <typename FunctorT>
1298 inline const typename MoveFunction<ReturnT(ArgsT...), SboSizeT>::VTable*
1299 MoveFunction<ReturnT(ArgsT...), SboSizeT>::get_vtable() noexcept {
1300  if constexpr (kIsInline<FunctorT>) {
1301  static constexpr VTable kVTable = {
1302  &InlineVTable<FunctorT>::invoke, &InlineVTable<FunctorT>::move_construct,
1303  &InlineVTable<FunctorT>::destroy,
1304 #if defined(__cpp_rtti)
1305  &InlineVTable<FunctorT>::target_type, &InlineVTable<FunctorT>::target,
1306  &InlineVTable<FunctorT>::target_const,
1307 #endif
1308  };
1309  return &kVTable;
1310  } else {
1311  static constexpr VTable kVTable = {
1312  &HeapVTable<FunctorT>::invoke, &HeapVTable<FunctorT>::move_construct, &HeapVTable<FunctorT>::destroy,
1313 #if defined(__cpp_rtti)
1314  &HeapVTable<FunctorT>::target_type, &HeapVTable<FunctorT>::target, &HeapVTable<FunctorT>::target_const,
1315 #endif
1316  };
1317  return &kVTable;
1318  }
1319 }
1320 
1321 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1322 template <typename FunctorT, typename SourceT>
1323 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::construct_from(SourceT&& src) {
1324  if constexpr (kIsInline<FunctorT>) {
1325  ::new (&storage_) FunctorT(std::forward<SourceT>(src));
1326  } else {
1327  auto& pool = MemoryPool::global_instance();
1328  auto* mem = pool.allocate(sizeof(FunctorT), alignof(FunctorT));
1329 
1330  if VUNLIKELY (mem == nullptr) {
1331  throw std::bad_alloc(); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1332  }
1333 
1334  try {
1335  auto* new_f = ::new (mem) FunctorT(std::forward<SourceT>(src));
1336  ::new (static_cast<void*>(&storage_)) FunctorT*(new_f);
1337  } catch (...) { // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1338  pool.deallocate(mem, sizeof(FunctorT), alignof(FunctorT)); // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1339  throw; // LCOV_EXCL_LINE GCOVR_EXCL_LINE
1340  }
1341  }
1342 
1343  vtable_ = get_vtable<FunctorT>();
1344 }
1345 
1346 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1347 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::move_from(MoveFunction&& other) noexcept {
1348  if VLIKELY (other.vtable_ != nullptr) {
1349  other.vtable_->move_construct(&storage_, &other.storage_);
1350  vtable_ = other.vtable_;
1351  other.vtable_ = nullptr;
1352  }
1353 }
1354 
1355 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1356 inline void MoveFunction<ReturnT(ArgsT...), SboSizeT>::reset() noexcept {
1357  if VLIKELY (vtable_ != nullptr) {
1358  vtable_->destroy(&storage_);
1359  vtable_ = nullptr;
1360  }
1361 }
1362 
1363 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1364 inline void swap(MoveFunction<ReturnT(ArgsT...), SboSizeT>& lhs,
1365  MoveFunction<ReturnT(ArgsT...), SboSizeT>& rhs) noexcept {
1366  lhs.swap(rhs);
1367 }
1368 
1369 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1370 inline bool operator==(const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept {
1371  return !cb;
1372 }
1373 
1374 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1375 inline bool operator==(std::nullptr_t, const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb) noexcept {
1376  return !cb;
1377 }
1378 
1379 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1380 inline bool operator!=(const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb, std::nullptr_t) noexcept {
1381  return static_cast<bool>(cb);
1382 }
1383 
1384 template <typename ReturnT, typename... ArgsT, size_t SboSizeT>
1385 inline bool operator!=(std::nullptr_t, const MoveFunction<ReturnT(ArgsT...), SboSizeT>& cb) noexcept {
1386  return static_cast<bool>(cb);
1387 }
1388 
1389 } // namespace vlink
1390 
1391 #else
1392 
1393 namespace vlink {
1394 
1395 template <typename SignatureT>
1396 using Function = std::function<SignatureT>;
1397 
1398 template <typename SignatureT>
1399 using LargeFunction = std::function<SignatureT>;
1400 
1401 template <typename SignatureT>
1402 using function = std::function<SignatureT>;
1403 
1404 #if defined(__cpp_lib_move_only_function) && __cpp_lib_move_only_function >= 202110L
1405 [[maybe_unused]] static constexpr bool kIsSupportMoveFunction = true;
1406 template <typename SignatureT>
1407 using MoveFunction = std::move_only_function<SignatureT>;
1408 
1409 template <typename SignatureT>
1410 using LargeMoveFunction = std::move_only_function<SignatureT>;
1411 
1412 template <typename SignatureT>
1413 using move_only_function = std::move_only_function<SignatureT>;
1414 #else
1415 [[maybe_unused]] static constexpr bool kIsSupportMoveFunction = false;
1416 template <typename SignatureT>
1417 using MoveFunction = std::function<SignatureT>;
1418 
1419 template <typename SignatureT>
1420 using LargeMoveFunction = std::function<SignatureT>;
1421 
1422 template <typename SignatureT>
1423 using move_only_function = std::function<SignatureT>;
1424 #endif
1425 
1426 } // namespace vlink
1427 
1428 #endif
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_EXPORT
Definition: macros.h:81
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
Size-class tiered memory pool with per-tier free lists and runtime statistics.