mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
236 lines
7.6 KiB
C++
236 lines
7.6 KiB
C++
/*
|
|
==============================================================================
|
|
|
|
This file is part of the JUCE 6 technical preview.
|
|
Copyright (c) 2020 - Raw Material Software Limited
|
|
|
|
You may use this code under the terms of the GPL v3
|
|
(see www.gnu.org/licenses).
|
|
|
|
For this technical preview, this file is not subject to commercial licensing.
|
|
|
|
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>
|
|
typename std::enable_if<std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
|
{
|
|
(*reinterpret_cast<Fn*> (s)) (args...);
|
|
}
|
|
|
|
template <typename Fn, typename Ret, typename... Args>
|
|
typename std::enable_if<! std::is_same<Ret, void>::value, Ret>::type call (void* s, Args... args)
|
|
{
|
|
return (*reinterpret_cast<Fn*> (s)) (std::forward<Args> (args)...);
|
|
}
|
|
|
|
template <typename Fn>
|
|
void clear (void* s)
|
|
{
|
|
auto& fn = *reinterpret_cast<Fn*> (s);
|
|
fn.~Fn();
|
|
// I know this looks insane, for some reason MSVC 14 sometimes thinks fn is unreferenced
|
|
juce::ignoreUnused (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 = typename std::aligned_storage<len>::type;
|
|
|
|
template <typename Item>
|
|
using Decay = typename std::decay<Item>::type;
|
|
|
|
template <typename Item, typename Fn = Decay<Item>>
|
|
using IntIfValidConversion = typename std::enable_if<sizeof (Fn) <= len
|
|
&& alignof (Fn) <= alignof (Storage)
|
|
&& ! std::is_same<FixedSizeFunction, Fn>::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 <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));
|
|
jassert ((void*) ptr == (void*) &storage);
|
|
juce::ignoreUnused (ptr);
|
|
}
|
|
|
|
/** Move constructor. */
|
|
FixedSizeFunction (FixedSizeFunction&& other) noexcept
|
|
: vtable (other.vtable)
|
|
{
|
|
move (std::move (other));
|
|
}
|
|
|
|
/** Converting constructor from smaller FixedSizeFunctions. */
|
|
template <size_t otherLen, typename std::enable_if<(otherLen < len), int>::type = 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, typename std::enable_if<(otherLen < len), int>::type = 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); }
|
|
|
|
|
|
}
|
|
}
|