Sane C++ Libraries
C++ Platform Abstraction Libraries
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
14
49template <typename FuncType>
50struct Function;
51
52template <typename R, typename... Args>
53struct Function<R(Args...)>
54{
55 private:
56 enum class FunctionErasedOperation
57 {
58 Destruct,
59 CopyConstruct,
60 MoveConstruct
61 };
62 using StubFunction = R (*)(const void* const*, typename TypeTraits::AddPointer<Args>::type...);
63 using OperationFunction = void (*)(FunctionErasedOperation operation, const void** other, const void* const*);
64
65 static const int LAMBDA_SIZE = sizeof(void*) * 2;
66
67 StubFunction functionStub;
68 OperationFunction functionOperation;
69
70 union
71 {
72 const void* classInstance;
73 char lambdaMemory[LAMBDA_SIZE] = {0};
74 };
75
76 void executeOperation(FunctionErasedOperation operation, const void** other) const
77 {
78 if (functionOperation)
79 (*functionOperation)(operation, other, &classInstance);
80 }
81
82 Function(const void* instance, StubFunction stub, OperationFunction operation)
83 : functionStub(stub), functionOperation(operation)
84 {
85 classInstance = instance;
86 }
87 using FreeFunction = R (*)(Args...);
88
89 public:
91 Function()
92 {
93 static_assert(sizeof(Function) == sizeof(void*) * 4, "Function Size");
94 functionStub = nullptr;
95 functionOperation = nullptr;
96 }
97
101 template <
102 typename Lambda,
103 typename = typename TypeTraits::EnableIf<
104 not TypeTraits::IsSame<typename TypeTraits::RemoveReference<Lambda>::type, Function>::value, void>::type>
105 Function(Lambda&& lambda)
106 {
107 functionStub = nullptr;
108 functionOperation = nullptr;
109 bind(forward<typename TypeTraits::RemoveReference<Lambda>::type>(lambda));
110 }
111
113 ~Function() { executeOperation(FunctionErasedOperation::Destruct, nullptr); }
114
117 Function(Function&& other)
118 {
119 functionStub = other.functionStub;
120 functionOperation = other.functionOperation;
121 classInstance = other.classInstance;
122 other.executeOperation(FunctionErasedOperation::MoveConstruct, &classInstance);
123 other.executeOperation(FunctionErasedOperation::Destruct, nullptr);
124 other.functionStub = nullptr;
125 other.functionOperation = nullptr;
126 }
127
130 Function(const Function& other)
131 {
132 functionStub = other.functionStub;
133 functionOperation = other.functionOperation;
134 other.executeOperation(FunctionErasedOperation::CopyConstruct, &classInstance);
135 }
136
139 Function& operator=(const Function& other)
140 {
141 executeOperation(FunctionErasedOperation::Destruct, nullptr);
142 functionStub = other.functionStub;
143 functionOperation = other.functionOperation;
144 other.executeOperation(FunctionErasedOperation::CopyConstruct, &classInstance);
145 return *this;
146 }
147
150 Function& operator=(Function&& other) noexcept
151 {
152 executeOperation(FunctionErasedOperation::Destruct, nullptr);
153 functionStub = other.functionStub;
154 functionOperation = other.functionOperation;
155 other.executeOperation(FunctionErasedOperation::MoveConstruct, &classInstance);
156 other.executeOperation(FunctionErasedOperation::Destruct, nullptr);
157 other.functionStub = nullptr;
158 return *this;
159 }
160
163 bool isValid() const { return functionStub != nullptr; }
164
165 bool operator==(const Function& other) const
166 {
167 return functionStub == other.functionStub and functionOperation == other.functionOperation and
168 classInstance == other.classInstance;
169 }
173 template <typename Lambda>
174 void bind(Lambda&& lambda)
175 {
176 executeOperation(FunctionErasedOperation::Destruct, nullptr);
177 functionStub = nullptr;
178 functionOperation = nullptr;
179
180 new (&classInstance, PlacementNew()) Lambda(forward<Lambda>(lambda));
181 static_assert(sizeof(Lambda) <= sizeof(lambdaMemory), "Lambda is too big");
182 functionStub = [](const void* const* p, typename TypeTraits::AddPointer<Args>::type... args) -> R
183 {
184 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
185 return lambda(*args...);
186 };
187 functionOperation = [](FunctionErasedOperation operation, const void** other, const void* const* p)
188 {
189 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
190 if (operation == FunctionErasedOperation::Destruct)
191 lambda.~Lambda();
192 else if (operation == FunctionErasedOperation::CopyConstruct)
193 new (other, PlacementNew()) Lambda(lambda);
194 else if (operation == FunctionErasedOperation::MoveConstruct)
195 new (other, PlacementNew()) Lambda(move(lambda));
196 else
197#if SC_COMPILER_MSVC
198 __assume(false);
199#else
200 __builtin_unreachable();
201#endif
202 };
203 }
204
207 template <R (*FreeFunction)(Args...)>
208 void bind()
209 {
210 executeOperation(FunctionErasedOperation::Destruct, nullptr);
211 classInstance = nullptr;
212 functionStub = &FunctionWrapper<FreeFunction>;
213 functionOperation = &FunctionOperation;
214 }
215
220 template <typename Class, R (Class::*MemberFunction)(Args...) const>
221 void bind(const Class& c)
222 {
223 executeOperation(FunctionErasedOperation::Destruct, nullptr);
224 classInstance = &c;
225 functionStub = &MemberWrapper<Class, MemberFunction>;
226 functionOperation = &MemberOperation;
227 }
228
233 template <typename Class, R (Class::*MemberFunction)(Args...)>
234 void bind(Class& c)
235 {
236 executeOperation(FunctionErasedOperation::Destruct, nullptr);
237 classInstance = &c;
238 functionStub = &MemberWrapper<Class, MemberFunction>;
239 functionOperation = &MemberOperation;
240 }
241
247 template <typename Class, R (Class::*MemberFunction)(Args...)>
248 static Function fromMember(Class& c)
249 {
250 return Function(&c, &MemberWrapper<Class, MemberFunction>, &MemberOperation);
251 }
252
258 template <typename Class, R (Class::*MemberFunction)(Args...) const>
259 static Function fromMember(const Class& c)
260 {
261 return Function(&c, &MemberWrapper<Class, MemberFunction>, &MemberOperation);
262 }
263
266 [[nodiscard]] R operator()(Args... args) const { return (*functionStub)(&classInstance, &args...); }
267
268 private:
269 static void MemberOperation(FunctionErasedOperation operation, const void** other, const void* const* p)
270 {
271 if (operation == FunctionErasedOperation::CopyConstruct or operation == FunctionErasedOperation::MoveConstruct)
272 *other = *p;
273 }
274
275 template <typename Class, R (Class::*MemberFunction)(Args...)>
276 static R MemberWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
277 {
278 Class* cls = const_cast<Class*>(static_cast<const Class*>(*p));
279 return (cls->*MemberFunction)(*args...);
280 }
281
282 template <typename Class, R (Class::*MemberFunction)(Args...) const>
283 static R MemberWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
284 {
285 const Class* cls = static_cast<const Class*>(*p);
286 return (cls->*MemberFunction)(*args...);
287 }
288
289 template <R (*FreeFunction)(Args...)>
290 static R FunctionWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
291 {
293 return FreeFunction(*args...);
294 }
295 static void FunctionOperation(FunctionErasedOperation, const void**, const void* const*) {}
296};
297
298template <typename T>
299using Delegate = Function<void(T)>;
300using Action = Function<void()>;
302} // namespace SC
#define SC_COMPILER_UNUSED(param)
Silence an unused variable or unused parameter warning.
Definition: Compiler.h:139
constexpr T && move(T &value)
Converts an lvalue to an rvalue reference.
Definition: Compiler.h:269
constexpr T && forward(typename TypeTraits::RemoveReference< T >::type &value)
Forwards an lvalue or an rvalue as an rvalue reference.
Definition: Compiler.h:272
Wraps function pointers, member functions and lambdas without ever allocating.
Definition: Function.h:50