template<class R, class ... Args>
Corrade::Containers::Function<R(Args...)> class new in Git master

Function wrapper.

An alternative to std::move_only_function from C++23 or a move-only alternative to std::function from C++11, providing a common interface for free functions, member functions, (capturing) lambdas and generic functors. Example usage:

Containers::Function<int(int)> a = std::abs;

Containers::Function<int(int)> b = [](int value) { return value*2; };

struct Accumulator {
    int sum = 1337;
    int add(int value) { return sum += value; }
} accumulator;

Containers::Function<int(int)> c{accumulator, &Accumulator::add};

The wrapped function is then called through operator()():

a(-16);                                                             // 16
b(376);                                                             // 752
c(110);                                                             // 1447

To prevent accidental type conversions and potential extra overhead coming from those, the wrapper only accepts functions that match the signature exactly. If a function or a functor has a set of overloads (such as is the case with std::abs() shown above), an overload matching the signature is picked. If you have a function which has a different signature, wrap it in a lambda of a matching singature first:

double degreesToRadians(double);

//Containers::Function<float(float)> a = degreesToRadians;          // error

Containers::Function<float(float)> b = [](float radians) -> float {
    return float(degreesToRadians(double(radians)));
};

Function call overhead

On construction, a stateless wrapper lambda is created that then delegates to the particular free function pointer, member function pointer, or a stateful functor / lambda. In the common case this means that invoking a Function always involves two function calls, one for the wrapper lambda and one for the actual function being called.

If the wrapped function is inlineable, in optimized builds this can reduce to just a single function call to the wrapper lambda, which then has the actual function call inlined inside.

Stateful function storage

The Function class is internally made large enough to fit any free or member function pointer. But because member function pointers can increase in size if multiple inheritance and/or virtual inheritance is involved, that space can be repurposed also for saving up to 24 bytes of stateful lambda / functor data on 64-bit platforms (and up to 16 bytes on 32-bit platforms). If larger, the data is allocated on heap instead. For implementation simplicity reasons the state is also allocated if isn't trivially copyable. You can use isAllocated() to check whether the function state needed a heap allocation or not.

/* Small enough, stored inline */
int seed = ;
Containers::Function<int(int)> hash = [seed](int value) {
    return seed ^ value;
};

/* Too large, allocated */
int state[8]{};
Containers::Function<int()> random = [state]() mutable {
    
};

/* Small enough but non-trivial, allocated */
Containers::String salt = ;
Containers::Function<int(int)> checksum = [salt](int value) {
    
};

If heap allocation is undesirable, the Function(NoAllocateInitT, F&&) overload can be used to prevent wrapping any function with state that would need to be allocated:

Containers::Function<int(int)> hash{Containers::NoAllocateInit,
    [seed](int value) {  }};

// Containers::Function<int()> random{Containers::NoAllocateInit,   // error
//    [state]() mutable { … }};

Type-erased function storage

The class derives from a type-erased FunctionData base, which owns all state and also takes care of proper moving and destruction for non-trivial functors. This allows it to be used as a type-erased storage in various containers for example. Cast the instance back to a concrete Function in order to use it.

Containers::FunctionData a = Containers::Function<int(int)>{std::abs};
Containers::FunctionData b = Containers::Function<float(float)>{std::round};

static_cast<Containers::Function<int(int)>&>(a)(-15);               // 15
static_cast<Containers::Function<float(float)>&>(b)(3.56f);         // 4.0f

Base classes

class FunctionData new in Git master
Function data storage

Public types

using Type = R()(Args...)
Function type.

Constructors, destructors, conversion operators

Function(std::nullptr_t = nullptr) noexcept
Default constructor.
Function(R(*)(Args...) f) noexcept
Wrap a free function pointer.
template<class Instance, class Class>
Function(Instance& instance, R(Class::*)(Args...) f) noexcept
Wrap a member function pointer.
template<class Instance, class Class>
Function(Instance& instance, R(Class::*)(Args...) f&) noexcept
template<class Instance, class Class>
Function(Instance& instance, R(Class::*)(Args...) const f) noexcept
template<class Instance, class Class>
Function(Instance& instance, R(Class::*)(Args...) const f&) noexcept
template<class Instance>
Function(Instance&, std::nullptr_t) noexcept
Create a null member function pointer.
template<class F>
Function(F&& f) noexcept(…)
Wrap a lambda or a functor.
template<class F>
Function(NoAllocateInitT, F&& f) explicit noexcept
Wrap a small enough and trivial lambda / functor.

Public functions

auto operator()(Args... args) -> R
Call the function pointer.

Function documentation

template<class R, class ... Args>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(std::nullptr_t = nullptr) noexcept

Default constructor.

Creates a nullptr function.

template<class R, class ... Args>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(R(*)(Args...) f) noexcept

Wrap a free function pointer.

If f is nullptr, the constructor is equivalent to Function(std::nullptr_t).

template<class R, class ... Args> template<class Instance, class Class>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(Instance& instance, R(Class::*)(Args...) f) noexcept

Wrap a member function pointer.

Default, &, const and const & r-value overloads are supported, && and const && however isn't, as the member function is always called on a l-value. If f is nullptr, the constructor is equivalent to Function(std::nullptr_t).

template<class R, class ... Args> template<class Instance, class Class>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(Instance& instance, R(Class::*)(Args...) f&) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class R, class ... Args> template<class Instance, class Class>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(Instance& instance, R(Class::*)(Args...) const f) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class R, class ... Args> template<class Instance, class Class>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(Instance& instance, R(Class::*)(Args...) const f&) noexcept

This is an overloaded member function, provided for convenience. It differs from the above function only in what argument(s) it accepts.

template<class R, class ... Args> template<class Instance>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(Instance&, std::nullptr_t) noexcept

Create a null member function pointer.

Equivalent to Function(std::nullptr_t).

template<class R, class ... Args> template<class F>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(F&& f) noexcept(…)

Wrap a lambda or a functor.

The functor is expected to exactly match the signature, no implicit conversions on either the arguments or return type are allowed. If the lambda capture or functor state is small enough and trivially copyable, it's stored inline, otherwise allocated on heap. See Stateful function storage for more information.

template<class R, class ... Args> template<class F>
Corrade::Containers::Function<R(Args...)><R, Args>::Function(NoAllocateInitT, F&& f) explicit noexcept

Wrap a small enough and trivial lambda / functor.

Compared to Function(F&&) compiles only if the lambda capture or functor state is small enough and trivially copyable to not need to be allocated on heap. See Stateful function storage for more information.

Note that there's no NoAllocateInit variant for free or member function pointers, as they never need to be allocated on heap.

template<class R, class ... Args>
R Corrade::Containers::Function<R(Args...)><R, Args>::operator()(Args... args)

Call the function pointer.

Expects that the pointer is not nullptr.