/* ============================================================================== This file is part of the JUCE 7 technical preview. Copyright (c) 2022 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For the technical preview this file cannot be licensed commercially. 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); } } }