Sane C++ Libraries
C++ Platform Abstraction Libraries
Loading...
Searching...
No Matches
Function.h
1// Copyright (c) Stefano Cristiano
2// SPDX-License-Identifier: MIT
3#pragma once
4#include "../Foundation/TypeTraits.h" // RemoveReference, AddPointer, IsSame
5
6namespace SC
7{
10
18template <typename FuncType, int LAMBDA_SIZE = sizeof(void*) * 2>
19struct Function;
20
21template <int LAMBDA_SIZE, typename R, typename... Args>
22struct Function<R(Args...), LAMBDA_SIZE>
23{
24 private:
25 enum class Operation
26 {
27 Destruct,
28 CopyConstruct,
29 MoveConstruct
30 };
31 using ExecuteFunction = R (*)(const void* const*, typename TypeTraits::AddPointer<Args>::type...);
32 using OperationFunction = void (*)(Operation operation, const void** other, const void* const*);
33
34 struct VTable
35 {
36 ExecuteFunction execute;
37 OperationFunction operation;
38 };
39
40 const VTable* vtable;
41
42 union
43 {
44 const void* classInstance;
45 char lambdaMemory[LAMBDA_SIZE] = {0};
46 };
47
48 void checkedOperation(Operation operation, const void** other) const
49 {
50 if (vtable)
51 vtable->operation(operation, other, &classInstance);
52 }
53
54 public:
56 Function()
57 {
58 static_assert(sizeof(Function) == sizeof(void*) + LAMBDA_SIZE, "Function Size");
59 vtable = nullptr;
60 }
61
65 template <
66 typename Lambda,
67 typename = typename TypeTraits::EnableIf<
68 not TypeTraits::IsSame<typename TypeTraits::RemoveReference<Lambda>::type, Function>::value, void>::type>
69 Function(Lambda&& lambda)
70 {
71 vtable = nullptr;
72 bind(forward<typename TypeTraits::RemoveReference<Lambda>::type>(lambda));
73 }
74
76 ~Function() { checkedOperation(Operation::Destruct, nullptr); }
77
80 Function(Function&& other)
81 {
82 vtable = other.vtable;
83 classInstance = other.classInstance;
84 other.checkedOperation(Operation::MoveConstruct, &classInstance);
85 other.checkedOperation(Operation::Destruct, nullptr);
86 other.vtable = nullptr;
87 }
88
91 Function(const Function& other)
92 {
93 vtable = other.vtable;
94 other.checkedOperation(Operation::CopyConstruct, &classInstance);
95 }
96
99 Function& operator=(const Function& other)
100 {
101 checkedOperation(Operation::Destruct, nullptr);
102 vtable = other.vtable;
103 other.checkedOperation(Operation::CopyConstruct, &classInstance);
104 return *this;
105 }
106
109 Function& operator=(Function&& other) noexcept
110 {
111 checkedOperation(Operation::Destruct, nullptr);
112 vtable = other.vtable;
113 other.checkedOperation(Operation::MoveConstruct, &classInstance);
114 other.checkedOperation(Operation::Destruct, nullptr);
115 other.vtable = nullptr;
116 return *this;
117 }
118
121 [[nodiscard]] bool isValid() const { return vtable != nullptr; }
122
124 [[nodiscard]] bool isBoundToClassInstance(void* instance) const { return classInstance == instance; }
125
126 bool operator==(const Function& other) const
127 {
128 return vtable == other.vtable and classInstance == other.classInstance;
129 }
130
134 template <typename Lambda>
135 void bind(Lambda&& lambda)
136 {
137 checkedOperation(Operation::Destruct, nullptr);
138 vtable = nullptr;
139 new (&classInstance, PlacementNew()) Lambda(forward<Lambda>(lambda));
140 vtable = getVTableForLambda<Lambda>();
141 }
142
143 private:
144 template <typename Lambda>
145 static auto getVTableForLambda()
146 {
147 static_assert(sizeof(Lambda) <= sizeof(lambdaMemory), "Lambda is too big");
148 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
149 [](const void* const* p, typename TypeTraits::AddPointer<Args>::type... args) SC_LANGUAGE_IF_CONSTEXPR
150 {
151 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
152 return lambda(*args...);
153 },
154 [](Operation operation, const void** other, const void* const* p) SC_LANGUAGE_IF_CONSTEXPR
155 {
156 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
157 if (operation == Operation::Destruct)
158 lambda.~Lambda();
159 else if (operation == Operation::CopyConstruct)
160 new (other, PlacementNew()) Lambda(lambda);
161 else if (operation == Operation::MoveConstruct)
162 new (other, PlacementNew()) Lambda(move(lambda));
163 }};
164 return &staticVTable;
165 }
166
167 public:
172 template <typename Lambda>
173 Lambda* dynamicCastTo() const
174 {
175 if (getVTableForLambda<Lambda>() != vtable)
176 return nullptr;
177 else
178 return &const_cast<Lambda&>(reinterpret_cast<const Lambda&>(classInstance));
179 }
180
183 template <R (*FreeFunction)(Args...)>
184 void bind()
185 {
186 checkedOperation(Operation::Destruct, nullptr);
187 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
188 [](const void* const*, typename TypeTraits::RemoveReference<Args>::type*... args) SC_LANGUAGE_IF_CONSTEXPR
189 { return FreeFunction(*args...); },
190 [](Operation, const void**, const void* const*) SC_LANGUAGE_IF_CONSTEXPR {}};
191 vtable = &staticVTable;
192 classInstance = nullptr;
193 }
194
199 template <typename Class, R (Class::*MemberFunction)(Args...) const>
200 void bind(const Class& c)
201 {
202 checkedOperation(Operation::Destruct, nullptr);
203 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
204 [](const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args) SC_LANGUAGE_IF_CONSTEXPR
205 {
206 const Class* cls = static_cast<const Class*>(*p);
207 return (cls->*MemberFunction)(*args...);
208 },
209 [](Operation operation, const void** other, const void* const* p) SC_LANGUAGE_IF_CONSTEXPR
210 {
211 if (operation != Operation::Destruct)
212 *other = *p;
213 }};
214 vtable = &staticVTable;
215 classInstance = &c;
216 }
217
222 template <typename Class, R (Class::*MemberFunction)(Args...)>
223 void bind(Class& c)
224 {
225 checkedOperation(Operation::Destruct, nullptr);
226 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
227 [](const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args) SC_LANGUAGE_IF_CONSTEXPR
228 {
229 Class* cls = const_cast<Class*>(static_cast<const Class*>(*p));
230 return (cls->*MemberFunction)(*args...);
231 },
232 [](Operation operation, const void** other, const void* const* p) SC_LANGUAGE_IF_CONSTEXPR
233 {
234 if (operation != Operation::Destruct)
235 *other = *p;
236 }};
237 vtable = &staticVTable;
238 classInstance = &c;
239 }
240
243 [[nodiscard]] R operator()(Args... args) const { return vtable->execute(&classInstance, &args...); }
244};
245
246template <typename T>
247using Delegate = Function<void(T)>;
248using Action = Function<void()>;
250} // namespace SC
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition Compiler.h:264
constexpr T && forward(typename TypeTraits::RemoveReference< T >::type &value)
Forwards an lvalue or an rvalue as an rvalue reference.
Definition Compiler.h:267
Wraps function pointers, member functions and lambdas without ever allocating.
Definition Function.h:19