Audio plugin host https://kx.studio/carla
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.

243 lines
7.8KB

  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. typename std::enable_if<std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
  45. {
  46. (*reinterpret_cast<Fn*> (s)) (args...);
  47. }
  48. template <typename Fn, typename Ret, typename... Args>
  49. typename std::enable_if<! std::is_same<Ret, void>::value, Ret>::type 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. auto& fn = *reinterpret_cast<Fn*> (s);
  57. fn.~Fn();
  58. // I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
  59. ignoreUnused (fn);
  60. }
  61. template <typename Fn, typename Ret, typename... Args>
  62. constexpr Vtable<Ret, Args...> makeVtable()
  63. {
  64. return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
  65. }
  66. } // namespace detail
  67. template <size_t len, typename T>
  68. class FixedSizeFunction;
  69. #endif
  70. /**
  71. A type similar to `std::function` that holds a callable object.
  72. Unlike `std::function`, the callable object will always be stored in
  73. a buffer of size `len` that is internal to the FixedSizeFunction instance.
  74. This in turn means that creating a FixedSizeFunction instance will never allocate,
  75. making FixedSizeFunctions suitable for use in realtime contexts.
  76. @tags{DSP}
  77. */
  78. template <size_t len, typename Ret, typename... Args>
  79. class FixedSizeFunction<len, Ret (Args...)>
  80. {
  81. private:
  82. using Storage = typename std::aligned_storage<len>::type;
  83. template <typename Item>
  84. using Decay = typename std::decay<Item>::type;
  85. template <typename Item, typename Fn = Decay<Item>>
  86. using IntIfValidConversion = typename std::enable_if<sizeof (Fn) <= len
  87. && alignof (Fn) <= alignof (Storage)
  88. && ! std::is_same<FixedSizeFunction, Fn>::value,
  89. int>::type;
  90. public:
  91. /** Create an empty function. */
  92. FixedSizeFunction() noexcept = default;
  93. /** Create an empty function. */
  94. FixedSizeFunction (std::nullptr_t) noexcept
  95. : FixedSizeFunction() {}
  96. FixedSizeFunction (const FixedSizeFunction&) = delete;
  97. /** Forwards the passed Callable into the internal storage buffer. */
  98. template <typename Callable,
  99. typename Fn = Decay<Callable>,
  100. IntIfValidConversion<Callable> = 0>
  101. FixedSizeFunction (Callable&& callable)
  102. {
  103. static_assert (sizeof (Fn) <= len,
  104. "The requested function cannot fit in this FixedSizeFunction");
  105. static_assert (alignof (Fn) <= alignof (Storage),
  106. "FixedSizeFunction cannot accommodate the requested alignment requirements");
  107. static constexpr auto vtableForCallable = detail::makeVtable<Fn, Ret, Args...>();
  108. vtable = &vtableForCallable;
  109. auto* ptr = new (&storage) Fn (std::forward<Callable> (callable));
  110. jassertquiet ((void*) ptr == (void*) &storage);
  111. }
  112. /** Move constructor. */
  113. FixedSizeFunction (FixedSizeFunction&& other) noexcept
  114. : vtable (other.vtable)
  115. {
  116. move (std::move (other));
  117. }
  118. /** Converting constructor from smaller FixedSizeFunctions. */
  119. template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
  120. FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
  121. : vtable (other.vtable)
  122. {
  123. move (std::move (other));
  124. }
  125. /** Nulls this instance. */
  126. FixedSizeFunction& operator= (std::nullptr_t) noexcept
  127. {
  128. return *this = FixedSizeFunction();
  129. }
  130. FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
  131. /** Assigns a new callable to this instance. */
  132. template <typename Callable, IntIfValidConversion<Callable> = 0>
  133. FixedSizeFunction& operator= (Callable&& callable)
  134. {
  135. return *this = FixedSizeFunction (std::forward<Callable> (callable));
  136. }
  137. /** Move assignment from smaller FixedSizeFunctions. */
  138. template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
  139. FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
  140. {
  141. return *this = FixedSizeFunction (std::move (other));
  142. }
  143. /** Move assignment operator. */
  144. FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
  145. {
  146. clear();
  147. vtable = other.vtable;
  148. move (std::move (other));
  149. return *this;
  150. }
  151. /** Destructor. */
  152. ~FixedSizeFunction() noexcept { clear(); }
  153. /** If this instance is currently storing a callable object, calls that object,
  154. otherwise throws `std::bad_function_call`.
  155. */
  156. Ret operator() (Args... args) const
  157. {
  158. if (vtable != nullptr)
  159. return vtable->call (&storage, std::forward<Args> (args)...);
  160. throw std::bad_function_call();
  161. }
  162. /** Returns true if this instance currently holds a callable. */
  163. explicit operator bool() const noexcept { return vtable != nullptr; }
  164. private:
  165. template <size_t, typename>
  166. friend class FixedSizeFunction;
  167. void clear() noexcept
  168. {
  169. if (vtable != nullptr)
  170. vtable->clear (&storage);
  171. }
  172. template <size_t otherLen, typename T>
  173. void move (FixedSizeFunction<otherLen, T>&& other) noexcept
  174. {
  175. if (vtable != nullptr)
  176. vtable->move (&other.storage, &storage);
  177. }
  178. const detail::Vtable<Ret, Args...>* vtable = nullptr;
  179. mutable Storage storage;
  180. };
  181. template <size_t len, typename T>
  182. bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
  183. template <size_t len, typename T>
  184. bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
  185. template <size_t len, typename T>
  186. bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
  187. template <size_t len, typename T>
  188. bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
  189. }
  190. }