Function.h 7.2 KB

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