1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * A generic callable type that can be initialized from any compatible callable,
9 * suitable for use as a function argument for the duration of the function
10 * call (and no longer).
13 #ifndef mozilla_FunctionRef_h
14 #define mozilla_FunctionRef_h
16 #include "mozilla/OperatorNewExtensions.h" // mozilla::NotNull, ::operator new
18 #include <cstddef> // std::nullptr_t
19 #include <type_traits> // std::{declval,integral_constant}, std::is_{convertible,same,void}_v, std::{enable_if,remove_reference,remove_cv}_t
21 // This concept and its implementation are substantially inspired by foonathan's
24 // https://foonathan.net/2017/01/function-ref-implementation/
25 // https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp
31 // Template helper to determine if |Returned| is a return type compatible with
32 // |Required|: if the former converts to the latter, or if |Required| is |void|
33 // and nothing is returned.
34 template <typename Returned
, typename Required
>
35 using CompatibleReturnType
=
36 std::integral_constant
<bool, std::is_void_v
<Required
> ||
37 std::is_convertible_v
<Returned
, Required
>>;
39 // Template helper to check if |Func| called with |Params| arguments returns
40 // a type compatible with |Ret|.
41 template <typename Func
, typename Ret
, typename
... Params
>
42 using EnableMatchingFunction
= std::enable_if_t
<
44 decltype(std::declval
<Func
&>()(std::declval
<Params
>()...)), Ret
>::value
,
47 struct MatchingFunctionPointerTag
{};
48 struct MatchingFunctorTag
{};
49 struct InvalidFunctorTag
{};
51 // Template helper to determine the proper way to store |Callable|: as function
52 // pointer, as pointer to object, or unstorable.
53 template <typename Callable
, typename Ret
, typename
... Params
>
54 struct GetCallableTag
{
55 // Match the case where |Callable| is a compatible function pointer or
56 // converts to one. (|+obj| invokes such a conversion.)
58 static MatchingFunctionPointerTag
test(
59 int, T
& obj
, EnableMatchingFunction
<decltype(+obj
), Ret
, Params
...> = 0);
61 // Match the case where |Callable| is callable but can't be converted to a
62 // function pointer. (|short| is a worse match for 0 than |int|, causing the
63 // function pointer match to be preferred if both apply.)
65 static MatchingFunctorTag
test(short, T
& obj
,
66 EnableMatchingFunction
<T
, Ret
, Params
...> = 0);
68 // Match all remaining cases. (Any other match is preferred to an ellipsis
70 static InvalidFunctorTag
test(...);
72 using Type
= decltype(test(0, std::declval
<Callable
&>()));
75 // If the callable is |nullptr|, |std::declval<std::nullptr_t&>()| will be an
76 // error. Provide a specialization for |nullptr| that will fail substitution.
77 template <typename Ret
, typename
... Params
>
78 struct GetCallableTag
<std::nullptr_t
, Ret
, Params
...> {};
80 template <typename Result
, typename Callable
, typename Ret
, typename
... Params
>
81 using EnableFunctionTag
= std::enable_if_t
<
82 std::is_same_v
<typename GetCallableTag
<Callable
, Ret
, Params
...>::Type
,
89 * An efficient, type-erasing, non-owning reference to a callable. It is
90 * intended for use as the type of a function parameter that is not used after
91 * the function in question returns.
93 * This class does not own the callable, so in general it is unsafe to store a
96 template <typename Fn
>
99 template <typename Ret
, typename
... Params
>
100 class FunctionRef
<Ret(Params
...)> {
103 // |FunctionRef| stores an adaptor function pointer, determined by the
104 // arguments passed to the constructor. That adaptor will perform the steps
105 // needed to invoke the callable passed at construction time.
106 using Adaptor
= Ret (*)(const Payload
& aPayload
, Params
... aParams
);
108 // If |FunctionRef|'s callable can be stored as a function pointer, that
109 // function pointer is stored after being cast to this *different* function
110 // pointer type. |mAdaptor| then casts back to the original type to call it.
111 // ([expr.reinterpret.cast]p6 guarantees that A->B->A function pointer casts
112 // produce the original function pointer value.) An outlandish signature is
113 // used to emphasize that the exact function pointer type doesn't matter.
114 using FuncPtr
= Payload
***** (*)(Payload
*****);
117 * An adaptor function (used by this class's function call operator) that
118 * invokes the callable in |mPayload|, forwarding arguments and converting
119 * return type as needed.
121 const Adaptor mAdaptor
;
123 /** Storage for the wrapped callable value. */
125 // This arm is used if |FunctionRef| is passed a compatible function pointer
126 // or a lambda/callable that converts to a compatible function pointer.
129 // This arm is used if |FunctionRef| is passed some other callable or
134 template <typename RealFuncPtr
>
135 static Ret
CallFunctionPointer(const Payload
& aPayload
,
136 Params
... aParams
) noexcept
{
137 auto func
= reinterpret_cast<RealFuncPtr
>(aPayload
.mFuncPtr
);
138 return static_cast<Ret
>(func(static_cast<Params
>(aParams
)...));
141 template <typename Ret2
, typename
... Params2
>
142 FunctionRef(detail::MatchingFunctionPointerTag
, Ret2 (*aFuncPtr
)(Params2
...))
143 : mAdaptor(&CallFunctionPointer
<Ret2 (*)(Params2
...)>) {
144 ::new (KnownNotNull
, &mPayload
.mFuncPtr
)
145 FuncPtr(reinterpret_cast<FuncPtr
>(aFuncPtr
));
150 * Construct a |FunctionRef| that's like a null function pointer that can't be
153 MOZ_IMPLICIT
FunctionRef(std::nullptr_t
) noexcept
: mAdaptor(nullptr) {
154 // This is technically unnecessary, but it seems best to always initialize
156 ::new (KnownNotNull
, &mPayload
.mObject
) void*(nullptr);
159 FunctionRef() : FunctionRef(nullptr) {}
162 * Constructs a |FunctionRef| from an object callable with |Params| arguments,
163 * that returns a type convertible to |Ret|, where the callable isn't
164 * convertible to function pointer (often because it contains some internal
165 * state). For example:
168 * auto doSideEffect = [&x]{ x++; }; // state is captured reference to |x|
169 * FunctionRef<void()> f(doSideEffect);
173 typename
= detail::EnableFunctionTag
<detail::MatchingFunctorTag
, Callable
,
175 typename
std::enable_if_t
<!std::is_same_v
<
176 typename
std::remove_reference_t
<typename
std::remove_cv_t
<Callable
>>,
177 FunctionRef
>>* = nullptr>
178 MOZ_IMPLICIT
FunctionRef(Callable
& aCallable
) noexcept
179 : mAdaptor([](const Payload
& aPayload
, Params
... aParams
) {
180 auto& func
= *static_cast<Callable
*>(aPayload
.mObject
);
181 return static_cast<Ret
>(func(static_cast<Params
>(aParams
)...));
183 ::new (KnownNotNull
, &mPayload
.mObject
) void*(&aCallable
);
187 * Constructs a |FunctionRef| from an value callable with |Params| arguments,
188 * that returns a type convertible to |Ret|, where the callable is stateless
189 * and is (or is convertible to) a function pointer. For example:
192 * double twice(double d) { return d * 2; }
193 * FunctionRef<double(double)> func1(&twice);
195 * // Compatible match
196 * float thrice(long double d) { return static_cast<float>(d) * 3; }
197 * FunctionRef<double(double)> func2(&thrice);
199 * // Non-generic lambdas that don't capture anything have a conversion
200 * // function to the appropriate function pointer type.
201 * FunctionRef<int(double)> f([](long double){ return 'c'; });
203 template <typename Callable
,
204 typename
= detail::EnableFunctionTag
<
205 detail::MatchingFunctionPointerTag
, Callable
, Ret
, Params
...>>
206 MOZ_IMPLICIT
FunctionRef(const Callable
& aCallable
) noexcept
207 : FunctionRef(detail::MatchingFunctionPointerTag
{}, +aCallable
) {}
209 /** Call the callable stored in this with the given arguments. */
210 Ret
operator()(Params
... params
) const {
211 return mAdaptor(mPayload
, static_cast<Params
>(params
)...);
214 /** Return true iff this wasn't created from |nullptr|. */
215 explicit operator bool() const noexcept
{ return mAdaptor
!= nullptr; }
218 } /* namespace mozilla */
220 #endif /* mozilla_FunctionRef_h */