31#ifndef LIBSBX_CORE_DELEGATE_HPP_
32#define LIBSBX_CORE_DELEGATE_HPP_
43#include <libsbx/core/concepts.hpp>
45#include <libsbx/memory/aligned_storage.hpp>
52 : std::runtime_error(
"bad_delegate_call") {}
55template<
typename Signature>
64template<
typename Return,
typename... Args>
67 using static_storage_type = memory::aligned_storage_t<
sizeof(std::byte*),
alignof(std::byte*)>;
68 using dynamic_storage_type = std::byte*;
71 static_storage_type static_storage;
72 dynamic_storage_type dynamic_storage;
81 : _vtable{
nullptr} { }
87 : _vtable{
nullptr} { }
96 template<callable<Return, Args...> Callable>
97 requires (!std::is_same_v<std::remove_reference_t<Callable>, delegate>)
99 : _vtable{_create_vtable<std::remove_reference_t<Callable>>()},
100 _storage{_create_storage<std::remove_reference_t<Callable>>(std::forward<std::remove_reference_t<Callable>>(
callable))} { }
102 delegate(Return(*callable)(Args...))
103 : delegate{[callable](Args... args){ std::invoke(callable, std::forward<Args>(args)...); }} { }
105 template<
typename Class>
106 delegate(Class& instance, Return(Class::*method)(Args...))
107 : delegate{_wrap_method(&instance, method)} { }
109 template<
typename Class>
110 delegate(Class& instance, Return(Class::*method)(Args...)const)
111 : delegate{_wrap_method(&instance, method)} { }
113 template<
typename Class>
114 delegate(
const Class& instance, Return(Class::*method)(Args...)const)
115 : delegate{_wrap_method(&instance, method)} { }
117 delegate(
const delegate& other)
118 : _vtable{other._vtable} {
120 _vtable->copy(other._storage, _storage);
124 delegate(delegate&& other) noexcept
125 : _vtable{std::exchange(other._vtable,
nullptr)} {
127 _vtable->move(other._storage, _storage);
133 _vtable->destroy(_storage);
137 delegate& operator=(
const delegate& other) {
138 if (
this != &other) {
140 _vtable->destroy(_storage);
143 _vtable = other._vtable;
146 _vtable->copy(other._storage, _storage);
153 delegate& operator=(delegate&& other)
noexcept {
154 if (
this != &other) {
156 _vtable->destroy(_storage);
159 _vtable = std::exchange(other._vtable,
nullptr);
162 _vtable->move(other._storage, _storage);
169 auto invoke(Args&&... args)
const {
171 throw std::runtime_error{
"bad_delegate_call"};
174 return std::invoke(_vtable->invoke, _storage, std::forward<Args>(args)...);
177 Return operator()(Args&&... args)
const {
178 return invoke(std::forward<Args>(args)...);
181 auto is_valid() const noexcept {
182 return _vtable !=
nullptr;
185 operator bool() const noexcept {
191 template<
typename Callable>
192 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));
194 template<
typename Class>
195 auto _wrap_method(Class* instance, Return(Class::*method)(Args...)) {
196 return [instance, method](Args... args){ std::invoke(method, instance, std::forward<Args>(args)...); };
199 template<
typename Class>
200 auto _wrap_method(Class* instance, Return(Class::*method)(Args...)const) {
201 return [instance, method](Args... args){ std::invoke(method, instance, std::forward<Args>(args)...); };
204 template<
typename Class>
205 auto _wrap_method(
const Class* instance, Return(Class::*method)(Args...)const) {
206 return [instance, method](Args... args){ std::invoke(method, instance, std::forward<Args>(args)...); };
210 Return(*invoke)(
const storage& storage, Args&&... args);
211 void(*copy)(
const storage& source, storage& destination);
212 void(*move)(storage& source, storage& destination);
213 void(*destroy)(storage& storage);
216 template<
typename Callable>
217 struct static_vtable {
218 static Return invoke(
const storage& storage, Args&&... args) {
219 return std::invoke(
reinterpret_cast<const Callable&
>(storage.static_storage), std::forward<Args>(args)...);
222 static void copy(
const storage& source, storage& destination) {
223 std::construct_at(
reinterpret_cast<Callable*
>(&destination.static_storage),
reinterpret_cast<const Callable&
>(source.static_storage));
226 static void move(storage& source, storage& destination) {
227 std::construct_at(
reinterpret_cast<Callable*
>(&destination.static_storage), std::move(
reinterpret_cast<Callable&
>(source.static_storage)));
231 static void destroy(storage& storage) {
232 std::destroy_at(
reinterpret_cast<Callable*
>(&storage.static_storage));
236 template<
typename Callable>
237 struct dynamic_vtable {
238 static Return invoke(
const storage& storage, Args&&... args) {
239 return std::invoke(*
reinterpret_cast<const Callable*
>(storage.dynamic_storage), std::forward<Args>(args)...);
242 static void copy(
const storage& source, storage& destination) {
243 destination.dynamic_storage =
reinterpret_cast<dynamic_storage_type
>(
new Callable{*
reinterpret_cast<Callable*
>(source.dynamic_storage)});
246 static void move(storage& source, storage& destination) {
247 destination.dynamic_storage = source.dynamic_storage;
248 source.dynamic_storage =
nullptr;
251 static void destroy(storage& storage) {
252 delete reinterpret_cast<Callable*
>(storage.dynamic_storage);
256 template<
typename Callable>
257 static vtable* _create_vtable() {
258 using vtable_type = std::conditional_t<requires_dynamic_allocation_v<Callable>, dynamic_vtable<Callable>, static_vtable<Callable>>;
260 static auto instance = vtable{
270 template<
typename Callable>
271 storage _create_storage(Callable&& callable) {
272 if constexpr (requires_dynamic_allocation_v<Callable>) {
274 return storage{ .dynamic_storage =
reinterpret_cast<dynamic_storage_type
>(
new Callable{std::forward<Callable>(callable)}) };
276 auto static_storage = static_storage_type{};
277 std::construct_at(
reinterpret_cast<Callable*
>(&static_storage), std::forward<Callable>(callable));
278 return storage{ .static_storage = static_storage };
283 mutable storage _storage{};
delegate(Callable &&callable)
Construct a delegate from a functor type.
Definition: delegate.hpp:98
delegate() noexcept
Default constructor.
Definition: delegate.hpp:80
delegate(std::nullptr_t) noexcept
Constructs a delegate from a nullptr.
Definition: delegate.hpp:86
Definition: delegate.hpp:56
Describes a type or object that can be invoked with the give parameters and return the given type.
Definition: concepts.hpp:18
Exception type that is thrown when a delegate that does not hold a handle is invoked.
Definition: delegate.hpp:50