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
168 template <typename Lambda>
169 void bind(Lambda&& lambda)
170 {
171 executeOperation(FunctionErasedOperation::Destruct, nullptr);
172 functionStub = nullptr;
173 functionOperation = nullptr;
174
175 new (&classInstance, PlacementNew()) Lambda(forward<Lambda>(lambda));
176 static_assert(sizeof(Lambda) <= sizeof(lambdaMemory), "Lambda is too big");
177 functionStub = [](const void* const* p, typename TypeTraits::AddPointer<Args>::type... args) -> R
178 {
179 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
180 return lambda(*args...);
181 };
182 functionOperation = [](FunctionErasedOperation operation, const void** other, const void* const* p)
183 {
184 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
185 if (operation == FunctionErasedOperation::Destruct)
186 lambda.~Lambda();
187 else if (operation == FunctionErasedOperation::CopyConstruct)
188 new (other, PlacementNew()) Lambda(lambda);
189 else if (operation == FunctionErasedOperation::MoveConstruct)
190 new (other, PlacementNew()) Lambda(move(lambda));
191 else
192#if SC_COMPILER_MSVC
193 __assume(false);
194#else
195 __builtin_unreachable();
196#endif
197 };
198 }
199
202 template <R (*FreeFunction)(Args...)>
203 void bind()
204 {
205 executeOperation(FunctionErasedOperation::Destruct, nullptr);
206 classInstance = nullptr;
207 functionStub = &FunctionWrapper<FreeFunction>;
208 functionOperation = &FunctionOperation;
209 }
210
215 template <typename Class, R (Class::*MemberFunction)(Args...) const>
216 void bind(const Class& c)
217 {
218 executeOperation(FunctionErasedOperation::Destruct, nullptr);
219 classInstance = &c;
220 functionStub = &MemberWrapper<Class, MemberFunction>;
221 functionOperation = &MemberOperation;
222 }
223
228 template <typename Class, R (Class::*MemberFunction)(Args...)>
229 void bind(Class& c)
230 {
231 executeOperation(FunctionErasedOperation::Destruct, nullptr);
232 classInstance = &c;
233 functionStub = &MemberWrapper<Class, MemberFunction>;
234 functionOperation = &MemberOperation;
235 }
236
242 template <typename Class, R (Class::*MemberFunction)(Args...)>
243 static Function fromMember(Class& c)
244 {
245 return Function(&c, &MemberWrapper<Class, MemberFunction>, &MemberOperation);
246 }
247
253 template <typename Class, R (Class::*MemberFunction)(Args...) const>
254 static Function fromMember(const Class& c)
255 {
256 return Function(&c, &MemberWrapper<Class, MemberFunction>, &MemberOperation);
257 }
258
261 [[nodiscard]] R operator()(Args... args) const { return (*functionStub)(&classInstance, &args...); }
262
263 private:
264 static void MemberOperation(FunctionErasedOperation operation, const void** other, const void* const* p)
265 {
266 if (operation == FunctionErasedOperation::CopyConstruct or operation == FunctionErasedOperation::MoveConstruct)
267 *other = *p;
268 }
269
270 template <typename Class, R (Class::*MemberFunction)(Args...)>
271 static R MemberWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
272 {
273 Class* cls = const_cast<Class*>(static_cast<const Class*>(*p));
274 return (cls->*MemberFunction)(*args...);
275 }
276
277 template <typename Class, R (Class::*MemberFunction)(Args...) const>
278 static R MemberWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
279 {
280 const Class* cls = static_cast<const Class*>(*p);
281 return (cls->*MemberFunction)(*args...);
282 }
283
284 template <R (*FreeFunction)(Args...)>
285 static R FunctionWrapper(const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args)
286 {
288 return FreeFunction(*args...);
289 }
290 static void FunctionOperation(FunctionErasedOperation, const void**, const void* const*) {}
291};
292
293template <typename T>
294using Delegate = Function<void(T)>;
295using Action = Function<void()>;
297} // 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