FunctionRef.h (8983B)
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/. */ 6 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). 11 */ 12 13 #ifndef mozilla_FunctionRef_h 14 #define mozilla_FunctionRef_h 15 16 #include "mozilla/OperatorNewExtensions.h" // mozilla::NotNull, ::operator new 17 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 21 22 // This concept and its implementation are substantially inspired by foonathan's 23 // prior art: 24 // 25 // https://foonathan.net/2017/01/function-ref-implementation/ 26 // https://github.com/foonathan/type_safe/blob/2017851053f8dd268372f1612865792c5c621570/include/type_safe/reference.hpp 27 28 namespace mozilla { 29 30 namespace detail { 31 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>>; 39 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>; 47 48 struct MatchingFunctionPointerTag {}; 49 struct MatchingFunctorTag {}; 50 struct InvalidFunctorTag {}; 51 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); 61 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); 68 69 // Match all remaining cases. (Any other match is preferred to an ellipsis 70 // match.) 71 static InvalidFunctorTag test(...); 72 73 using Type = decltype(test(0, std::declval<Callable&>())); 74 }; 75 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...> {}; 80 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>; 86 87 } // namespace detail 88 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. 93 * 94 * This class does not own the callable, so in general it is unsafe to store a 95 * FunctionRef. 96 */ 97 template <typename Fn> 98 class MOZ_TEMPORARY_CLASS FunctionRef; 99 100 template <typename Ret, typename... Params> 101 class MOZ_TEMPORARY_CLASS FunctionRef<Ret(Params...)> { 102 union Payload; 103 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); 108 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*****); 116 117 /** 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. 121 */ 122 const Adaptor mAdaptor; 123 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; 129 130 // This arm is used if |FunctionRef| is passed some other callable or 131 // |nullptr|. 132 void* mObject; 133 } mPayload; 134 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)...)); 140 } 141 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)); 147 } 148 149 public: 150 /** 151 * Construct a |FunctionRef| that's like a null function pointer that can't be 152 * called. 153 */ 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); 158 } 159 160 FunctionRef() : FunctionRef(nullptr) {} 161 162 /** 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: 167 * 168 * int x = 5; 169 * DoSomething([&x] { x++; }); 170 */ 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>*>( 180 aPayload.mObject); 181 return static_cast<Ret>(func(std::forward<Params>(aParams)...)); 182 }) { 183 ::new (KnownNotNull, &mPayload.mObject) void*(&aCallable); 184 } 185 186 /** 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: 190 * 191 * // Exact match 192 * double twice(double d) { return d * 2; } 193 * FunctionRef<double(double)> func1(&twice); 194 * 195 * // Compatible match 196 * float thrice(long double d) { return static_cast<float>(d) * 3; } 197 * FunctionRef<double(double)> func2(&thrice); 198 * 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'; }); 202 */ 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) {} 208 209 /** Call the callable stored in this with the given arguments. */ 210 Ret operator()(Params... params) const { 211 return mAdaptor(mPayload, std::forward<Params>(params)...); 212 } 213 214 /** Return true iff this wasn't created from |nullptr|. */ 215 explicit operator bool() const noexcept { return mAdaptor != nullptr; } 216 }; 217 218 } /* namespace mozilla */ 219 220 #endif /* mozilla_FunctionRef_h */