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
18template <typename FuncType>
19struct Function;
20
21template <typename R, typename... Args>
22struct Function<R(Args...)>
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 static const int LAMBDA_SIZE = sizeof(void*) * 2;
41
42 const VTable* vtable;
43
44 union
45 {
46 const void* classInstance;
47 char lambdaMemory[LAMBDA_SIZE] = {0};
48 };
49
50 void checkedOperation(Operation operation, const void** other) const
51 {
52 if (vtable)
53 vtable->operation(operation, other, &classInstance);
54 }
55
56 public:
58 Function()
59 {
60 static_assert(sizeof(Function) == sizeof(void*) * 3, "Function Size");
61 vtable = nullptr;
62 }
63
67 template <
68 typename Lambda,
69 typename = typename TypeTraits::EnableIf<
70 not TypeTraits::IsSame<typename TypeTraits::RemoveReference<Lambda>::type, Function>::value, void>::type>
71 Function(Lambda&& lambda)
72 {
73 vtable = nullptr;
74 bind(forward<typename TypeTraits::RemoveReference<Lambda>::type>(lambda));
75 }
76
78 ~Function() { checkedOperation(Operation::Destruct, nullptr); }
79
82 Function(Function&& other)
83 {
84 vtable = other.vtable;
85 classInstance = other.classInstance;
86 other.checkedOperation(Operation::MoveConstruct, &classInstance);
87 other.checkedOperation(Operation::Destruct, nullptr);
88 other.vtable = nullptr;
89 }
90
93 Function(const Function& other)
94 {
95 vtable = other.vtable;
96 other.checkedOperation(Operation::CopyConstruct, &classInstance);
97 }
98
101 Function& operator=(const Function& other)
102 {
103 checkedOperation(Operation::Destruct, nullptr);
104 vtable = other.vtable;
105 other.checkedOperation(Operation::CopyConstruct, &classInstance);
106 return *this;
107 }
108
111 Function& operator=(Function&& other) noexcept
112 {
113 checkedOperation(Operation::Destruct, nullptr);
114 vtable = other.vtable;
115 other.checkedOperation(Operation::MoveConstruct, &classInstance);
116 other.checkedOperation(Operation::Destruct, nullptr);
117 other.vtable = nullptr;
118 return *this;
119 }
120
123 [[nodiscard]] bool isValid() const { return vtable != nullptr; }
124
126 [[nodiscard]] bool isBoundToClassInstance(void* instance) const { return classInstance == instance; }
127
128 bool operator==(const Function& other) const
129 {
130 return vtable == other.vtable and classInstance == other.classInstance;
131 }
132
136 template <typename Lambda>
137 void bind(Lambda&& lambda)
138 {
139 checkedOperation(Operation::Destruct, nullptr);
140 vtable = nullptr;
141 new (&classInstance, PlacementNew()) Lambda(forward<Lambda>(lambda));
142 vtable = getVTableForLambda<Lambda>();
143 }
144
145 private:
146 template <typename Lambda>
147 static auto getVTableForLambda()
148 {
149 static_assert(sizeof(Lambda) <= sizeof(lambdaMemory), "Lambda is too big");
150 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
151 [](const void* const* p, typename TypeTraits::AddPointer<Args>::type... args) SC_LANGUAGE_IF_CONSTEXPR
152 {
153 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
154 return lambda(*args...);
155 },
156 [](Operation operation, const void** other, const void* const* p) SC_LANGUAGE_IF_CONSTEXPR
157 {
158 Lambda& lambda = *reinterpret_cast<Lambda*>(const_cast<void**>(p));
159 if (operation == Operation::Destruct)
160 lambda.~Lambda();
161 else if (operation == Operation::CopyConstruct)
162 new (other, PlacementNew()) Lambda(lambda);
163 else if (operation == Operation::MoveConstruct)
164 new (other, PlacementNew()) Lambda(move(lambda));
165 }};
166 return &staticVTable;
167 }
168
169 public:
174 template <typename Lambda>
175 Lambda* dynamicCastTo() const
176 {
177 if (getVTableForLambda<Lambda>() != vtable)
178 return nullptr;
179 else
180 return &const_cast<Lambda&>(reinterpret_cast<const Lambda&>(classInstance));
181 }
182
185 template <R (*FreeFunction)(Args...)>
186 void bind()
187 {
188 checkedOperation(Operation::Destruct, nullptr);
189 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
190 [](const void* const*, typename TypeTraits::RemoveReference<Args>::type*... args) SC_LANGUAGE_IF_CONSTEXPR
191 { return FreeFunction(*args...); },
192 [](Operation, const void**, const void* const*) SC_LANGUAGE_IF_CONSTEXPR {}};
193 vtable = &staticVTable;
194 classInstance = nullptr;
195 }
196
201 template <typename Class, R (Class::*MemberFunction)(Args...) const>
202 void bind(const Class& c)
203 {
204 checkedOperation(Operation::Destruct, nullptr);
205 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
206 [](const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args) SC_LANGUAGE_IF_CONSTEXPR
207 {
208 const Class* cls = static_cast<const Class*>(*p);
209 return (cls->*MemberFunction)(*args...);
210 },
211 [](Operation operation, const void** other, const void* const* p) SC_LANGUAGE_IF_CONSTEXPR
212 {
213 if (operation != Operation::Destruct)
214 *other = *p;
215 }};
216 vtable = &staticVTable;
217 classInstance = &c;
218 }
219
224 template <typename Class, R (Class::*MemberFunction)(Args...)>
225 void bind(Class& c)
226 {
227 checkedOperation(Operation::Destruct, nullptr);
228 static SC_LANGUAGE_IF_CONSTEXPR const VTable staticVTable = {
229 [](const void* const* p, typename TypeTraits::RemoveReference<Args>::type*... args) SC_LANGUAGE_IF_CONSTEXPR
230 {
231 Class* cls = const_cast<Class*>(static_cast<const Class*>(*p));
232 return (cls->*MemberFunction)(*args...);
233 },
234 [](Operation operation, const void** other, const void* const* p) SC_LANGUAGE_IF_CONSTEXPR
235 {
236 if (operation != Operation::Destruct)
237 *other = *p;
238 }};
239 vtable = &staticVTable;
240 classInstance = &c;
241 }
242
245 [[nodiscard]] R operator()(Args... args) const { return vtable->execute(&classInstance, &args...); }
246};
247
248template <typename T>
249using Delegate = Function<void(T)>;
250using Action = Function<void()>;
252} // namespace SC
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:19