1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <cstddef> 20 #include <functional> 21 #include <type_traits> 22 #include <utility> 23 24 #include <ftl/details/function.h> 25 26 namespace android::ftl { 27 28 // ftl::Function<F, N> is a container for function object, and can mostly be used in place of 29 // std::function<F>. 30 // 31 // Unlike std::function<F>, a ftl::Function<F, N>: 32 // 33 // * Uses a static amount of memory (controlled by N), and never any dynamic allocation. 34 // * Satisfies the std::is_trivially_copyable<> trait. 35 // * Satisfies the std::is_trivially_destructible<> trait. 36 // 37 // However those same limits are also required from the contained function object in turn. 38 // 39 // The size of a ftl::Function<F, N> is guaranteed to be: 40 // 41 // sizeof(std::intptr_t) * (N + 2) 42 // 43 // A ftl::Function<F, N> can always be implicitly converted to a larger size ftl::Function<F, M>. 44 // Trying to convert the other way leads to a compilation error. 45 // 46 // A default-constructed ftl::Function is in an empty state. The operator bool() overload returns 47 // false in this state. It is undefined behavior to attempt to invoke the function in this state. 48 // 49 // The ftl::Function<F, N> can also be constructed or assigned from ftl::no_op. This sets up the 50 // ftl::Function to be non-empty, with a function that when called does nothing except 51 // default-constructs a return value. 52 // 53 // The ftl::make_function() helpers construct a ftl::Function<F, N>, including deducing the 54 // values of F and N from the arguments it is given. 55 // 56 // The static ftl::Function<F, N>::make() helpers construct a ftl::Function<F, N> without that 57 // deduction, and also allow for implicit argument conversion if the target being called needs them. 58 // 59 // The construction helpers allow any of the following types of functions to be stored: 60 // 61 // * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small 62 // capture, or other "functor". The requirements are: 63 // 64 // 1) The function object must be trivial to destroy (in fact, the destructor will never 65 // actually be called once copied to the internal storage). 66 // 2) The function object must be trivial to copy (the raw bytes will be copied as the 67 // ftl::Function<F, N> is copied/moved). 68 // 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1), 69 // and it cannot require stricter alignment than alignof(std::intptr_t). 70 // 71 // With the default of N=0, a lambda can only capture a single pointer-sized argument. This is 72 // enough to capture `this`, which is why N=0 is the default. 73 // 74 // * A member function, with the address passed as the template value argument to the construction 75 // helper function, along with the instance pointer needed to invoke it passed as an ordinary 76 // argument. 77 // 78 // ftl::make_function<&Class::member_function>(this); 79 // 80 // Note that the indicated member function will be invoked non-virtually. If you need it to be 81 // invoked virtually, you should invoke it yourself with a small lambda like so: 82 // 83 // ftl::function([this] { virtual_member_function(); }); 84 // 85 // * An ordinary function ("free function"), with the address of the function passed as a template 86 // value argument. 87 // 88 // ftl::make_function<&std::atoi>(); 89 // 90 // As with the member function helper, as the function is known at compile time, it will be called 91 // directly. 92 // 93 // Example usage: 94 // 95 // class MyClass { 96 // public: 97 // void on_event() const {} 98 // int on_string(int*, std::string_view) { return 1; } 99 // 100 // auto get_function() { 101 // return ftl::function([this] { on_event(); }); 102 // } 103 // } cls; 104 // 105 // // A function container with no arguments, and returning no value. 106 // ftl::Function<void()> f; 107 // 108 // // Construct a ftl::Function containing a small lambda. 109 // f = cls.get_function(); 110 // 111 // // Construct a ftl::Function that calls `cls.on_event()`. 112 // f = ftl::function<&MyClass::on_event>(&cls); 113 // 114 // // Create a do-nothing function. 115 // f = ftl::no_op; 116 // 117 // // Invoke the contained function. 118 // f(); 119 // 120 // // Also invokes it. 121 // std::invoke(f); 122 // 123 // // Create a typedef to give a more meaningful name and bound the size. 124 // using MyFunction = ftl::Function<int(std::string_view), 2>; 125 // int* ptr = nullptr; 126 // auto f1 = MyFunction::make( 127 // [cls = &cls, ptr](std::string_view sv) { 128 // return cls->on_string(ptr, sv); 129 // }); 130 // int r = f1("abc"sv); 131 // 132 // // Returns a default-constructed int (0). 133 // f1 = ftl::no_op; 134 // r = f1("abc"sv); 135 // assert(r == 0); 136 137 template <typename F, std::size_t N = 0> 138 class Function; 139 140 // Used to construct a Function that does nothing. 141 struct NoOpTag {}; 142 143 constexpr NoOpTag no_op; 144 145 // Detects that a type is a `ftl::Function<F, N>` regardless of what `F` and `N` are. 146 template <typename> 147 struct is_function : public std::false_type {}; 148 149 template <typename F, std::size_t N> 150 struct is_function<Function<F, N>> : public std::true_type {}; 151 152 template <typename T> 153 constexpr bool is_function_v = is_function<T>::value; 154 155 template <typename Ret, typename... Args, std::size_t N> 156 class Function<Ret(Args...), N> final { 157 // Enforce a valid size, with an arbitrary maximum allowed size for the container of 158 // sizeof(std::intptr_t) * 16, though that maximum can be relaxed. 159 static_assert(N <= details::kFunctionMaximumN); 160 161 using OpaqueStorageTraits = details::function_opaque_storage<N>; 162 163 public: 164 // Defining result_type allows ftl::Function to be substituted for std::function. 165 using result_type = Ret; 166 167 // Constructs an empty ftl::Function. 168 Function() = default; 169 170 // Constructing or assigning from nullptr_t also creates an empty ftl::Function. 171 Function(std::nullptr_t) {} 172 Function& operator=(std::nullptr_t) { return *this = Function(nullptr); } 173 174 // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which 175 // returns a default constructed return value. 176 Function(NoOpTag) : function_(details::bind_opaque_no_op<Ret, Args...>()) {} 177 Function& operator=(NoOpTag) { return *this = Function(no_op); } 178 179 // Constructing/assigning from a function object stores a copy of that function object, however: 180 // * It must be trivially copyable, as the implementation makes a copy with memcpy(). 181 // * It must be trivially destructible, as the implementation doesn't destroy the copy! 182 // * It must fit in the limited internal storage, which enforces size/alignment restrictions. 183 184 template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>> 185 Function(const F& f) 186 : opaque_(OpaqueStorageTraits::opaque_copy(f)), 187 function_(details::bind_opaque_function_object<F, Ret, Args...>(f)) {} 188 189 template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>> 190 Function& operator=(const F& f) noexcept { 191 return *this = Function{OpaqueStorageTraits::opaque_copy(f), 192 details::bind_opaque_function_object<F, Ret, Args...>(f)}; 193 } 194 195 // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else. 196 197 template <std::size_t M> 198 Function(const Function<Ret(Args...), M>& other) 199 : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {} 200 201 template <std::size_t M> 202 auto& operator=(const Function<Ret(Args...), M>& other) { 203 return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_}; 204 } 205 206 // Returns true if a function is set. 207 explicit operator bool() const { return function_ != nullptr; } 208 209 // Checks if the other function has the same contents as this one. 210 bool operator==(const Function& other) const { 211 return other.opaque_ == opaque_ && other.function_ == function_; 212 } 213 bool operator!=(const Function& other) const { return !operator==(other); } 214 215 // Alternative way of testing for a function being set. 216 bool operator==(std::nullptr_t) const { return function_ == nullptr; } 217 bool operator!=(std::nullptr_t) const { return function_ != nullptr; } 218 219 // Invokes the function. 220 Ret operator()(Args... args) const { 221 return std::invoke(function_, opaque_.data(), std::forward<Args>(args)...); 222 } 223 224 // Creation helper for function objects, such as lambdas. 225 template <typename F> 226 static auto make(const F& f) -> decltype(Function{f}) { 227 return Function{f}; 228 } 229 230 // Creation helper for a class pointer and a compile-time chosen member function to call. 231 template <auto MemberFunction, typename Class> 232 static auto make(Class* instance) -> decltype(Function{ 233 details::bind_member_function<MemberFunction>(instance, 234 static_cast<Ret (*)(Args...)>(nullptr))}) { 235 return Function{details::bind_member_function<MemberFunction>( 236 instance, static_cast<Ret (*)(Args...)>(nullptr))}; 237 } 238 239 // Creation helper for a compile-time chosen free function to call. 240 template <auto FreeFunction> 241 static auto make() -> decltype(Function{ 242 details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}) { 243 return Function{ 244 details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}; 245 } 246 247 private: 248 // Needed so a Function<F, M> can be converted to a Function<F, N>. 249 template <typename, std::size_t> 250 friend class Function; 251 252 // The function pointer type of function stored in `function_`. The first argument is always 253 // `&opaque_`. 254 using StoredFunction = Ret(void*, Args...); 255 256 // The type of the opaque storage, used to hold an appropriate function object. 257 // The type stored here is ONLY known to the StoredFunction. 258 // We always use at least one std::intptr_t worth of storage, and always a multiple of that size. 259 using OpaqueStorage = typename OpaqueStorageTraits::type; 260 261 // Internal constructor for creating from a raw opaque blob + function pointer. 262 Function(const OpaqueStorage& opaque, StoredFunction* function) 263 : opaque_(opaque), function_(function) {} 264 265 // Note: `mutable` so that `operator() const` can use it. 266 mutable OpaqueStorage opaque_{}; 267 StoredFunction* function_{nullptr}; 268 }; 269 270 // Makes a ftl::Function given a function object `F`. 271 template <typename F, typename T = details::function_traits<F>> 272 Function(const F&) -> Function<typename T::type, T::size>; 273 274 template <typename F> 275 auto make_function(const F& f) -> decltype(Function{f}) { 276 return Function{f}; 277 } 278 279 // Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`. 280 template <auto MemberFunction, typename Class> 281 auto make_function(Class* instance) 282 -> decltype(Function{details::bind_member_function<MemberFunction>( 283 instance, 284 static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}) { 285 return Function{details::bind_member_function<MemberFunction>( 286 instance, static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}; 287 } 288 289 // Makes a ftl::Function given an ordinary free function. 290 template <auto FreeFunction> 291 auto make_function() -> decltype(Function{ 292 details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}) { 293 return Function{ 294 details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}; 295 } 296 297 } // namespace android::ftl 298