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 [[nodiscard]] bool isValid() const { return functionStub != nullptr; }
164
166 [[nodiscard]] bool isBoundToClassInstance(void* instance) const { return classInstance == instance; }
167
168 bool operator==(const Function& other) const
169 {
170 return functionStub == other.functionStub and functionOperation == other.functionOperation and
171 classInstance == other.classInstance;
172 }
176 template <typename Lambda>
177 void bind(Lambda&& lambda)
178 {
179 executeOperation(FunctionErasedOperation::Destruct, nullptr);
180 functionStub = nullptr;
181 functionOperation = nullptr;
182
183 new (&classInstance, PlacementNew()) Lambda(forward<Lambda>(lambda));
184 static_assert(sizeof(Lambda) <= sizeof(lambdaMemory), "Lambda is too big");
185 functionStub = [](const void* const* p, typename TypeTraits::AddPointer<Args>::type... args) -> R
186 {
187 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
188 return lambda(*args...);
189 };
190 functionOperation = [](FunctionErasedOperation operation, const void** other, const void* const* p)
191 {
192 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
193 if (operation == FunctionErasedOperation::Destruct)
194 lambda.~Lambda();
195 else if (operation == FunctionErasedOperation::CopyConstruct)
196 new (other, PlacementNew()) Lambda(lambda);
197 else if (operation == FunctionErasedOperation::MoveConstruct)
198 new (other, PlacementNew()) Lambda(move(lambda));
199 else
200#if SC_COMPILER_MSVC
201 __assume(false);
202#else
203 __builtin_unreachable();
204#endif
205 };
206 }
207
210 template <R (*FreeFunction)(Args...)>
211 void bind()
212 {
213 executeOperation(FunctionErasedOperation::Destruct, nullptr);
214 classInstance = nullptr;
215 functionStub = &FunctionWrapper<FreeFunction>;
216 functionOperation = &FunctionOperation;
217 }
218
223 template <typename Class, R (Class::*MemberFunction)(Args...) const>
224 void bind(const Class& c)
225 {
226 executeOperation(FunctionErasedOperation::Destruct, nullptr);
227 classInstance = &c;
228 functionStub = &MemberWrapper<Class, MemberFunction>;
229 functionOperation = &MemberOperation;
230 }
231
236 template <typename Class, R (Class::*MemberFunction)(Args...)>
237 void bind(Class& c)
238 {
239 executeOperation(FunctionErasedOperation::Destruct, nullptr);
240 classInstance = &c;
241 functionStub = &MemberWrapper<Class, MemberFunction>;
242 functionOperation = &MemberOperation;
243 }
244
250 template <typename Class, R (Class::*MemberFunction)(Args...)>
251 static Function fromMember(Class& c)
252 {
253 return Function(&c, &MemberWrapper<Class, MemberFunction>, &MemberOperation);
254 }
255
261 template <typename Class, R (Class::*MemberFunction)(Args...) const>
262 static Function fromMember(const Class& c)
263 {
264 return Function(&c, &MemberWrapper<Class, MemberFunction>, &MemberOperation);
265 }
266
269 [[nodiscard]] R operator()(Args... args) const { return (*functionStub)(&classInstance, &args...); }
270
271 private:
272 static void MemberOperation(FunctionErasedOperation operation, const void** other, const void* const* p)
273 {
274 if (operation == FunctionErasedOperation::CopyConstruct or operation == FunctionErasedOperation::MoveConstruct)
275 *other = *p;
276 }
277
278 template <typename Class, R (Class::*MemberFunction)(Args...)>
279 static R MemberWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
280 {
281 Class* cls = const_cast<Class*>(static_cast<const Class*>(*p));
282 return (cls->*MemberFunction)(*args...);
283 }
284
285 template <typename Class, R (Class::*MemberFunction)(Args...) const>
286 static R MemberWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
287 {
288 const Class* cls = static_cast<const Class*>(*p);
289 return (cls->*MemberFunction)(*args...);
290 }
291
292 template <R (*FreeFunction)(Args...)>
293 static R FunctionWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
294 {
296 return FreeFunction(*args...);
297 }
298 static void FunctionOperation(FunctionErasedOperation, const void**, const void* const*) {}
299};
300
301template <typename T>
302using Delegate = Function<void(T)>;
303using Action = Function<void()>;
305} // 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