Concept Subspace :: sus :: fn :: FnMut
template <class F, class... S>concept FnMut
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::InvokedFnMut<F, typename __private::Sig<S...>::Args>::returns()
} -> __private::ValidReturnType<typename __private::Sig<S...>::Return>;
requires FnOnce<F, S...>;
}
The version of a callable object that is allowed to mutate internal state and may be called multiple times.
A FnMut
is typically the best fit for any callable that may be called one
or more times.
Because a FnMut
is able to mutate internal state, it may return different
values each time it is called with the same inputs.
A type that satisfies FnMut
will return a type that can be converted to
R
when called with the arguments Args...
. FnMut
is satisfied by
being callable as an lvalue (which is done by providing an operator() that
is not &&
-qualified). Mutable and const lambdas will satisfy
FnMut
.
The second argument of FnMut<F, S>
is a function signature with the
format ReturnType(Args...)
, where Args...
are the arguments that will
be passed to the FnMut
and ReturnType
is what is expected to be
received back. It would appear as a matching concept as:
void function(FnMut<ReturnType(Args...)> auto f) { ... }
Use of FnMut
FnMut
should be received by value typically, though it can be received by
reference if mutations should be visible to the caller.
A FnMut
should be called by passing it to call_mut
along with any arguments.
This ensures the correct overload is called on the object and
that method pointers are called correctly.
A FnMut
may be called any number of times, unlike FnOnce
,
and should not be moved when called.
Type erasure
Using a concept like FnMut
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 FnMut
without templates.
To do so, FnMut
supports being type-erased, on the heap or the stack,
into a DynFnMut
type.
To receive ownership of a type-erased FnMut
, receive a
Box
<
DynFnMut<R(Args...)>
>
instead.
To receive a reference to a type-erased FnMut
, receive a
DynFnMut<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 FnMut
matching type and calls it:
// Accepts any type that can be called once with (Option<i32>) and returns
// i32.
static i32 do_stuff_mut(sus::fn::FnMut<i32(sus::Option<i32>)> auto f) {
return sus::fn::call_mut(f, sus::some(400)) +
sus::fn::call_mut(f, sus::some(100));
}
i32 x = do_stuff_mut([i = 0_i32](sus::Option<i32> o) mutable -> i32 {
i += 1;
return sus::move(o).unwrap_or_default() + i;
});
sus_check(x == 401 + 102);
A FnMut
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_mut(const Class& c,
sus::fn::FnMut<i32(const Class&)> auto f) {
return sus::fn::call_mut(f, c);
}
// Map the class C to its value().
auto c = Class(42);
sus_check(map_class_mut(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));