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.

244 lines
7.9KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - 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 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-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. juce::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. jassert ((void*) ptr == (void*) &storage);
  111. juce::ignoreUnused (ptr);
  112. }
  113. /** Move constructor. */
  114. FixedSizeFunction (FixedSizeFunction&& other) noexcept
  115. : vtable (other.vtable)
  116. {
  117. move (std::move (other));
  118. }
  119. /** Converting constructor from smaller FixedSizeFunctions. */
  120. template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
  121. FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
  122. : vtable (other.vtable)
  123. {
  124. move (std::move (other));
  125. }
  126. /** Nulls this instance. */
  127. FixedSizeFunction& operator= (std::nullptr_t) noexcept
  128. {
  129. return *this = FixedSizeFunction();
  130. }
  131. FixedSizeFunction& operator= (const FixedSizeFunction&) = delete;
  132. /** Assigns a new callable to this instance. */
  133. template <typename Callable, IntIfValidConversion<Callable> = 0>
  134. FixedSizeFunction& operator= (Callable&& callable)
  135. {
  136. return *this = FixedSizeFunction (std::forward<Callable> (callable));
  137. }
  138. /** Move assignment from smaller FixedSizeFunctions. */
  139. template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 0>
  140. FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& other) noexcept
  141. {
  142. return *this = FixedSizeFunction (std::move (other));
  143. }
  144. /** Move assignment operator. */
  145. FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept
  146. {
  147. clear();
  148. vtable = other.vtable;
  149. move (std::move (other));
  150. return *this;
  151. }
  152. /** Destructor. */
  153. ~FixedSizeFunction() noexcept { clear(); }
  154. /** If this instance is currently storing a callable object, calls that object,
  155. otherwise throws `std::bad_function_call`.
  156. */
  157. Ret operator() (Args... args) const
  158. {
  159. if (vtable != nullptr)
  160. return vtable->call (&storage, std::forward<Args> (args)...);
  161. throw std::bad_function_call();
  162. }
  163. /** Returns true if this instance currently holds a callable. */
  164. explicit operator bool() const noexcept { return vtable != nullptr; }
  165. private:
  166. template <size_t, typename>
  167. friend class FixedSizeFunction;
  168. void clear() noexcept
  169. {
  170. if (vtable != nullptr)
  171. vtable->clear (&storage);
  172. }
  173. template <size_t otherLen, typename T>
  174. void move (FixedSizeFunction<otherLen, T>&& other) noexcept
  175. {
  176. if (vtable != nullptr)
  177. vtable->move (&other.storage, &storage);
  178. }
  179. const detail::Vtable<Ret, Args...>* vtable = nullptr;
  180. mutable Storage storage;
  181. };
  182. template <size_t len, typename T>
  183. bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
  184. template <size_t len, typename T>
  185. bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
  186. template <size_t len, typename T>
  187. bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
  188. template <size_t len, typename T>
  189. bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
  190. }
  191. }