mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-11 23:54:18 +00:00
241 lines
7.7 KiB
C++
241 lines
7.7 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
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 <typename Ret, typename... Args>
|
|
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 <typename Fn>
|
|
void move (void* from, void* to)
|
|
{
|
|
new (to) Fn (std::move (*reinterpret_cast<Fn*> (from)));
|
|
}
|
|
|
|
template <typename Fn, typename Ret, typename... Args>
|
|
std::enable_if_t<std::is_same_v<Ret, void>, Ret> call (void* s, Args... args)
|
|
{
|
|
(*reinterpret_cast<Fn*> (s)) (args...);
|
|
}
|
|
|
|
template <typename Fn, typename Ret, typename... Args>
|
|
std::enable_if_t<! std::is_same_v<Ret, void>, Ret> call (void* s, Args... args)
|
|
{
|
|
return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
|
|
}
|
|
|
|
template <typename Fn>
|
|
void clear (void* s)
|
|
{
|
|
// I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
|
|
[[maybe_unused]] auto& fn = *reinterpret_cast<Fn*> (s);
|
|
fn.~Fn();
|
|
}
|
|
|
|
template <typename Fn, typename Ret, typename... Args>
|
|
constexpr Vtable<Ret, Args...> makeVtable()
|
|
{
|
|
return { move <Fn>, call <Fn, Ret, Args...>, clear<Fn> };
|
|
}
|
|
} // namespace detail
|
|
|
|
template <size_t len, typename T>
|
|
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 <size_t len, typename Ret, typename... Args>
|
|
class FixedSizeFunction<len, Ret (Args...)>
|
|
{
|
|
private:
|
|
using Storage = std::aligned_storage_t<len>;
|
|
|
|
template <typename Item>
|
|
using Decay = std::decay_t<Item>;
|
|
|
|
template <typename Item, typename Fn = Decay<Item>>
|
|
using IntIfValidConversion = std::enable_if_t<sizeof (Fn) <= len
|
|
&& alignof (Fn) <= alignof (Storage)
|
|
&& ! std::is_same_v<FixedSizeFunction, Fn>,
|
|
int>;
|
|
|
|
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 <typename Callable,
|
|
typename Fn = Decay<Callable>,
|
|
IntIfValidConversion<Callable> = 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<Fn, Ret, Args...>();
|
|
vtable = &vtableForCallable;
|
|
|
|
auto* ptr = new (&storage) Fn (std::forward<Callable> (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 <size_t otherLen, std::enable_if_t<(otherLen < len), int> = 0>
|
|
FixedSizeFunction (FixedSizeFunction<otherLen, Ret (Args...)>&& 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 <typename Callable, IntIfValidConversion<Callable> = 0>
|
|
FixedSizeFunction& operator= (Callable&& callable)
|
|
{
|
|
return *this = FixedSizeFunction (std::forward<Callable> (callable));
|
|
}
|
|
|
|
/** Move assignment from smaller FixedSizeFunctions. */
|
|
template <size_t otherLen, std::enable_if_t<(otherLen < len), int> = 0>
|
|
FixedSizeFunction& operator= (FixedSizeFunction<otherLen, Ret (Args...)>&& 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> (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 <size_t, typename>
|
|
friend class FixedSizeFunction;
|
|
|
|
void clear() noexcept
|
|
{
|
|
if (vtable != nullptr)
|
|
vtable->clear (&storage);
|
|
}
|
|
|
|
template <size_t otherLen, typename T>
|
|
void move (FixedSizeFunction<otherLen, T>&& other) noexcept
|
|
{
|
|
if (vtable != nullptr)
|
|
vtable->move (&other.storage, &storage);
|
|
}
|
|
|
|
const detail::Vtable<Ret, Args...>* vtable = nullptr;
|
|
mutable Storage storage;
|
|
};
|
|
|
|
template <size_t len, typename T>
|
|
bool operator!= (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return bool (fn); }
|
|
|
|
template <size_t len, typename T>
|
|
bool operator!= (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return bool (fn); }
|
|
|
|
template <size_t len, typename T>
|
|
bool operator== (const FixedSizeFunction<len, T>& fn, std::nullptr_t) { return ! (fn != nullptr); }
|
|
|
|
template <size_t len, typename T>
|
|
bool operator== (std::nullptr_t, const FixedSizeFunction<len, T>& fn) { return ! (fn != nullptr); }
|
|
|
|
|
|
}
|
|
}
|