Lloyd Pique | ace9af9 | 2022-11-09 13:56:40 -0800 | [diff] [blame] | 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; |
Dominik Laskowski | 021ab8c | 2024-06-10 12:32:34 -0400 | [diff] [blame^] | 126 | // auto f1 = MyFunction::make( |
Lloyd Pique | ace9af9 | 2022-11-09 13:56:40 -0800 | [diff] [blame] | 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 |