38 class function< R(Args...) >
40 using callable_f = R (*)(function< R(Args...) >*, Args...);
41 using copy_ctor_f = void (*)(function< R(Args...) >*, function< R(Args...) >
const*);
42 using copy_assign_f = void (*)(function< R(Args...) >*, function< R(Args...) >
const*);
43 using move_ctor_f = void (*)(function< R(Args...) >*, function< R(Args...) >*)
noexcept;
44 using move_assign_f = void (*)(function< R(Args...) >*, function< R(Args...) >*)
noexcept;
45 using reset_f = void (*)(function< R(Args...) >*)
noexcept;
46 using destroy_f = void (*)(function< R(Args...) >*)
noexcept;
50 copy_ctor_f
const m_copy_ctor;
51 copy_assign_f
const m_copy_assign;
52 move_ctor_f
const m_move_ctor;
53 move_assign_f
const m_move_assign;
54 reset_f
const m_reset;
55 destroy_f
const m_destroy;
56 callable_f
const m_call;
59 static std::size_t
constexpr sbo_size = 16;
60 static std::size_t
constexpr sbo_align = 16;
62 callable_f m_callable;
63 impl_tbl
const* m_impl_tbl;
64 alignas(sbo_align) std::array< std::byte, sbo_size > m_storage;
66 template <
typename Functor >
67 static constexpr bool use_sbo()
69 return sizeof(Functor) <= sbo_size &&
alignof(Functor) <= sbo_align && std::is_nothrow_copy_constructible_v< Functor > && std::is_nothrow_copy_assignable_v< Functor >;
79 template <
typename Functor >
80 static Functor* get_storage_address(function< R(Args...) >* f)
82 if constexpr (use_sbo< Functor >())
84 return std::launder< Functor >(
reinterpret_cast< Functor*
>(f->m_storage.data()));
88 static_assert(
alignof(Functor*) <= sbo_align,
"Functor pointer must fit in storage for non-SBO case");
89 static_assert(
sizeof(Functor*) <= sbo_size,
"Functor pointer must fit in storage for non-SBO case");
90 return std::launder< Functor >(*
reinterpret_cast< Functor**
>(f->m_storage.data()));
94 template <
typename Functor >
95 static Functor
const* get_storage_address(function< R(Args...) >
const* f)
97 if constexpr (use_sbo< Functor >())
99 return std::launder< Functor const >(
reinterpret_cast< Functor const*
>(f->m_storage.data()));
103 static_assert(
alignof(Functor*) <= sbo_align,
"Functor pointer must fit in storage for non-SBO case");
104 static_assert(
sizeof(Functor*) <= sbo_size,
"Functor pointer must fit in storage for non-SBO case");
105 return std::launder< Functor const >(*
reinterpret_cast< Functor* const*
>(f->m_storage.data()));
109 template < auto& function_ref >
112 static constexpr auto&
id = function_ref;
115 template < auto& function_ref >
116 auto make_dispatch_for(
decltype(function_ref)&) -> dispatch_for< function_ref >
121 template < auto& Function >
122 static R free_call_impl(function< R(Args...) >* f, Args... args)
124 return Function(args...);
127 template < auto& Function >
128 static void free_copy_ctor_impl(function< R(Args...) >* self, function< R(Args...) >
const* other)
130 self->m_impl_tbl = &free_vtbl< Function >;
131 self->m_callable = &free_call_impl< Function >;
134 template < auto& Function >
135 static void free_copy_assign_impl(function< R(Args...) >* self, function< R(Args...) >
const* other)
137 self->m_impl_tbl->m_destroy(self);
138 self->m_impl_tbl = &free_vtbl< Function >;
139 self->m_callable = &free_call_impl< Function >;
142 template < auto& Function >
143 static void free_move_ctor_impl(function< R(Args...) >* self, function< R(Args...) >* other)
noexcept
145 self->m_impl_tbl = &free_vtbl< Function >;
146 self->m_callable = &free_call_impl< Function >;
149 template < auto& Function >
150 static void free_move_assign_impl(function< R(Args...) >* self, function< R(Args...) >* other)
noexcept
152 self->m_impl_tbl->m_destroy(self);
153 self->m_impl_tbl = &free_vtbl< Function >;
154 self->m_callable = &free_call_impl< Function >;
157 template <
typename Functor >
158 static void copy_ctor_impl(function< R(Args...) >* self, function< R(Args...) >
const* other)
160 if constexpr (use_sbo< Functor >())
162 Functor* self_ptr = std::launder< Functor >(get_storage_address< Functor >(self));
163 const Functor* other_ptr = std::launder< Functor const >(get_storage_address< Functor >(other));
164 new (self_ptr) Functor(*other_ptr);
165 self->m_impl_tbl = &vtbl< Functor >;
166 self->m_callable = &call_impl< Functor >;
171 const Functor* other_ptr = get_storage_address< Functor >(other);
172 Functor* new_functor =
new Functor(*other_ptr);
173 new ((
void*)self->m_storage.data()) Functor*(new_functor);
174 self->m_impl_tbl = &vtbl< Functor >;
175 self->m_callable = &call_impl< Functor >;
179 template <
typename Functor >
180 static void move_ctor_impl(function< R(Args...) >* self, function< R(Args...) >* other)
noexcept
182 if constexpr (use_sbo< Functor >())
184 Functor* self_ptr = std::launder< Functor >(get_storage_address< Functor >(self));
185 const Functor* other_ptr = std::launder< Functor const >(get_storage_address< Functor >(other));
186 new (self_ptr) Functor(std::move(*other_ptr));
187 self->m_impl_tbl = &vtbl< Functor >;
188 self->m_callable = &call_impl< Functor >;
193 Functor* other_ptr = std::launder< Functor >(get_storage_address< Functor >(other));
194 self->m_impl_tbl = &vtbl< Functor >;
195 self->m_callable = &call_impl< Functor >;
196 new ((
void*)self->m_storage.data()) Functor*(other_ptr);
197 *std::launder(
reinterpret_cast< Functor**
>(other->m_storage.data())) =
nullptr;
198 other->m_impl_tbl = &void_vtbl;
199 other->m_callable = &null_call_impl;
203 template <
typename Functor >
204 static void copy_assign_impl(function< R(Args...) >* self, function< R(Args...) >
const* other)
211 if constexpr (use_sbo< Functor >())
213 self->m_impl_tbl->m_destroy(self);
215 copy_ctor_impl< Functor >(self, other);
216 if constexpr (
sizeof(Functor) <
sizeof(m_storage))
219 poison_region(
reinterpret_cast< std::byte*
>(self) +
sizeof(Functor),
sizeof(m_storage) -
sizeof(Functor));
225 const Functor* other_ptr = get_storage_address< Functor >(other);
226 Functor* new_functor =
new Functor(*other_ptr);
227 self->m_impl_tbl->m_destroy(self);
228 new ((
void*)self->m_storage.data()) Functor*(new_functor);
229 self->m_impl_tbl = &vtbl< Functor >;
230 self->m_callable = vtbl< Functor >.m_call;
234 template <
typename Functor >
235 static void move_assign_impl(function< R(Args...) >* self, function< R(Args...) >* other)
noexcept
241 self->m_impl_tbl->m_destroy(self);
242 move_ctor_impl< Functor >(self, other);
245 template <
typename Functor >
246 static void destroy_impl(function< R(Args...) >* f)
noexcept
248 Functor* ptr = get_storage_address< Functor >(f);
249 if constexpr (use_sbo< Functor >())
259 static void null_copy_ctor_impl(function< R(Args...) >* self, function< R(Args...) >
const* other)
noexcept
261 self->m_impl_tbl = &void_vtbl;
262 self->m_callable = &null_call_impl;
265 static void null_move_ctor_impl(function< R(Args...) >* self, function< R(Args...) >* other)
noexcept
267 self->m_impl_tbl = &void_vtbl;
268 self->m_callable = &null_call_impl;
271 static void null_copy_assign_impl(function< R(Args...) >* self, function< R(Args...) >
const* other)
noexcept
273 self->m_impl_tbl->m_destroy(self);
274 self->m_impl_tbl = &void_vtbl;
275 self->m_callable = &null_call_impl;
278 static void null_move_assign_impl(function< R(Args...) >* self, function< R(Args...) >* other)
noexcept
280 self->m_impl_tbl->m_destroy(self);
281 self->m_impl_tbl = &void_vtbl;
282 self->m_callable = &null_call_impl;
285 static void null_destroy_impl(function< R(Args...) >* f)
noexcept
290 static void null_reset_impl(function< R(Args...) >* f)
noexcept
293 poison_region(
reinterpret_cast< void*
>(&f->m_storage),
sizeof(f->m_storage));
296 template <
typename Functor >
297 static void reset_impl(function< R(Args...) >* f)
noexcept
299 destroy_impl< Functor >(f);
300 f->m_impl_tbl = &void_vtbl;
301 f->m_callable = &null_call_impl;
304 template <
typename Functor >
305 static R call_impl(function< R(Args...) >* f, Args... args)
307 Functor* ptr = get_storage_address< Functor >(f);
308 return (*ptr)(std::forward< Args >(args)...);
311 static R null_call_impl(function< R(Args...) >* f, Args... args)
313 throw std::bad_function_call();
316 template <
typename Functor >
317 static constexpr impl_tbl make_impl_tbl()
319 return {.m_copy_ctor = ©_ctor_impl< Functor >, .m_copy_assign = ©_assign_impl< Functor >, .m_move_ctor = &move_ctor_impl< Functor >, .m_move_assign = &move_assign_impl< Functor >, .m_reset = &reset_impl< Functor >, .m_destroy = &destroy_impl< Functor >, .m_call = &call_impl< Functor >};
322 static constexpr impl_tbl make_null_impl_tbl()
325 .m_copy_ctor = &null_copy_ctor_impl,
326 .m_copy_assign = &null_copy_assign_impl,
327 .m_move_ctor = &null_move_ctor_impl,
328 .m_move_assign = &null_move_assign_impl,
329 .m_reset = &null_reset_impl,
330 .m_destroy = &null_destroy_impl,
331 .m_call = &null_call_impl,
335 template <
typename Functor >
336 static constexpr impl_tbl
const vtbl = make_impl_tbl< Functor >();
337 template < auto& Function >
338 static constexpr impl_tbl
const free_vtbl = {.m_copy_ctor = &free_copy_ctor_impl< Function >, .m_copy_assign = &free_copy_assign_impl< Function >, .m_move_ctor = &free_move_ctor_impl< Function >, .m_move_assign = &free_move_assign_impl< Function >, .m_reset = &null_reset_impl, .m_destroy = &null_destroy_impl, .m_call = &free_call_impl< Function >};
340 static constexpr impl_tbl
const void_vtbl = make_null_impl_tbl();
343 function() noexcept : m_callable(&null_call_impl), m_impl_tbl(&void_vtbl)
345 poison_region(
reinterpret_cast< void*
>(&m_storage),
sizeof(m_storage));
350 m_impl_tbl->m_destroy(
this);
357 template < auto& fn >
361 static constexpr auto&
id = fn;
369 template < auto& fn >
382 template <
typename Functor,
typename = std::enable_if_t< !std::is_same_v< std::decay_t< Functor >, function > > >
383 function(Functor&& f)
noexcept(use_sbo< std::decay_t< Functor > >())
385 using Decayed = std::decay_t< Functor >;
386 static_assert(std::is_invocable_r_v< R, Decayed, Args... >,
"Functor must be invokable with the correct signature");
388 if constexpr (use_sbo< Decayed >())
390 new (get_storage_address< Decayed >(
this)) Decayed(std::forward< Functor >(f));
391 m_impl_tbl = &vtbl< Decayed >;
392 m_callable = &call_impl< Decayed >;
396 Decayed* storage =
new Decayed(std::forward< Functor >(f));
397 new ((
void*)m_storage.data()) Decayed*(storage);
398 m_impl_tbl = &vtbl< Decayed >;
399 m_callable = &call_impl< Decayed >;
406 other.m_impl_tbl->m_copy_ctor(
this, &other);
412 other.m_impl_tbl->m_move_ctor(
this, &other);
422 other.m_impl_tbl->m_copy_assign(
this, &other);
433 other.m_impl_tbl->m_move_assign(
this, &other);
438 operator bool() const noexcept
440 return m_callable != &null_call_impl;
451 return m_callable(
this, std::forward< Args >(args)...);
457 return m_callable == &null_call_impl;