Concept Subspace :: sus :: fn :: FnOnce
template <class F, class... S>concept FnOnce
requires {// Receives and passes along the signature as a pack instead of a single
// argument in order to consistently provide a static_assert() in `Sig` when
// `S` is not a function signature.
{__private::InvokedFnOnce<F, typename __private::Sig<S...>::Args>::returns()
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
}
The version of a callable object that may be called only once.
A FnOnce is typically the best fit for any callable that will
only be called at most once.
A type that satisfies FnOnce will return a type that can be converted to
R when called with the arguments Args.... FnOnce is satisfied by
being callable as an rvalue (which is done by providing an operator() that
is not &-qualified). Mutable and const lambdas will satisfy FnOnce.
The second argument of FnOnce<F, S> is a function signature with the
format ReturnType(Args...), where Args... are the arguments that will
be passed to the FnOnce and ReturnType is what is expected to be
received back. It would appear as a matching concept as:
void function(FnOnce<ReturnType(Args...)> auto f) { ... }
Use of FnOnce
FnOnce should be received by value typically. If received as a rvalue
(universal) reference, it should be constrained by
IsMoveRef<decltype(f)> to avoid moving out of
an lvalue in the caller.
A FnOnce should be called by moving it with sus::move() when passing it
to call_once along with any arguments.
This ensures the
correct overload is called on the object and that method pointers are
called correctly. It is moved-from after calling, and it should only be
called once.
Calling a FnOnce multiple times may panic
or cause Undefined Behaviour.
Not moving the FnOnce when calling it may fail to compile,
panic, or cause Undefined Behaviour depending on the type
that is being used to satisfy FnOnce.
Type erasure
Using a concept like FnOnce in a function parameter requires the function
to be a template. Template functions can not be virtual, they must appear
in the header, and they can have a negative impact on binary size. So it can
be desirable to work with a FnOnce without templates.
To do so, FnOnce supports being type-erased, on the heap or the stack,
into a DynFnOnce type.
To receive ownership of a type-erased FnOnce, receive a
Box<DynFnOnce<R(Args...)>>
instead.
To receive a reference to a type-erased FnOnce, receive a
DynFnOnce<R(Args...)>&& instead.
See DynConcept for more on type erasure of
concept-satisfying types.
Compatibility
Any callable type that satisfies Fn or FnMut will also satisfy FnOnce.
While a FnOnce should only be called once, this is compatible with the
requirements of FnMut and Fn which can be called only a single time. As
well, FnOnce is allowed to mutate internal state, but it does not have to,
which is compatible with the const nature of Fn.
Examples
A function that receives a FnOnce matching type and calls it:
// Accepts any type that can be called once with (Option<i32>) and returns
// i32.
i32 do_stuff_once(sus::fn::FnOnce<i32(sus::Option<i32>)> auto f) {
return sus::fn::call_once(sus::move(f), sus::some(400));
}
i32 x = do_stuff_once([](sus::Option<i32> o) -> i32 {
return sus::move(o).unwrap_or_default() + 4;
});
sus_check(x == 400 + 4);
A FnOnce whose first parameter is a class can be matched with a method
from that same class if the remaining parameters match the method's
signature:
struct Class {
Class(i32 value) : value_(value) {}
i32 value() const { return value_; }
private:
i32 value_;
};
i32 map_class_once(const Class& c,
sus::fn::FnOnce<i32(const Class&)> auto f) {
return sus::fn::call_once(sus::move(f), c);
}
// Map the class C to its value().
auto c = Class(42);
sus_check(map_class_once(c, &Class::value) == 42);
Using a method pointer as the parameter for Option::map() will call that
method on the object inside the Option:
struct Class {
Class(i32 value) : value_(value) {}
i32 value() const { return value_; }
private:
i32 value_;
};
auto o = sus::Option<Class>(Class(42));
sus_check(o.map(&Class::value) == sus::some(42));