blob: 50ca2489e19ec2afedfb05eb78753d8800b53935 [file] [log] [blame]
#include <cdefs.h>
#include <functional>
#include <tuple>
/**
* Base template for `FunctionWrapper`, never used.
*/
template<typename FnType>
class FunctionWrapper;
/**
* A non-owning type-erased reference to a callable object. This is used
* to pass lambdas (and similar) down the stack without increasing code
* size by template specialisation. Instances of this class must not be
* stored, they should be used only for passing type-erased callable objects
* down the stack.
*
* Instances of this class are two words: a reference to the lambda, and a
* wrapper callback that invokes the lambda.
*
* This is similar to `std::function` but is non-owning and so is guaranteed
* not to allocate memory (the called function may capture memory).
*/
template<class R, class... Args>
class FunctionWrapper<R(Args...)>
{
/**
* Storage for the type-erased function. This holds the reference to
* lambda and to the invoke function.
*/
alignas(void *) char storage[2 * sizeof(void *)];
/**
* Base type for the type-erased function. This defines the virtual
* function that is used to invoke the captured lambda.
*/
struct ErasedFunctionWrapperBase
{
virtual R operator()(Args... args) = 0;
};
/**
* Returns a pointer to the storage, cast to the type-erased function
* type.
*/
ErasedFunctionWrapperBase &stored_function()
{
return *reinterpret_cast<ErasedFunctionWrapperBase *>(storage);
}
/**
* Templated subclass that is specialised for each concrete callable
* type `T` that is passed. One instance of this will be created for
* each lambda type, with a single method in its vtable that invokes
* the lambda.
*/
template<typename T>
class ErasedFunctionWrapper : public ErasedFunctionWrapperBase
{
/// Pointer to the captured lambda.
T &&fn;
public:
/**
* Invoke function. This is virtual and overrides the version in
* the parent class, allowing this to be called from code that does
* not know the cocrete type of the lambda.
*/
R operator()(Args... args) override
{
return fn(std::forward<Args>(args)...);
}
/**
* Construct the type-erased function wrapper, capturing the
* lambda.
*/
ErasedFunctionWrapper(T &&fn) : fn{std::forward<T>(fn)} {}
};
public:
/**
* This is a non-owning reference, delete its copy and move
* constructors to avoid accidental copies.
*/
FunctionWrapper(FunctionWrapper &) = delete;
FunctionWrapper(FunctionWrapper &&) = delete;
FunctionWrapper &operator=(FunctionWrapper &&) = delete;
/**
* Construct the type-erased function wrapper, capturing the lambda.
*/
template<typename T>
__always_inline FunctionWrapper(T &&fn)
{
// Make sure that we got the size for the storage right!
static_assert(sizeof(storage) >= sizeof(ErasedFunctionWrapper<T>));
// Construct the type-erased function in place.
new (storage) ErasedFunctionWrapper<T>(std::forward<T>(fn));
}
/**
* Invoke the captured lambda.
*/
__always_inline R operator()(Args... args)
{
return stored_function()(std::forward<Args>(args)...);
}
};