Function.h 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. // Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #pragma once
  6. #include <AnKi/Util/Allocator.h>
  7. #include <AnKi/Util/Forward.h>
  8. #include <AnKi/Util/Array.h>
  9. namespace anki {
  10. /// @addtogroup util_containers
  11. /// @{
  12. /// This is almost like a re-implementation of std::function. The difference is that it has some functionality to avoid
  13. /// allocations. Can be used like:
  14. /// @code
  15. /// Function<Error(U32, F32), 16> func;
  16. /// func.init(allocator, [&someInt](U32 u, F32 f) {someInt = xxx + u + f; return Error::NONE;});
  17. /// func.call(10, 1.2f);
  18. /// @endcode
  19. /// @tparam T_INLINE_STORAGE_SIZE Optional inline storage to avoid deallocations (small object optimization)
  20. template<typename TReturn, typename... TArgs, PtrSize T_INLINE_STORAGE_SIZE>
  21. class Function<TReturn(TArgs...), T_INLINE_STORAGE_SIZE>
  22. {
  23. public:
  24. Function() = default;
  25. /// Move.
  26. Function(Function&& b)
  27. {
  28. *this = std::move(b);
  29. }
  30. // Non-copyable.
  31. Function(const Function&) = delete;
  32. /// Same as init().
  33. template<typename TAlloc, typename T>
  34. Function(TAlloc alloc, const T& func)
  35. {
  36. init(alloc, func);
  37. }
  38. // Does nothing important.
  39. ~Function()
  40. {
  41. ANKI_ASSERT(getState() == STATE_UNINITIALIZED && "Forgot to call destroy()");
  42. }
  43. // Non-copyable.
  44. Function& operator=(const Function&) = delete;
  45. /// Move.
  46. Function& operator=(Function&& b)
  47. {
  48. ANKI_ASSERT(getState() == STATE_UNINITIALIZED);
  49. m_state = b.m_state;
  50. b.m_state = STATE_UNINITIALIZED;
  51. memcpy(&m_callableInlineStorage[0], &b.m_callableInlineStorage[0], sizeof(m_callableInlineStorage));
  52. return *this;
  53. }
  54. /// Initialize the function.
  55. /// @param alloc The allocator (it might be used).
  56. /// @param func The lambda.
  57. template<typename TAlloc, typename T>
  58. void init(TAlloc alloc, const T& func)
  59. {
  60. ANKI_ASSERT(getState() == STATE_UNINITIALIZED);
  61. // Init storage
  62. constexpr Bool useInlineStorage = sizeof(T) <= INLINE_STORAGE_SIZE && std::is_trivially_copyable<T>::value
  63. && std::is_trivially_destructible<T>::value;
  64. if(useInlineStorage)
  65. {
  66. setState(STATE_INLINE_STORAGE);
  67. memcpy(&m_callableInlineStorage[0], &func, sizeof(func));
  68. setFunctionCallback([](const Function& self, TArgs... args) -> TReturn {
  69. // Yes I know, a const_cast hack follows. If the T was in some pointer then all would be fine. Look
  70. // at the setFunctionCallback() of the STATE_ALLOCATED. Only a static_cast there. It's unfair.
  71. const T* t0 = reinterpret_cast<const T*>(&self.m_callableInlineStorage[0]);
  72. T* t1 = const_cast<T*>(t0);
  73. return (*t1)(args...);
  74. });
  75. }
  76. else
  77. {
  78. setState(STATE_ALLOCATED);
  79. using CallableT = Callable<T>;
  80. CallableT* callable = alloc.template newInstance<CallableT>(func);
  81. m_callablePtr = callable;
  82. callable->m_size = sizeof(CallableT);
  83. callable->m_alignment = alignof(CallableT);
  84. setFunctionCallback([](const Function& self, TArgs... args) -> TReturn {
  85. return static_cast<CallableT*>(self.m_callablePtr)->m_func(args...);
  86. });
  87. callable->m_destroyCallback = [](CallableBase& c) {
  88. static_cast<CallableT&>(c).~CallableT();
  89. };
  90. callable->m_copyCallback = [](const CallableBase& otherC, CallableBase& c) {
  91. ::new(&static_cast<CallableT&>(c)) CallableT(static_cast<const CallableT&>(otherC));
  92. };
  93. }
  94. }
  95. /// Destroy the object.
  96. template<typename TAlloc>
  97. void destroy(TAlloc alloc)
  98. {
  99. if(getState() == STATE_ALLOCATED)
  100. {
  101. ANKI_ASSERT(m_callablePtr && m_callablePtr->m_destroyCallback);
  102. m_callablePtr->m_destroyCallback(*m_callablePtr);
  103. alloc.getMemoryPool().free(m_callablePtr);
  104. }
  105. m_state = STATE_UNINITIALIZED;
  106. }
  107. /// Call the Function with some arguments.
  108. TReturn call(TArgs... args) const
  109. {
  110. return getFunctionCallback()(*this, args...);
  111. }
  112. /// Same as call().
  113. TReturn operator()(TArgs... args) const
  114. {
  115. return getFunctionCallback()(*this, args...);
  116. }
  117. /// Copy from another.
  118. template<typename TAlloc>
  119. Function& copy(const Function& other, TAlloc alloc)
  120. {
  121. ANKI_ASSERT(getState() == STATE_UNINITIALIZED && "Need to destroy it first");
  122. if(other.getState() == STATE_UNINITIALIZED)
  123. {
  124. // Nothing to do
  125. }
  126. else if(other.getState() == STATE_INLINE_STORAGE)
  127. {
  128. // It should be trivially copyable, can use memcpy then
  129. m_state = other.m_state;
  130. memcpy(&m_callableInlineStorage[0], &other.m_callableInlineStorage[0], sizeof(m_callableInlineStorage));
  131. }
  132. else
  133. {
  134. ANKI_ASSERT(other.getState() == STATE_ALLOCATED);
  135. m_state = other.m_state;
  136. // Allocate callable
  137. ANKI_ASSERT(other.m_callablePtr && other.m_callablePtr->m_alignment > 0 && other.m_callablePtr->m_size > 0);
  138. m_callablePtr = static_cast<CallableBase*>(
  139. alloc.getMemoryPool().allocate(other.m_callablePtr->m_size, other.m_callablePtr->m_alignment));
  140. // Copy
  141. other.m_callablePtr->m_copyCallback(*other.m_callablePtr, *m_callablePtr);
  142. }
  143. return *this;
  144. }
  145. private:
  146. class CallableBase;
  147. using FunctionCallback = TReturn (*)(const Function&, TArgs...);
  148. using DestroyCallback = void (*)(CallableBase&);
  149. using CopyCallback = void (*)(const CallableBase&, CallableBase&);
  150. class CallableBase
  151. {
  152. public:
  153. DestroyCallback m_destroyCallback = nullptr;
  154. CopyCallback m_copyCallback = nullptr;
  155. U32 m_size = 0;
  156. U32 m_alignment = 0;
  157. CallableBase() = default;
  158. CallableBase(const CallableBase&) = default;
  159. CallableBase& operator=(const CallableBase&) = delete; // You won't need it
  160. };
  161. template<typename T>
  162. class Callable : public CallableBase
  163. {
  164. public:
  165. T m_func;
  166. Callable(const T& t)
  167. : m_func(t)
  168. {
  169. }
  170. Callable(const Callable& b)
  171. : CallableBase(b)
  172. , m_func(b.m_func)
  173. {
  174. }
  175. Callable& operator=(const Callable&) = delete; // You won't need it
  176. };
  177. static constexpr PtrSize STATE_UNINITIALIZED = PtrSize(0b1001) << PtrSize(60);
  178. static constexpr PtrSize STATE_ALLOCATED = PtrSize(0b1101) << PtrSize(60);
  179. static constexpr PtrSize STATE_INLINE_STORAGE = PtrSize(0b1011) << PtrSize(60);
  180. static constexpr PtrSize STATE_ALL_BITS = PtrSize(0b1111) << PtrSize(60);
  181. static_assert(sizeof(void*) == 8, "Wrong assumption");
  182. static constexpr PtrSize lmax(PtrSize a, PtrSize b)
  183. {
  184. return (a > b) ? a : b;
  185. }
  186. static constexpr PtrSize INLINE_STORAGE_SIZE =
  187. lmax(T_INLINE_STORAGE_SIZE, lmax(ANKI_SAFE_ALIGNMENT, sizeof(void*)));
  188. union
  189. {
  190. CallableBase* m_callablePtr;
  191. alignas(ANKI_SAFE_ALIGNMENT) Array<U8, INLINE_STORAGE_SIZE> m_callableInlineStorage;
  192. };
  193. union
  194. {
  195. // Hide the state in the high bits of the m_functionCallback pointer.
  196. PtrSize m_state = STATE_UNINITIALIZED;
  197. FunctionCallback m_functionCallback;
  198. };
  199. PtrSize getState() const
  200. {
  201. const PtrSize s = m_state & STATE_ALL_BITS;
  202. ANKI_ASSERT(s == STATE_UNINITIALIZED || s == STATE_ALLOCATED || s == STATE_INLINE_STORAGE);
  203. return s;
  204. }
  205. void setState(PtrSize s)
  206. {
  207. ANKI_ASSERT(s == STATE_UNINITIALIZED || s == STATE_ALLOCATED || s == STATE_INLINE_STORAGE);
  208. m_state = (m_state & ~STATE_ALL_BITS) | s;
  209. }
  210. FunctionCallback getFunctionCallback() const
  211. {
  212. return numberToPtr<FunctionCallback>(m_state & ~STATE_ALL_BITS);
  213. }
  214. void setFunctionCallback(FunctionCallback f)
  215. {
  216. m_state = (m_state & STATE_ALL_BITS) | ptrToNumber(f);
  217. ANKI_ASSERT(f == getFunctionCallback());
  218. }
  219. };
  220. /// @}
  221. } // end namespace anki