Yilong Li | 14a93e9 | 2021-05-04 18:37:19 -0700 | [diff] [blame^] | 1 | // Copyright 2021 The Android Open Source Project |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
| 14 | |
| 15 | // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| 16 | // Use of this source code is governed by a BSD-style license that can be |
| 17 | // found in the LICENSE file. |
| 18 | |
| 19 | #pragma once |
| 20 | |
| 21 | #include <stddef.h> |
| 22 | #include <stdlib.h> |
| 23 | |
| 24 | #include <memory> |
| 25 | |
| 26 | #include "Nullable.h" |
| 27 | |
| 28 | #include <new> |
| 29 | #include <type_traits> |
| 30 | #include <utility> |
| 31 | |
| 32 | namespace android::base { |
| 33 | namespace fit { |
| 34 | namespace internal { |
| 35 | |
| 36 | template <typename Result, typename... Args> |
| 37 | struct target_ops final { |
| 38 | const void* (*target_type_id)(void* bits, const void* impl_ops); |
| 39 | void* (*get)(void* bits); |
| 40 | Result (*invoke)(void* bits, Args... args); |
| 41 | void (*move)(void* from_bits, void* to_bits); |
| 42 | void (*destroy)(void* bits); |
| 43 | }; |
| 44 | |
| 45 | template <typename Callable, bool is_inline, bool is_shared, typename Result, typename... Args> |
| 46 | struct target; |
| 47 | |
| 48 | inline const void* unshared_target_type_id(void* bits, const void* impl_ops) { |
| 49 | return impl_ops; |
| 50 | } |
| 51 | |
| 52 | // vtable for nullptr (empty target function) |
| 53 | |
| 54 | template <typename Result, typename... Args> |
| 55 | struct target<decltype(nullptr), |
| 56 | /*is_inline=*/true, |
| 57 | /*is_shared=*/false, |
| 58 | Result, |
| 59 | Args...> |
| 60 | final { |
| 61 | static Result invoke(void* bits, Args... args) { __builtin_abort(); } |
| 62 | |
| 63 | static const target_ops<Result, Args...> ops; |
| 64 | }; |
| 65 | |
| 66 | inline void* null_target_get(void* bits) { |
| 67 | return nullptr; |
| 68 | } |
| 69 | inline void null_target_move(void* from_bits, void* to_bits) {} |
| 70 | inline void null_target_destroy(void* bits) {} |
| 71 | |
| 72 | template <typename Result, typename... Args> |
| 73 | constexpr target_ops<Result, Args...> target<decltype(nullptr), |
| 74 | /*is_inline=*/true, |
| 75 | /*is_shared=*/false, |
| 76 | Result, |
| 77 | Args...>::ops = { |
| 78 | &unshared_target_type_id, &null_target_get, &target::invoke, &null_target_move, |
| 79 | &null_target_destroy}; |
| 80 | |
| 81 | // vtable for inline target function |
| 82 | |
| 83 | template <typename Callable, typename Result, typename... Args> |
| 84 | struct target<Callable, |
| 85 | /*is_inline=*/true, |
| 86 | /*is_shared=*/false, |
| 87 | Result, |
| 88 | Args...> |
| 89 | final { |
| 90 | template <typename Callable_> |
| 91 | static void initialize(void* bits, Callable_&& target) { |
| 92 | new (bits) Callable(std::forward<Callable_>(target)); |
| 93 | } |
| 94 | static Result invoke(void* bits, Args... args) { |
| 95 | auto& target = *static_cast<Callable*>(bits); |
| 96 | return target(std::forward<Args>(args)...); |
| 97 | } |
| 98 | static void move(void* from_bits, void* to_bits) { |
| 99 | auto& from_target = *static_cast<Callable*>(from_bits); |
| 100 | new (to_bits) Callable(std::move(from_target)); |
| 101 | from_target.~Callable(); |
| 102 | } |
| 103 | static void destroy(void* bits) { |
| 104 | auto& target = *static_cast<Callable*>(bits); |
| 105 | target.~Callable(); |
| 106 | } |
| 107 | |
| 108 | static const target_ops<Result, Args...> ops; |
| 109 | }; |
| 110 | |
| 111 | inline void* inline_target_get(void* bits) { |
| 112 | return bits; |
| 113 | } |
| 114 | |
| 115 | template <typename Callable, typename Result, typename... Args> |
| 116 | constexpr target_ops<Result, Args...> target<Callable, |
| 117 | /*is_inline=*/true, |
| 118 | /*is_shared=*/false, |
| 119 | Result, |
| 120 | Args...>::ops = { |
| 121 | &unshared_target_type_id, &inline_target_get, &target::invoke, &target::move, &target::destroy}; |
| 122 | |
| 123 | // vtable for pointer to target function |
| 124 | |
| 125 | template <typename Callable, typename Result, typename... Args> |
| 126 | struct target<Callable, |
| 127 | /*is_inline=*/false, |
| 128 | /*is_shared=*/false, |
| 129 | Result, |
| 130 | Args...> |
| 131 | final { |
| 132 | template <typename Callable_> |
| 133 | static void initialize(void* bits, Callable_&& target) { |
| 134 | auto ptr = static_cast<Callable**>(bits); |
| 135 | *ptr = new Callable(std::forward<Callable_>(target)); |
| 136 | } |
| 137 | static Result invoke(void* bits, Args... args) { |
| 138 | auto& target = **static_cast<Callable**>(bits); |
| 139 | return target(std::forward<Args>(args)...); |
| 140 | } |
| 141 | static void move(void* from_bits, void* to_bits) { |
| 142 | auto from_ptr = static_cast<Callable**>(from_bits); |
| 143 | auto to_ptr = static_cast<Callable**>(to_bits); |
| 144 | *to_ptr = *from_ptr; |
| 145 | } |
| 146 | static void destroy(void* bits) { |
| 147 | auto ptr = static_cast<Callable**>(bits); |
| 148 | delete *ptr; |
| 149 | } |
| 150 | |
| 151 | static const target_ops<Result, Args...> ops; |
| 152 | }; |
| 153 | |
| 154 | inline void* heap_target_get(void* bits) { |
| 155 | return *static_cast<void**>(bits); |
| 156 | } |
| 157 | |
| 158 | template <typename Callable, typename Result, typename... Args> |
| 159 | constexpr target_ops<Result, Args...> target<Callable, |
| 160 | /*is_inline=*/false, |
| 161 | /*is_shared=*/false, |
| 162 | Result, |
| 163 | Args...>::ops = { |
| 164 | &unshared_target_type_id, &heap_target_get, &target::invoke, &target::move, &target::destroy}; |
| 165 | |
| 166 | // vtable for fit::function std::shared_ptr to target function |
| 167 | |
| 168 | template <typename SharedFunction> |
| 169 | const void* get_target_type_id(const SharedFunction& function_or_callback) { |
| 170 | return function_or_callback.target_type_id(); |
| 171 | } |
| 172 | |
| 173 | // For this vtable, |
| 174 | // Callable by definition will be either a fit::function or fit::callback |
| 175 | template <typename SharedFunction, typename Result, typename... Args> |
| 176 | struct target<SharedFunction, |
| 177 | /*is_inline=*/false, |
| 178 | /*is_shared=*/true, |
| 179 | Result, |
| 180 | Args...> |
| 181 | final { |
| 182 | static void initialize(void* bits, SharedFunction target) { |
| 183 | new (bits) std::shared_ptr<SharedFunction>( |
| 184 | std::move(std::make_shared<SharedFunction>(std::move(target)))); |
| 185 | } |
| 186 | static void copy_shared_ptr(void* from_bits, void* to_bits) { |
| 187 | auto& from_shared_ptr = *static_cast<std::shared_ptr<SharedFunction>*>(from_bits); |
| 188 | new (to_bits) std::shared_ptr<SharedFunction>(from_shared_ptr); |
| 189 | } |
| 190 | static const void* target_type_id(void* bits, const void* impl_ops) { |
| 191 | auto& function_or_callback = **static_cast<std::shared_ptr<SharedFunction>*>(bits); |
| 192 | return ::android::base::fit::internal::get_target_type_id(function_or_callback); |
| 193 | } |
| 194 | static void* get(void* bits) { |
| 195 | auto& function_or_callback = **static_cast<std::shared_ptr<SharedFunction>*>(bits); |
| 196 | return function_or_callback.template target<SharedFunction>( |
| 197 | /*check=*/false); // void* will fail the check |
| 198 | } |
| 199 | static Result invoke(void* bits, Args... args) { |
| 200 | auto& function_or_callback = **static_cast<std::shared_ptr<SharedFunction>*>(bits); |
| 201 | return function_or_callback(std::forward<Args>(args)...); |
| 202 | } |
| 203 | static void move(void* from_bits, void* to_bits) { |
| 204 | auto from_shared_ptr = std::move(*static_cast<std::shared_ptr<SharedFunction>*>(from_bits)); |
| 205 | new (to_bits) std::shared_ptr<SharedFunction>(std::move(from_shared_ptr)); |
| 206 | } |
| 207 | static void destroy(void* bits) { |
| 208 | static_cast<std::shared_ptr<SharedFunction>*>(bits)->reset(); |
| 209 | } |
| 210 | |
| 211 | static const target_ops<Result, Args...> ops; |
| 212 | }; |
| 213 | |
| 214 | template <typename SharedFunction, typename Result, typename... Args> |
| 215 | constexpr target_ops<Result, Args...> target<SharedFunction, |
| 216 | /*is_inline=*/false, |
| 217 | /*is_shared=*/true, |
| 218 | Result, |
| 219 | Args...>::ops = { |
| 220 | &target::target_type_id, &target::get, &target::invoke, &target::move, &target::destroy}; |
| 221 | |
| 222 | template <size_t inline_target_size, bool requireInline, typename Result, typename... Args> |
| 223 | class function_base; |
| 224 | |
| 225 | // Function implementation details. |
| 226 | // See |fit::function| and |fit::callback| documentation for more information. |
| 227 | template <size_t inline_target_size, bool requireInline, typename Result, typename... Args> |
| 228 | class function_base<inline_target_size, requireInline, Result(Args...)> { |
| 229 | using ops_type = const target_ops<Result, Args...>*; |
| 230 | using storage_type = typename std::aligned_storage<( |
| 231 | inline_target_size >= sizeof(void*) ? inline_target_size : sizeof(void*))>:: |
| 232 | type; // avoid including <algorithm> for max |
| 233 | template <typename Callable> |
| 234 | using target_type = target<Callable, |
| 235 | (sizeof(Callable) <= sizeof(storage_type)), |
| 236 | /*is_shared=*/false, |
| 237 | Result, |
| 238 | Args...>; |
| 239 | template <typename SharedFunction> |
| 240 | using shared_target_type = target<SharedFunction, |
| 241 | /*is_inline=*/false, |
| 242 | /*is_shared=*/true, |
| 243 | Result, |
| 244 | Args...>; |
| 245 | using null_target_type = target_type<decltype(nullptr)>; |
| 246 | |
| 247 | protected: |
| 248 | using result_type = Result; |
| 249 | |
| 250 | function_base() { initialize_null_target(); } |
| 251 | |
| 252 | function_base(decltype(nullptr)) { initialize_null_target(); } |
| 253 | |
| 254 | function_base(Result (*target)(Args...)) { initialize_target(target); } |
| 255 | |
| 256 | template <typename Callable, |
| 257 | typename = std::enable_if_t< |
| 258 | std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)), |
| 259 | result_type>::value>> |
| 260 | function_base(Callable&& target) { |
| 261 | initialize_target(std::forward<Callable>(target)); |
| 262 | } |
| 263 | |
| 264 | function_base(function_base&& other) { move_target_from(std::move(other)); } |
| 265 | |
| 266 | ~function_base() { destroy_target(); } |
| 267 | |
| 268 | // Returns true if the function has a non-empty target. |
| 269 | explicit operator bool() const { return ops_->get(&bits_) != nullptr; } |
| 270 | |
| 271 | // Returns a pointer to the function's target. |
| 272 | // If |check| is true (the default), the function _may_ abort if the |
| 273 | // caller tries to assign the target to a varible of the wrong type. (This |
| 274 | // check is currently skipped for share()d objects.) |
| 275 | // Note the shared pointer vtable must set |check| to false to assign the |
| 276 | // target to |void*|. |
| 277 | template <typename Callable> |
| 278 | Callable* target(bool check = true) { |
| 279 | if (check) |
| 280 | check_target_type<Callable>(); |
| 281 | return static_cast<Callable*>(ops_->get(&bits_)); |
| 282 | } |
| 283 | |
| 284 | // Returns a pointer to the function's target (const version). |
| 285 | // If |check| is true (the default), the function _may_ abort if the |
| 286 | // caller tries to assign the target to a varible of the wrong type. (This |
| 287 | // check is currently skipped for share()d objects.) |
| 288 | // Note the shared pointer vtable must set |check| to false to assign the |
| 289 | // target to |void*|. |
| 290 | template <typename Callable> |
| 291 | const Callable* target(bool check = true) const { |
| 292 | if (check) |
| 293 | check_target_type<Callable>(); |
| 294 | return static_cast<Callable*>(ops_->get(&bits_)); |
| 295 | } |
| 296 | |
| 297 | // Used by the derived "impl" classes to implement share(). |
| 298 | // |
| 299 | // The caller creates a new object of the same type as itself, and passes in |
| 300 | // the empty object. This function first checks if |this| is already shared, |
| 301 | // and if not, creates a new version of itself containing a |
| 302 | // |std::shared_ptr| to its original self, and updates |ops_| to the vtable |
| 303 | // for the shared version. |
| 304 | // |
| 305 | // Then it copies its |shared_ptr| to the |bits_| of the given |copy|, |
| 306 | // and assigns the same shared pointer vtable to the copy's |ops_|. |
| 307 | // |
| 308 | // The target itself is not copied; it is moved to the heap and its |
| 309 | // lifetime is extended until all references have been released. |
| 310 | // |
| 311 | // Note: This method is not supported on |fit::InlineFunction<>| |
| 312 | // because it may incur a heap allocation which is contrary to |
| 313 | // the stated purpose of |fit::InlineFunction<>|. |
| 314 | template <typename SharedFunction> |
| 315 | void share_with(SharedFunction& copy) { |
| 316 | static_assert(!requireInline, "Inline functions cannot be shared."); |
| 317 | if (ops_->get(&bits_) != nullptr) { |
| 318 | if (ops_ != &shared_target_type<SharedFunction>::ops) { |
| 319 | convert_to_shared_target<SharedFunction>(); |
| 320 | } |
| 321 | copy_shared_target_to(copy); |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | // Used by derived "impl" classes to implement operator()(). |
| 326 | // Invokes the function's target. |
| 327 | // Note that fit::callback will release the target immediately after |
| 328 | // invoke() (also affecting any share()d copies). |
| 329 | // Aborts if the function's target is empty. |
| 330 | Result invoke(Args... args) const { return ops_->invoke(&bits_, std::forward<Args>(args)...); } |
| 331 | |
| 332 | // Used by derived "impl" classes to implement operator=(). |
| 333 | // Assigns an empty target. |
| 334 | void assign(decltype(nullptr)) { |
| 335 | destroy_target(); |
| 336 | initialize_null_target(); |
| 337 | } |
| 338 | |
| 339 | // Used by derived "impl" classes to implement operator=(). |
| 340 | // Assigns the function's target. |
| 341 | // If target == nullptr, assigns an empty target. |
| 342 | template <typename Callable, |
| 343 | typename = std::enable_if_t< |
| 344 | std::is_convertible<decltype(std::declval<Callable&>()(std::declval<Args>()...)), |
| 345 | result_type>::value>> |
| 346 | void assign(Callable&& target) { |
| 347 | destroy_target(); |
| 348 | initialize_target(std::forward<Callable>(target)); |
| 349 | } |
| 350 | |
| 351 | // Used by derived "impl" classes to implement operator=(). |
| 352 | // Assigns the function with a target moved from another function, |
| 353 | // leaving the other function with an empty target. |
| 354 | void assign(function_base&& other) { |
| 355 | destroy_target(); |
| 356 | move_target_from(std::move(other)); |
| 357 | } |
| 358 | |
| 359 | void swap(function_base& other) { |
| 360 | if (&other == this) |
| 361 | return; |
| 362 | ops_type temp_ops = ops_; |
| 363 | storage_type temp_bits; |
| 364 | ops_->move(&bits_, &temp_bits); |
| 365 | |
| 366 | ops_ = other.ops_; |
| 367 | other.ops_->move(&other.bits_, &bits_); |
| 368 | |
| 369 | other.ops_ = temp_ops; |
| 370 | temp_ops->move(&temp_bits, &other.bits_); |
| 371 | } |
| 372 | |
| 373 | // returns an opaque ID unique to the |Callable| type of the target. |
| 374 | // Used by check_target_type. |
| 375 | const void* target_type_id() const { return ops_->target_type_id(&bits_, ops_); } |
| 376 | |
| 377 | // Deleted copy constructor and assign. |function_base| implementations are |
| 378 | // move-only. |
| 379 | function_base(const function_base& other) = delete; |
| 380 | function_base& operator=(const function_base& other) = delete; |
| 381 | |
| 382 | // Move assignment must be provided by subclasses. |
| 383 | function_base& operator=(function_base&& other) = delete; |
| 384 | |
| 385 | private: |
| 386 | // Implements the move operation, used by move construction and move |
| 387 | // assignment. Leaves other target initialized to null. |
| 388 | void move_target_from(function_base&& other) { |
| 389 | ops_ = other.ops_; |
| 390 | other.ops_->move(&other.bits_, &bits_); |
| 391 | other.initialize_null_target(); |
| 392 | } |
| 393 | |
| 394 | // fit::function and fit::callback are not directly copyable, but share() |
| 395 | // will create shared references to the original object. This method |
| 396 | // implements the copy operation for the |std::shared_ptr| wrapper. |
| 397 | template <typename SharedFunction> |
| 398 | void copy_shared_target_to(SharedFunction& copy) { |
| 399 | copy.destroy_target(); |
| 400 | assert(ops_ == &shared_target_type<SharedFunction>::ops); |
| 401 | shared_target_type<SharedFunction>::copy_shared_ptr(&bits_, ©.bits_); |
| 402 | copy.ops_ = ops_; |
| 403 | } |
| 404 | |
| 405 | // assumes target is uninitialized |
| 406 | void initialize_null_target() { ops_ = &null_target_type::ops; } |
| 407 | |
| 408 | // target may or may not be initialized. |
| 409 | template <typename Callable> |
| 410 | void initialize_target(Callable&& target) { |
| 411 | // Convert function or function references to function pointer. |
| 412 | using DecayedCallable = std::decay_t<Callable>; |
| 413 | static_assert( |
| 414 | std::alignment_of<DecayedCallable>::value <= std::alignment_of<storage_type>::value, |
| 415 | "Alignment of Callable must be <= alignment of max_align_t."); |
| 416 | static_assert(!requireInline || sizeof(DecayedCallable) <= inline_target_size, |
| 417 | "Callable too large to store inline as requested."); |
| 418 | if (is_null(target)) { |
| 419 | initialize_null_target(); |
| 420 | } else { |
| 421 | ops_ = &target_type<DecayedCallable>::ops; |
| 422 | target_type<DecayedCallable>::initialize(&bits_, std::forward<Callable>(target)); |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | // assumes target is uninitialized |
| 427 | template <typename SharedFunction> |
| 428 | void convert_to_shared_target() { |
| 429 | shared_target_type<SharedFunction>::initialize( |
| 430 | &bits_, std::move(*static_cast<SharedFunction*>(this))); |
| 431 | ops_ = &shared_target_type<SharedFunction>::ops; |
| 432 | } |
| 433 | |
| 434 | // leaves target uninitialized |
| 435 | void destroy_target() { ops_->destroy(&bits_); } |
| 436 | |
| 437 | // Called by target() if |check| is true. |
| 438 | // Checks the template parameter, usually inferred from the context of |
| 439 | // the call to target(), and aborts the program if it can determine that |
| 440 | // the Callable type is not compatible with the function's Result and Args. |
| 441 | template <typename Callable> |
| 442 | void check_target_type() const { |
| 443 | if (target_type<Callable>::ops.target_type_id(nullptr, &target_type<Callable>::ops) != |
| 444 | target_type_id()) { |
| 445 | __builtin_abort(); |
| 446 | } |
| 447 | } |
| 448 | |
| 449 | ops_type ops_; |
| 450 | mutable storage_type bits_; |
| 451 | }; |
| 452 | |
| 453 | } // namespace internal |
| 454 | |
| 455 | } // namespace fit |
| 456 | } // namespace android::base |