30#ifndef LIBSBX_CORE_DELEGATE_HPP_
31#define LIBSBX_CORE_DELEGATE_HPP_
42#include <libsbx/core/concepts.hpp>
44#include <libsbx/memory/aligned_storage.hpp>
51 : std::runtime_error(
"bad_delegate_call") {}
54template<
typename Signature>
63template<
typename Return,
typename... Args>
66 using static_storage_type = memory::aligned_storage_t<
sizeof(std::byte*),
alignof(std::byte*)>;
67 using dynamic_storage_type = std::byte*;
70 static_storage_type static_storage;
71 dynamic_storage_type dynamic_storage;
80 : _vtable{
nullptr} { }
86 : _vtable{
nullptr} { }
95 template<callable<Return, Args...> Callable>
96 requires (!std::is_same_v<std::remove_reference_t<Callable>, delegate>)
98 : _vtable{_create_vtable<std::remove_reference_t<Callable>>()},
99 _storage{_create_storage<std::remove_reference_t<Callable>>(std::forward<std::remove_reference_t<Callable>>(
callable))} { }
101 delegate(Return(*callable)(Args...))
102 : delegate{[callable](Args... args){ std::invoke(callable, std::forward<Args>(args)...); }} { }
104 template<
typename Class>
105 delegate(Class& instance, Return(Class::*method)(Args...))
106 : delegate{_wrap_method(&instance, method)} { }
108 template<
typename Class>
109 delegate(Class& instance, Return(Class::*method)(Args...)const)
110 : delegate{_wrap_method(&instance, method)} { }
112 template<
typename Class>
113 delegate(
const Class& instance, Return(Class::*method)(Args...)const)
114 : delegate{_wrap_method(&instance, method)} { }
116 delegate(
const delegate& other)
117 : _vtable{other._vtable} {
119 _vtable->copy(other._storage, _storage);
123 delegate(delegate&& other) noexcept
124 : _vtable{std::exchange(other._vtable,
nullptr)} {
126 _vtable->move(other._storage, _storage);
132 _vtable->destroy(_storage);
136 delegate& operator=(
const delegate& other) {
137 if (
this != &other) {
139 _vtable->destroy(_storage);
142 _vtable = other._vtable;
145 _vtable->copy(other._storage, _storage);
152 delegate& operator=(delegate&& other)
noexcept {
153 if (
this != &other) {
155 _vtable->destroy(_storage);
158 _vtable = std::exchange(other._vtable,
nullptr);
161 _vtable->move(other._storage, _storage);
168 auto invoke(Args&&... args)
const {
170 throw std::runtime_error{
"bad_delegate_call"};
173 return std::invoke(_vtable->invoke, _storage, std::forward<Args>(args)...);
176 Return operator()(Args&&... args)
const {
177 return invoke(std::forward<Args>(args)...);
180 auto is_valid() const noexcept {
181 return _vtable !=
nullptr;
184 operator bool() const noexcept {
190 template<
typename Callable>
191 inline static constexpr auto requires_dynamic_allocation_v = !(std::is_nothrow_move_constructible_v<Callable> &&
sizeof(Callable) <=
sizeof(static_storage_type) &&
alignof(Callable) <=
alignof(static_storage_type));
193 template<
typename Class>
194 auto _wrap_method(Class* instance, Return(Class::*method)(Args...)) {
195 return [instance, method](Args... args){ std::invoke(method, instance, std::forward<Args>(args)...); };
198 template<
typename Class>
199 auto _wrap_method(Class* instance, Return(Class::*method)(Args...)const) {
200 return [instance, method](Args... args){ std::invoke(method, instance, std::forward<Args>(args)...); };
203 template<
typename Class>
204 auto _wrap_method(
const Class* instance, Return(Class::*method)(Args...)const) {
205 return [instance, method](Args... args){ std::invoke(method, instance, std::forward<Args>(args)...); };
209 Return(*invoke)(
const storage& storage, Args&&... args);
210 void(*copy)(
const storage& source, storage& destination);
211 void(*move)(storage& source, storage& destination);
212 void(*destroy)(storage& storage);
215 template<
typename Callable>
216 struct static_vtable {
217 static Return invoke(
const storage& storage, Args&&... args) {
218 return std::invoke(
reinterpret_cast<const Callable&
>(storage.static_storage), std::forward<Args>(args)...);
221 static void copy(
const storage& source, storage& destination) {
222 std::construct_at(
reinterpret_cast<Callable*
>(&destination.static_storage),
reinterpret_cast<const Callable&
>(source.static_storage));
225 static void move(storage& source, storage& destination) {
226 std::construct_at(
reinterpret_cast<Callable*
>(&destination.static_storage), std::move(
reinterpret_cast<Callable&
>(source.static_storage)));
230 static void destroy(storage& storage) {
231 std::destroy_at(
reinterpret_cast<Callable*
>(&storage.static_storage));
235 template<
typename Callable>
236 struct dynamic_vtable {
237 static Return invoke(
const storage& storage, Args&&... args) {
238 return std::invoke(*
reinterpret_cast<const Callable*
>(storage.dynamic_storage), std::forward<Args>(args)...);
241 static void copy(
const storage& source, storage& destination) {
242 destination.dynamic_storage =
reinterpret_cast<dynamic_storage_type
>(
new Callable{*
reinterpret_cast<Callable*
>(source.dynamic_storage)});
245 static void move(storage& source, storage& destination) {
246 destination.dynamic_storage = source.dynamic_storage;
247 source.dynamic_storage =
nullptr;
250 static void destroy(storage& storage) {
251 delete reinterpret_cast<Callable*
>(storage.dynamic_storage);
255 template<
typename Callable>
256 static vtable* _create_vtable() {
257 using vtable_type = std::conditional_t<requires_dynamic_allocation_v<Callable>, dynamic_vtable<Callable>, static_vtable<Callable>>;
259 static auto instance = vtable{
269 template<
typename Callable>
270 storage _create_storage(Callable&& callable) {
271 if constexpr (requires_dynamic_allocation_v<Callable>) {
273 return storage{ .dynamic_storage =
reinterpret_cast<dynamic_storage_type
>(
new Callable{std::forward<Callable>(callable)}) };
275 auto static_storage = static_storage_type{};
276 std::construct_at(
reinterpret_cast<Callable*
>(&static_storage), std::forward<Callable>(callable));
277 return storage{ .static_storage = static_storage };
282 mutable storage _storage{};
delegate(Callable &&callable)
Construct a delegate from a functor type.
Definition: delegate.hpp:97
delegate() noexcept
Default constructor.
Definition: delegate.hpp:79
delegate(std::nullptr_t) noexcept
Constructs a delegate from a nullptr.
Definition: delegate.hpp:85
Definition: delegate.hpp:55
Describes a type or object that can be invoked with the give parameters and return the given type.
Definition: concepts.hpp:17
Exception type that is thrown when a delegate that does not hold a handle is invoked.
Definition: delegate.hpp:49