/* ============================================================================== This file is part of the JUCE library. Copyright (c) 2022 - Raw Material Software Limited JUCE is an open source library subject to commercial or open-source licensing. By using JUCE, you agree to the terms of both the JUCE 7 End-User License Agreement and JUCE Privacy Policy. End User License Agreement: www.juce.com/juce-7-licence Privacy Policy: www.juce.com/juce-privacy-policy Or: You may also use this code under the terms of the GPL v3 (see www.gnu.org/licenses). JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ namespace juce { namespace dsp { #ifndef DOXYGEN namespace detail { template struct Vtable { using Storage = void*; using Move = void (*) (Storage, Storage); using Call = Ret (*) (Storage, Args...); using Clear = void (*) (Storage); constexpr Vtable (Move moveIn, Call callIn, Clear clearIn) noexcept : move (moveIn), call (callIn), clear (clearIn) {} Move move = nullptr; Call call = nullptr; Clear clear = nullptr; }; template void move (void* from, void* to) { new (to) Fn (std::move (*reinterpret_cast (from))); } template typename std::enable_if::value, Ret>::type call (void* s, Args... args) { (*reinterpret_cast (s)) (args...); } template typename std::enable_if::value, Ret>::type call (void* s, Args... args) { return (*reinterpret_cast (s)) (std::forward (args)...); } template void clear (void* s) { auto& fn = *reinterpret_cast (s); fn.~Fn(); // I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced ignoreUnused (fn); } template constexpr Vtable makeVtable() { return { move , call , clear }; } } // namespace detail template class FixedSizeFunction; #endif /** A type similar to `std::function` that holds a callable object. Unlike `std::function`, the callable object will always be stored in a buffer of size `len` that is internal to the FixedSizeFunction instance. This in turn means that creating a FixedSizeFunction instance will never allocate, making FixedSizeFunctions suitable for use in realtime contexts. @tags{DSP} */ template class FixedSizeFunction { private: using Storage = typename std::aligned_storage::type; template using Decay = typename std::decay::type; template > using IntIfValidConversion = typename std::enable_if::value, int>::type; public: /** Create an empty function. */ FixedSizeFunction() noexcept = default; /** Create an empty function. */ FixedSizeFunction (std::nullptr_t) noexcept : FixedSizeFunction() {} FixedSizeFunction (const FixedSizeFunction&) = delete; /** Forwards the passed Callable into the internal storage buffer. */ template , IntIfValidConversion = 0> FixedSizeFunction (Callable&& callable) { static_assert (sizeof (Fn) <= len, "The requested function cannot fit in this FixedSizeFunction"); static_assert (alignof (Fn) <= alignof (Storage), "FixedSizeFunction cannot accommodate the requested alignment requirements"); static constexpr auto vtableForCallable = detail::makeVtable(); vtable = &vtableForCallable; auto* ptr = new (&storage) Fn (std::forward (callable)); jassertquiet ((void*) ptr == (void*) &storage); } /** Move constructor. */ FixedSizeFunction (FixedSizeFunction&& other) noexcept : vtable (other.vtable) { move (std::move (other)); } /** Converting constructor from smaller FixedSizeFunctions. */ template ::type = 0> FixedSizeFunction (FixedSizeFunction&& other) noexcept : vtable (other.vtable) { move (std::move (other)); } /** Nulls this instance. */ FixedSizeFunction& operator= (std::nullptr_t) noexcept { return *this = FixedSizeFunction(); } FixedSizeFunction& operator= (const FixedSizeFunction&) = delete; /** Assigns a new callable to this instance. */ template = 0> FixedSizeFunction& operator= (Callable&& callable) { return *this = FixedSizeFunction (std::forward (callable)); } /** Move assignment from smaller FixedSizeFunctions. */ template ::type = 0> FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept { return *this = FixedSizeFunction (std::move (other)); } /** Move assignment operator. */ FixedSizeFunction& operator= (FixedSizeFunction&& other) noexcept { clear(); vtable = other.vtable; move (std::move (other)); return *this; } /** Destructor. */ ~FixedSizeFunction() noexcept { clear(); } /** If this instance is currently storing a callable object, calls that object, otherwise throws `std::bad_function_call`. */ Ret operator() (Args... args) const { if (vtable != nullptr) return vtable->call (&storage, std::forward (args)...); throw std::bad_function_call(); } /** Returns true if this instance currently holds a callable. */ explicit operator bool() const noexcept { return vtable != nullptr; } private: template friend class FixedSizeFunction; void clear() noexcept { if (vtable != nullptr) vtable->clear (&storage); } template void move (FixedSizeFunction&& other) noexcept { if (vtable != nullptr) vtable->move (&other.storage, &storage); } const detail::Vtable* vtable = nullptr; mutable Storage storage; }; template bool operator!= (const FixedSizeFunction& fn, std::nullptr_t) { return bool (fn); } template bool operator!= (std::nullptr_t, const FixedSizeFunction& fn) { return bool (fn); } template bool operator== (const FixedSizeFunction& fn, std::nullptr_t) { return ! (fn != nullptr); } template bool operator== (std::nullptr_t, const FixedSizeFunction& fn) { return ! (fn != nullptr); } } }