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