1*38e8c45fSAndroid Build Coastguard Worker /* 2*38e8c45fSAndroid Build Coastguard Worker * Copyright 2022 The Android Open Source Project 3*38e8c45fSAndroid Build Coastguard Worker * 4*38e8c45fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*38e8c45fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*38e8c45fSAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*38e8c45fSAndroid Build Coastguard Worker * 8*38e8c45fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*38e8c45fSAndroid Build Coastguard Worker * 10*38e8c45fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*38e8c45fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*38e8c45fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*38e8c45fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*38e8c45fSAndroid Build Coastguard Worker * limitations under the License. 15*38e8c45fSAndroid Build Coastguard Worker */ 16*38e8c45fSAndroid Build Coastguard Worker 17*38e8c45fSAndroid Build Coastguard Worker #pragma once 18*38e8c45fSAndroid Build Coastguard Worker 19*38e8c45fSAndroid Build Coastguard Worker #include <array> 20*38e8c45fSAndroid Build Coastguard Worker #include <cstddef> 21*38e8c45fSAndroid Build Coastguard Worker #include <cstdint> 22*38e8c45fSAndroid Build Coastguard Worker #include <cstring> 23*38e8c45fSAndroid Build Coastguard Worker #include <type_traits> 24*38e8c45fSAndroid Build Coastguard Worker 25*38e8c45fSAndroid Build Coastguard Worker namespace android::ftl::details { 26*38e8c45fSAndroid Build Coastguard Worker 27*38e8c45fSAndroid Build Coastguard Worker // The maximum allowed value for the template argument `N` in 28*38e8c45fSAndroid Build Coastguard Worker // `ftl::Function<F, N>`. 29*38e8c45fSAndroid Build Coastguard Worker constexpr size_t kFunctionMaximumN = 14; 30*38e8c45fSAndroid Build Coastguard Worker 31*38e8c45fSAndroid Build Coastguard Worker // Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member 32*38e8c45fSAndroid Build Coastguard Worker // function type `Ret(Args...)`. 33*38e8c45fSAndroid Build Coastguard Worker 34*38e8c45fSAndroid Build Coastguard Worker template <typename> 35*38e8c45fSAndroid Build Coastguard Worker struct remove_member_function_pointer; 36*38e8c45fSAndroid Build Coastguard Worker 37*38e8c45fSAndroid Build Coastguard Worker template <typename Class, typename Ret, typename... Args> 38*38e8c45fSAndroid Build Coastguard Worker struct remove_member_function_pointer<Ret (Class::*)(Args...)> { 39*38e8c45fSAndroid Build Coastguard Worker using type = Ret(Args...); 40*38e8c45fSAndroid Build Coastguard Worker }; 41*38e8c45fSAndroid Build Coastguard Worker 42*38e8c45fSAndroid Build Coastguard Worker template <typename Class, typename Ret, typename... Args> 43*38e8c45fSAndroid Build Coastguard Worker struct remove_member_function_pointer<Ret (Class::*)(Args...) const> { 44*38e8c45fSAndroid Build Coastguard Worker using type = Ret(Args...); 45*38e8c45fSAndroid Build Coastguard Worker }; 46*38e8c45fSAndroid Build Coastguard Worker 47*38e8c45fSAndroid Build Coastguard Worker template <auto MemberFunction> 48*38e8c45fSAndroid Build Coastguard Worker using remove_member_function_pointer_t = 49*38e8c45fSAndroid Build Coastguard Worker typename remove_member_function_pointer<decltype(MemberFunction)>::type; 50*38e8c45fSAndroid Build Coastguard Worker 51*38e8c45fSAndroid Build Coastguard Worker // Helper functions for binding to the supported targets. 52*38e8c45fSAndroid Build Coastguard Worker 53*38e8c45fSAndroid Build Coastguard Worker template <typename Ret, typename... Args> 54*38e8c45fSAndroid Build Coastguard Worker auto bind_opaque_no_op() -> Ret (*)(void*, Args...) { 55*38e8c45fSAndroid Build Coastguard Worker return [](void*, Args...) -> Ret { 56*38e8c45fSAndroid Build Coastguard Worker if constexpr (!std::is_void_v<Ret>) { 57*38e8c45fSAndroid Build Coastguard Worker return Ret{}; 58*38e8c45fSAndroid Build Coastguard Worker } 59*38e8c45fSAndroid Build Coastguard Worker }; 60*38e8c45fSAndroid Build Coastguard Worker } 61*38e8c45fSAndroid Build Coastguard Worker 62*38e8c45fSAndroid Build Coastguard Worker template <typename F, typename Ret, typename... Args> 63*38e8c45fSAndroid Build Coastguard Worker auto bind_opaque_function_object(const F&) -> Ret (*)(void*, Args...) { 64*38e8c45fSAndroid Build Coastguard Worker return [](void* opaque, Args... args) -> Ret { 65*38e8c45fSAndroid Build Coastguard Worker return std::invoke(*static_cast<F*>(opaque), std::forward<Args>(args)...); 66*38e8c45fSAndroid Build Coastguard Worker }; 67*38e8c45fSAndroid Build Coastguard Worker } 68*38e8c45fSAndroid Build Coastguard Worker 69*38e8c45fSAndroid Build Coastguard Worker template <auto MemberFunction, typename Class, typename Ret, typename... Args> 70*38e8c45fSAndroid Build Coastguard Worker auto bind_member_function(Class* instance, Ret (*)(Args...) = nullptr) { 71*38e8c45fSAndroid Build Coastguard Worker return [instance](Args... args) -> Ret { 72*38e8c45fSAndroid Build Coastguard Worker return std::invoke(MemberFunction, instance, std::forward<Args>(args)...); 73*38e8c45fSAndroid Build Coastguard Worker }; 74*38e8c45fSAndroid Build Coastguard Worker } 75*38e8c45fSAndroid Build Coastguard Worker 76*38e8c45fSAndroid Build Coastguard Worker template <auto FreeFunction, typename Ret, typename... Args> 77*38e8c45fSAndroid Build Coastguard Worker auto bind_free_function(Ret (*)(Args...) = nullptr) { 78*38e8c45fSAndroid Build Coastguard Worker return [](Args... args) -> Ret { return std::invoke(FreeFunction, std::forward<Args>(args)...); }; 79*38e8c45fSAndroid Build Coastguard Worker } 80*38e8c45fSAndroid Build Coastguard Worker 81*38e8c45fSAndroid Build Coastguard Worker // Traits class for the opaque storage used by Function. 82*38e8c45fSAndroid Build Coastguard Worker 83*38e8c45fSAndroid Build Coastguard Worker template <std::size_t N> 84*38e8c45fSAndroid Build Coastguard Worker struct function_opaque_storage { 85*38e8c45fSAndroid Build Coastguard Worker // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, 86*38e8c45fSAndroid Build Coastguard Worker // which allows a lambda with zero or one capture args. 87*38e8c45fSAndroid Build Coastguard Worker using type = std::array<std::intptr_t, N + 1>; 88*38e8c45fSAndroid Build Coastguard Worker 89*38e8c45fSAndroid Build Coastguard Worker template <typename S> 90*38e8c45fSAndroid Build Coastguard Worker static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v<S>; 91*38e8c45fSAndroid Build Coastguard Worker 92*38e8c45fSAndroid Build Coastguard Worker template <typename S> 93*38e8c45fSAndroid Build Coastguard Worker static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v<S>; 94*38e8c45fSAndroid Build Coastguard Worker 95*38e8c45fSAndroid Build Coastguard Worker template <typename S> 96*38e8c45fSAndroid Build Coastguard Worker static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); 97*38e8c45fSAndroid Build Coastguard Worker 98*38e8c45fSAndroid Build Coastguard Worker template <typename S> 99*38e8c45fSAndroid Build Coastguard Worker static constexpr bool require_alignment_compatible = 100*38e8c45fSAndroid Build Coastguard Worker std::alignment_of_v<S> <= std::alignment_of_v<type>; 101*38e8c45fSAndroid Build Coastguard Worker 102*38e8c45fSAndroid Build Coastguard Worker // Copies `src` into the opaque storage, and returns that storage. 103*38e8c45fSAndroid Build Coastguard Worker template <typename S> 104*38e8c45fSAndroid Build Coastguard Worker static type opaque_copy(const S& src) { 105*38e8c45fSAndroid Build Coastguard Worker // TODO: Replace with C++20 concepts/constraints which can give more details. 106*38e8c45fSAndroid Build Coastguard Worker static_assert(require_trivially_copyable<S>, 107*38e8c45fSAndroid Build Coastguard Worker "ftl::Function can only store lambdas that capture trivially copyable data."); 108*38e8c45fSAndroid Build Coastguard Worker static_assert( 109*38e8c45fSAndroid Build Coastguard Worker require_trivially_destructible<S>, 110*38e8c45fSAndroid Build Coastguard Worker "ftl::Function can only store lambdas that capture trivially destructible data."); 111*38e8c45fSAndroid Build Coastguard Worker static_assert(require_will_fit_in_opaque_storage<S>, 112*38e8c45fSAndroid Build Coastguard Worker "ftl::Function has limited storage for lambda captured state. Maybe you need to " 113*38e8c45fSAndroid Build Coastguard Worker "increase N?"); 114*38e8c45fSAndroid Build Coastguard Worker static_assert(require_alignment_compatible<S>); 115*38e8c45fSAndroid Build Coastguard Worker 116*38e8c45fSAndroid Build Coastguard Worker type opaque; 117*38e8c45fSAndroid Build Coastguard Worker std::memcpy(opaque.data(), &src, sizeof(S)); 118*38e8c45fSAndroid Build Coastguard Worker return opaque; 119*38e8c45fSAndroid Build Coastguard Worker } 120*38e8c45fSAndroid Build Coastguard Worker }; 121*38e8c45fSAndroid Build Coastguard Worker 122*38e8c45fSAndroid Build Coastguard Worker // Traits class to help determine the template parameters to use for a ftl::Function, given a 123*38e8c45fSAndroid Build Coastguard Worker // function object. 124*38e8c45fSAndroid Build Coastguard Worker 125*38e8c45fSAndroid Build Coastguard Worker template <typename F, typename = decltype(&F::operator())> 126*38e8c45fSAndroid Build Coastguard Worker struct function_traits { 127*38e8c45fSAndroid Build Coastguard Worker // The function type `F` with which to instantiate the `Function<F, N>` template. 128*38e8c45fSAndroid Build Coastguard Worker using type = remove_member_function_pointer_t<&F::operator()>; 129*38e8c45fSAndroid Build Coastguard Worker 130*38e8c45fSAndroid Build Coastguard Worker // The (minimum) size `N` with which to instantiate the `Function<F, N>` template. 131*38e8c45fSAndroid Build Coastguard Worker static constexpr std::size_t size = 132*38e8c45fSAndroid Build Coastguard Worker (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); 133*38e8c45fSAndroid Build Coastguard Worker }; 134*38e8c45fSAndroid Build Coastguard Worker 135*38e8c45fSAndroid Build Coastguard Worker } // namespace android::ftl::details 136