• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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