Bug 1890277: part 2) Add `require-trusted-types-for` directive to CSP parser, guarded...
[gecko.git] / mfbt / FunctionRef.h
blob374173d884a25297bffc71224167d04853050e2a
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/. */
7 /*
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
20 #include <utility> // std::forward
22 // This concept and its implementation are substantially inspired by foonathan's
23 // prior art:
25 // https://foonathan.net/2017/01/function-ref-implementation/
26 // https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp
28 namespace mozilla {
30 namespace detail {
32 // Template helper to determine if |Returned| is a return type compatible with
33 // |Required|: if the former converts to the latter, or if |Required| is |void|
34 // and nothing is returned.
35 template <typename Returned, typename Required>
36 using CompatibleReturnType =
37 std::integral_constant<bool, std::is_void_v<Required> ||
38 std::is_convertible_v<Returned, Required>>;
40 // Template helper to check if |Func| called with |Params| arguments returns
41 // a type compatible with |Ret|.
42 template <typename Func, typename Ret, typename... Params>
43 using EnableMatchingFunction = std::enable_if_t<
44 CompatibleReturnType<
45 decltype(std::declval<Func&>()(std::declval<Params>()...)), Ret>::value,
46 int>;
48 struct MatchingFunctionPointerTag {};
49 struct MatchingFunctorTag {};
50 struct InvalidFunctorTag {};
52 // Template helper to determine the proper way to store |Callable|: as function
53 // pointer, as pointer to object, or unstorable.
54 template <typename Callable, typename Ret, typename... Params>
55 struct GetCallableTag {
56 // Match the case where |Callable| is a compatible function pointer or
57 // converts to one. (|+obj| invokes such a conversion.)
58 template <typename T>
59 static MatchingFunctionPointerTag test(
60 int, T& obj, EnableMatchingFunction<decltype(+obj), Ret, Params...> = 0);
62 // Match the case where |Callable| is callable but can't be converted to a
63 // function pointer. (|short| is a worse match for 0 than |int|, causing the
64 // function pointer match to be preferred if both apply.)
65 template <typename T>
66 static MatchingFunctorTag test(short, T& obj,
67 EnableMatchingFunction<T, Ret, Params...> = 0);
69 // Match all remaining cases. (Any other match is preferred to an ellipsis
70 // match.)
71 static InvalidFunctorTag test(...);
73 using Type = decltype(test(0, std::declval<Callable&>()));
76 // If the callable is |nullptr|, |std::declval<std::nullptr_t&>()| will be an
77 // error. Provide a specialization for |nullptr| that will fail substitution.
78 template <typename Ret, typename... Params>
79 struct GetCallableTag<std::nullptr_t, Ret, Params...> {};
81 template <typename Result, typename Callable, typename Ret, typename... Params>
82 using EnableFunctionTag = std::enable_if_t<
83 std::is_same_v<typename GetCallableTag<Callable, Ret, Params...>::Type,
84 Result>,
85 int>;
87 } // namespace detail
89 /**
90 * An efficient, type-erasing, non-owning reference to a callable. It is
91 * intended for use as the type of a function parameter that is not used after
92 * the function in question returns.
94 * This class does not own the callable, so in general it is unsafe to store a
95 * FunctionRef.
97 template <typename Fn>
98 class MOZ_TEMPORARY_CLASS FunctionRef;
100 template <typename Ret, typename... Params>
101 class MOZ_TEMPORARY_CLASS FunctionRef<Ret(Params...)> {
102 union Payload;
104 // |FunctionRef| stores an adaptor function pointer, determined by the
105 // arguments passed to the constructor. That adaptor will perform the steps
106 // needed to invoke the callable passed at construction time.
107 using Adaptor = Ret (*)(const Payload& aPayload, Params... aParams);
109 // If |FunctionRef|'s callable can be stored as a function pointer, that
110 // function pointer is stored after being cast to this *different* function
111 // pointer type. |mAdaptor| then casts back to the original type to call it.
112 // ([expr.reinterpret.cast]p6 guarantees that A->B->A function pointer casts
113 // produce the original function pointer value.) An outlandish signature is
114 // used to emphasize that the exact function pointer type doesn't matter.
115 using FuncPtr = Payload***** (*)(Payload*****);
118 * An adaptor function (used by this class's function call operator) that
119 * invokes the callable in |mPayload|, forwarding arguments and converting
120 * return type as needed.
122 const Adaptor mAdaptor;
124 /** Storage for the wrapped callable value. */
125 union Payload {
126 // This arm is used if |FunctionRef| is passed a compatible function pointer
127 // or a lambda/callable that converts to a compatible function pointer.
128 FuncPtr mFuncPtr;
130 // This arm is used if |FunctionRef| is passed some other callable or
131 // |nullptr|.
132 void* mObject;
133 } mPayload;
135 template <typename RealFuncPtr>
136 static Ret CallFunctionPointer(const Payload& aPayload,
137 Params... aParams) noexcept {
138 auto func = reinterpret_cast<RealFuncPtr>(aPayload.mFuncPtr);
139 return static_cast<Ret>(func(std::forward<Params>(aParams)...));
142 template <typename Ret2, typename... Params2>
143 FunctionRef(detail::MatchingFunctionPointerTag, Ret2 (*aFuncPtr)(Params2...))
144 : mAdaptor(&CallFunctionPointer<Ret2 (*)(Params2...)>) {
145 ::new (KnownNotNull, &mPayload.mFuncPtr)
146 FuncPtr(reinterpret_cast<FuncPtr>(aFuncPtr));
149 public:
151 * Construct a |FunctionRef| that's like a null function pointer that can't be
152 * called.
154 MOZ_IMPLICIT FunctionRef(std::nullptr_t) noexcept : mAdaptor(nullptr) {
155 // This is technically unnecessary, but it seems best to always initialize
156 // a union arm.
157 ::new (KnownNotNull, &mPayload.mObject) void*(nullptr);
160 FunctionRef() : FunctionRef(nullptr) {}
163 * Constructs a |FunctionRef| from an object callable with |Params| arguments,
164 * that returns a type convertible to |Ret|, where the callable isn't
165 * convertible to function pointer (often because it contains some internal
166 * state). For example:
168 * int x = 5;
169 * DoSomething([&x] { x++; });
171 template <typename Callable,
172 typename = detail::EnableFunctionTag<detail::MatchingFunctorTag,
173 Callable, Ret, Params...>,
174 typename std::enable_if_t<!std::is_same_v<
175 std::remove_cv_t<std::remove_reference_t<Callable>>,
176 FunctionRef>>* = nullptr>
177 MOZ_IMPLICIT FunctionRef(Callable&& aCallable MOZ_LIFETIME_BOUND) noexcept
178 : mAdaptor([](const Payload& aPayload, Params... aParams) {
179 auto& func = *static_cast<std::remove_reference_t<Callable>*>(aPayload.mObject);
180 return static_cast<Ret>(func(std::forward<Params>(aParams)...));
181 }) {
182 ::new (KnownNotNull, &mPayload.mObject) void*(&aCallable);
186 * Constructs a |FunctionRef| from an value callable with |Params| arguments,
187 * that returns a type convertible to |Ret|, where the callable is stateless
188 * and is (or is convertible to) a function pointer. For example:
190 * // Exact match
191 * double twice(double d) { return d * 2; }
192 * FunctionRef<double(double)> func1(&twice);
194 * // Compatible match
195 * float thrice(long double d) { return static_cast<float>(d) * 3; }
196 * FunctionRef<double(double)> func2(&thrice);
198 * // Non-generic lambdas that don't capture anything have a conversion
199 * // function to the appropriate function pointer type.
200 * FunctionRef<int(double)> f([](long double){ return 'c'; });
202 template <typename Callable,
203 typename = detail::EnableFunctionTag<
204 detail::MatchingFunctionPointerTag, Callable, Ret, Params...>>
205 MOZ_IMPLICIT FunctionRef(const Callable& aCallable) noexcept
206 : FunctionRef(detail::MatchingFunctionPointerTag{}, +aCallable) {}
208 /** Call the callable stored in this with the given arguments. */
209 Ret operator()(Params... params) const {
210 return mAdaptor(mPayload, std::forward<Params>(params)...);
213 /** Return true iff this wasn't created from |nullptr|. */
214 explicit operator bool() const noexcept { return mAdaptor != nullptr; }
217 } /* namespace mozilla */
219 #endif /* mozilla_FunctionRef_h */