33 #ifndef WPIUTIL_WPI_FUNCTION_EXTRAS_H
34 #define WPIUTIL_WPI_FUNCTION_EXTRAS_H
36 #include "wpi/Compiler.h"
37 #include "wpi/PointerIntPair.h"
38 #include "wpi/PointerUnion.h"
46 #if defined(__GNUC__) && !defined(__clang__)
47 #pragma GCC diagnostic push
48 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
51 template <
typename ReturnT,
typename... ParamTs>
53 static constexpr
size_t InlineStorageSize =
sizeof(
void *) * 4;
58 template <
typename T>
struct IsSizeLessThanThresholdT {
59 static constexpr
bool value =
sizeof(T) <= (2 *
sizeof(
void *));
72 using AdjustedParamT =
typename std::conditional<
73 !std::is_reference<T>::value &&
74 std::is_trivially_copy_constructible<T>::value &&
75 std::is_trivially_move_constructible<T>::value &&
76 IsSizeLessThanThresholdT<T>::value,
81 using CallPtrT = ReturnT (*)(
void *CallableAddr,
82 AdjustedParamT<ParamTs>... Params);
83 using MovePtrT = void (*)(
void *LHSCallableAddr,
void *RHSCallableAddr);
84 using DestroyPtrT = void (*)(
void *CallableAddr);
88 struct alignas(8) TrivialCallback {
94 struct alignas(8) NonTrivialCallbacks {
97 DestroyPtrT DestroyPtr;
108 union StorageUnionT {
111 struct OutOfLineStorageT {
117 sizeof(OutOfLineStorageT) <= InlineStorageSize,
118 "Should always use all of the out-of-line storage for inline storage!");
122 typename std::aligned_storage<InlineStorageSize,
alignof(
void *)>::type
131 bool isInlineStorage()
const {
return CallbackAndInlineFlag.getInt(); }
133 bool isTrivialCallback()
const {
134 return CallbackAndInlineFlag.getPointer().template is<TrivialCallback *>();
137 CallPtrT getTrivialCallback()
const {
138 return CallbackAndInlineFlag.getPointer().template get<TrivialCallback *>()->CallPtr;
141 NonTrivialCallbacks *getNonTrivialCallbacks()
const {
142 return CallbackAndInlineFlag.getPointer()
143 .template get<NonTrivialCallbacks *>();
146 void *getInlineStorage() {
return &StorageUnion.InlineStorage; }
148 void *getOutOfLineStorage() {
149 return StorageUnion.OutOfLineStorage.StoragePtr;
151 size_t getOutOfLineStorageSize()
const {
152 return StorageUnion.OutOfLineStorage.Size;
154 size_t getOutOfLineStorageAlignment()
const {
155 return StorageUnion.OutOfLineStorage.Alignment;
158 void setOutOfLineStorage(
void *Ptr,
size_t Size,
size_t Alignment) {
159 StorageUnion.OutOfLineStorage = {Ptr, Size, Alignment};
162 template <
typename CallableT>
163 static ReturnT CallImpl(
void *CallableAddr, AdjustedParamT<ParamTs>... Params) {
164 return (*reinterpret_cast<CallableT *>(CallableAddr))(
165 std::forward<ParamTs>(Params)...);
168 template <
typename CallableT>
169 static void MoveImpl(
void *LHSCallableAddr,
void *RHSCallableAddr) noexcept {
170 new (LHSCallableAddr)
171 CallableT(std::move(*reinterpret_cast<CallableT *>(RHSCallableAddr)));
174 template <
typename CallableT>
175 static void DestroyImpl(
void *CallableAddr) noexcept {
176 reinterpret_cast<CallableT *>(CallableAddr)->~CallableT();
180 unique_function() =
default;
181 unique_function(std::nullptr_t ) {}
184 if (!CallbackAndInlineFlag.getPointer())
188 bool IsInlineStorage = isInlineStorage();
190 if (!isTrivialCallback())
191 getNonTrivialCallbacks()->DestroyPtr(
192 IsInlineStorage ? getInlineStorage() : getOutOfLineStorage());
194 if (!IsInlineStorage)
196 getOutOfLineStorageAlignment());
199 unique_function(unique_function &&RHS) noexcept {
201 CallbackAndInlineFlag = RHS.CallbackAndInlineFlag;
207 if (!isInlineStorage()) {
209 StorageUnion.OutOfLineStorage = RHS.StorageUnion.OutOfLineStorage;
210 }
else if (isTrivialCallback()) {
212 memcpy(getInlineStorage(), RHS.getInlineStorage(), InlineStorageSize);
215 getNonTrivialCallbacks()->MovePtr(getInlineStorage(),
216 RHS.getInlineStorage());
220 RHS.CallbackAndInlineFlag = {};
224 memset(RHS.getInlineStorage(), 0xAD, InlineStorageSize);
228 unique_function &operator=(unique_function &&RHS) noexcept {
235 this->~unique_function();
236 new (
this) unique_function(std::move(RHS));
240 template <
typename CallableT> unique_function(CallableT Callable) {
241 bool IsInlineStorage =
true;
242 void *CallableAddr = getInlineStorage();
243 if (
sizeof(CallableT) > InlineStorageSize ||
244 alignof(CallableT) >
alignof(decltype(StorageUnion.InlineStorage))) {
245 IsInlineStorage =
false;
248 auto Size =
sizeof(CallableT);
249 auto Alignment =
alignof(CallableT);
251 setOutOfLineStorage(CallableAddr, Size, Alignment);
255 new (CallableAddr) CallableT(std::move(Callable));
264 if (std::is_trivially_move_constructible<CallableT>::value &&
265 std::is_trivially_destructible<CallableT>::value) {
268 static TrivialCallback Callback = { &CallImpl<CallableT> };
270 CallbackAndInlineFlag = {&Callback, IsInlineStorage};
277 static NonTrivialCallbacks Callbacks = {
278 &CallImpl<CallableT>, &MoveImpl<CallableT>, &DestroyImpl<CallableT>};
280 CallbackAndInlineFlag = {&Callbacks, IsInlineStorage};
283 ReturnT operator()(ParamTs... Params) {
285 isInlineStorage() ? getInlineStorage() : getOutOfLineStorage();
287 return (isTrivialCallback()
288 ? getTrivialCallback()
289 : getNonTrivialCallbacks()->CallPtr)(CallableAddr, Params...);
292 explicit operator bool()
const {
293 return (
bool)CallbackAndInlineFlag.getPointer();
297 #if defined(__GNUC__) && !defined(__clang__)
298 #pragma GCC diagnostic pop
303 #endif // WPIUTIL_WPI_FUNCTION_H