Function.h 6.6 KB

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