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 <array> |
| 20 | #include <cstddef> |
| 21 | #include <cstdint> |
| 22 | #include <cstring> |
| 23 | #include <type_traits> |
| 24 | |
| 25 | namespace android::ftl::details { |
| 26 | |
| 27 | // The maximum allowed value for the template argument `N` in |
| 28 | // `ftl::Function<F, N>`. |
| 29 | constexpr size_t kFunctionMaximumN = 14; |
| 30 | |
| 31 | // Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member |
| 32 | // function type `Ret(Args...)`. |
| 33 | |
| 34 | template <typename> |
| 35 | struct remove_member_function_pointer; |
| 36 | |
| 37 | template <typename Class, typename Ret, typename... Args> |
| 38 | struct remove_member_function_pointer<Ret (Class::*)(Args...)> { |
| 39 | using type = Ret(Args...); |
| 40 | }; |
| 41 | |
| 42 | template <typename Class, typename Ret, typename... Args> |
| 43 | struct remove_member_function_pointer<Ret (Class::*)(Args...) const> { |
| 44 | using type = Ret(Args...); |
| 45 | }; |
| 46 | |
| 47 | template <auto MemberFunction> |
| 48 | using remove_member_function_pointer_t = |
| 49 | typename remove_member_function_pointer<decltype(MemberFunction)>::type; |
| 50 | |
| 51 | // Helper functions for binding to the supported targets. |
| 52 | |
| 53 | template <typename Ret, typename... Args> |
| 54 | auto bind_opaque_no_op() -> Ret (*)(void*, Args...) { |
| 55 | return [](void*, Args...) -> Ret { |
| 56 | if constexpr (!std::is_void_v<Ret>) { |
| 57 | return Ret{}; |
| 58 | } |
| 59 | }; |
| 60 | } |
| 61 | |
| 62 | template <typename F, typename Ret, typename... Args> |
| 63 | auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) { |
| 64 | return [](void* opaque, Args... args) -> Ret { |
| 65 | return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...); |
| 66 | }; |
| 67 | } |
| 68 | |
| 69 | template <auto MemberFunction, typename Class, typename Ret, typename... Args> |
| 70 | auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) { |
| 71 | return [instance](Args... args) -> Ret { |
| 72 | return std::invoke(MemberFunction, instance, std::forward<Args>(args)...); |
| 73 | }; |
| 74 | } |
| 75 | |
| 76 | template <auto FreeFunction, typename Ret, typename... Args> |
| 77 | auto bind_free_function(Ret (*)(Args...) = nullptr) { |
| 78 | return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); }; |
| 79 | } |
| 80 | |
| 81 | // Traits class for the opaque storage used by Function. |
| 82 | |
| 83 | template <std::size_t N> |
| 84 | struct function_opaque_storage { |
| 85 | // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, |
| 86 | // which allows a lambda with zero or one capture args. |
| 87 | using type = std::array<std::intptr_t, N + 1>; |
| 88 | |
| 89 | template <typename S> |
| 90 | static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>; |
| 91 | |
| 92 | template <typename S> |
| 93 | static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>; |
| 94 | |
| 95 | template <typename S> |
| 96 | static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); |
| 97 | |
| 98 | template <typename S> |
| 99 | static constexpr bool require_alignment_compatible = |
| 100 | std::alignment_of_v<S> <= std::alignment_of_v<type>; |
| 101 | |
| 102 | // Copies `src` into the opaque storage, and returns that storage. |
| 103 | template <typename S> |
| 104 | static type opaque_copy(const S& src) { |
| 105 | // TODO: Replace with C++20 concepts/constraints which can give more details. |
| 106 | static_assert(require_trivially_copyable<S>, |
| 107 | "ftl::Function can only store lambdas that capture trivially copyable data."); |
| 108 | static_assert( |
| 109 | require_trivially_destructible<S>, |
| 110 | "ftl::Function can only store lambdas that capture trivially destructible data."); |
| 111 | static_assert(require_will_fit_in_opaque_storage<S>, |
| 112 | "ftl::Function has limited storage for lambda captured state. Maybe you need to " |
| 113 | "increase N?"); |
| 114 | static_assert(require_alignment_compatible<S>); |
| 115 | |
| 116 | type opaque; |
| 117 | std::memcpy(opaque.data(), &src, sizeof(S)); |
| 118 | return opaque; |
| 119 | } |
| 120 | }; |
| 121 | |
| 122 | // Traits class to help determine the template parameters to use for a ftl::Function, given a |
| 123 | // function object. |
| 124 | |
| 125 | template <typename F, typename = decltype(&F::operator())> |
| 126 | struct function_traits { |
| 127 | // The function type `F` with which to instantiate the `Function<F, N>` template. |
| 128 | using type = remove_member_function_pointer_t<&F::operator()>; |
| 129 | |
| 130 | // The (minimum) size `N` with which to instantiate the `Function<F, N>` template. |
| 131 | static constexpr std::size_t size = |
| 132 | (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); |
| 133 | }; |
| 134 | |
| 135 | } // namespace android::ftl::details |