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 136