The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

242 lines
7.7KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. namespace dsp
  21. {
  22. #ifndef DOXYGEN
  23. namespace detail
  24. {
  25. template <typename Ret, typename... Args>
  26. struct Vtable
  27. {
  28. using Storage = void*;
  29. using Move = void (*) (Storage, Storage);
  30. using Call = Ret (*) (Storage, Args...);
  31. using Clear = void (*) (Storage);
  32. constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept
  33. : move (moveIn), call (callIn), clear (clearIn) {}
  34. Move move = nullptr;
  35. Call call = nullptr;
  36. Clear clear = nullptr;
  37. };
  38. template <typename Fn>
  39. void move (void* from, void* to)
  40. {
  41. new (to) Fn (std::move (*reinterpret_cast<Fn*> (from)));
  42. }
  43. template <typename Fn, typename Ret, typename... Args>
  44. std::enable_if_t<std::is_same_v<Ret, void>, Ret> call (void* s, Args... args)
  45. {
  46. (*reinterpret_cast<Fn*> (s)) (args...);
  47. }
  48. template <typename Fn, typename Ret, typename... Args>
  49. std::enable_if_t<! std::is_same_v<Ret, void>, Ret> call (void* s, Args... args)
  50. {
  51. return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
  52. }
  53. template <typename Fn>
  54. void clear (void* s)
  55. {
  56. // I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
  57. [[maybe_unused]] auto& fn = *reinterpret_cast<Fn*> (s);
  58. fn.~Fn();
  59. }
  60. template <typename Fn, typename Ret, typename... Args>
  61. constexpr Vtable<Ret, Args...> makeVtable()
  62. {
  63. return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
  64. }
  65. } // namespace detail
  66. template <size_t len, typename T>
  67. class FixedSizeFunction;
  68. #endif
  69. /**
  70. A type similar to `std::function` that holds a callable object.
  71. Unlike `std::function`, the callable object will always be stored in
  72. a buffer of size `len` that is internal to the FixedSizeFunction instance.
  73. This in turn means that creating a FixedSizeFunction instance will never allocate,
  74. making FixedSizeFunctions suitable for use in realtime contexts.
  75. @tags{DSP}
  76. */
  77. template <size_t len, typename Ret, typename... Args>
  78. class FixedSizeFunction<len, Ret (Args...)>
  79. {
  80. private:
  81. using Storage = std::aligned_storage_t<len>;
  82. template <typename Item>
  83. using Decay = std::decay_t<Item>;
  84. template <typename Item, typename Fn = Decay<Item>>
  85. using IntIfValidConversion = std::enable_if_t<sizeof (Fn) <= len
  86. && alignof (Fn) <= alignof (Storage)
  87. && ! std::is_same_v<FixedSizeFunction, Fn>,
  88. int>;
  89. public:
  90. /** Create an empty function. */
  91. FixedSizeFunction() noexcept = default;
  92. /** Create an empty function. */
  93. FixedSizeFunction (std::nullptr_t) noexcept
  94. : FixedSizeFunction() {}
  95. FixedSizeFunction (const FixedSizeFunction&) = delete;
  96. /** Forwards the passed Callable into the internal storage buffer. */
  97. template <typename Callable,
  98. typename Fn = Decay<Callable>,
  99. IntIfValidConversion<Callable> = 0>
  100. FixedSizeFunction (Callable&& callable)
  101. {
  102. static_assert (sizeof (Fn) <= len,
  103. "The requested function cannot fit in this FixedSizeFunction");
  104. static_assert (alignof (Fn) <= alignof (Storage),
  105. "FixedSizeFunction cannot accommodate the requested alignment requirements");
  106. static constexpr auto vtableForCallable = detail::makeVtable<Fn, Ret, Args...>();
  107. vtable = &vtableForCallable;
  108. auto* ptr = new (&storage) Fn (std::forward<Callable> (callable));
  109. jassertquiet ((void*) ptr == (void*) &storage);
  110. }
  111. /** Move constructor. */
  112. FixedSizeFunction (FixedSizeFunction&& other) noexcept
  113. : vtable (other.vtable)
  114. {
  115. move (std::move (other));
  116. }
  117. /** Converting constructor from smaller FixedSizeFunctions. */
  118. template <size_t otherLen, std::enable_if_t<(otherLen < len), int> = 0>
  119. FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
  120. : vtable (other.vtable)
  121. {
  122. move (std::move (other));
  123. }
  124. /** Nulls this instance. */
  125. FixedSizeFunction& operator= (std::nullptr_t) noexcept
  126. {
  127. return *this = FixedSizeFunction();
  128. }
  129. FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
  130. /** Assigns a new callable to this instance. */
  131. template <typename Callable, IntIfValidConversion<Callable> = 0>
  132. FixedSizeFunction& operator= (Callable&& callable)
  133. {
  134. return *this = FixedSizeFunction (std::forward<Callable> (callable));
  135. }
  136. /** Move assignment from smaller FixedSizeFunctions. */
  137. template <size_t otherLen, std::enable_if_t<(otherLen < len), int> = 0>
  138. FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
  139. {
  140. return *this = FixedSizeFunction (std::move (other));
  141. }
  142. /** Move assignment operator. */
  143. FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
  144. {
  145. clear();
  146. vtable = other.vtable;
  147. move (std::move (other));
  148. return *this;
  149. }
  150. /** Destructor. */
  151. ~FixedSizeFunction() noexcept { clear(); }
  152. /** If this instance is currently storing a callable object, calls that object,
  153. otherwise throws `std::bad_function_call`.
  154. */
  155. Ret operator() (Args... args) const
  156. {
  157. if (vtable != nullptr)
  158. return vtable->call (&storage, std::forward<Args> (args)...);
  159. throw std::bad_function_call();
  160. }
  161. /** Returns true if this instance currently holds a callable. */
  162. explicit operator bool() const noexcept { return vtable != nullptr; }
  163. private:
  164. template <size_t, typename>
  165. friend class FixedSizeFunction;
  166. void clear() noexcept
  167. {
  168. if (vtable != nullptr)
  169. vtable->clear (&storage);
  170. }
  171. template <size_t otherLen, typename T>
  172. void move (FixedSizeFunction<otherLen, T>&& other) noexcept
  173. {
  174. if (vtable != nullptr)
  175. vtable->move (&other.storage, &storage);
  176. }
  177. const detail::Vtable<Ret, Args...>* vtable = nullptr;
  178. mutable Storage storage;
  179. };
  180. template <size_t len, typename T>
  181. bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
  182. template <size_t len, typename T>
  183. bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
  184. template <size_t len, typename T>
  185. bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
  186. template <size_t len, typename T>
  187. bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
  188. }
  189. }