blob: 35c5a8b30201a934de8c99b57ba78d60dd65a6f6 [file] [log] [blame]
Lloyd Piqueace9af92022-11-09 13:56:40 -08001/*
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
25namespace android::ftl::details {
26
27// The maximum allowed value for the template argument `N` in
28// `ftl::Function<F, N>`.
29constexpr 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
34template <typename>
35struct remove_member_function_pointer;
36
37template <typename Class, typename Ret, typename... Args>
38struct remove_member_function_pointer<Ret (Class::*)(Args...)> {
39 using type = Ret(Args...);
40};
41
42template <typename Class, typename Ret, typename... Args>
43struct remove_member_function_pointer<Ret (Class::*)(Args...) const> {
44 using type = Ret(Args...);
45};
46
47template <auto MemberFunction>
48using 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
53template <typename Ret, typename... Args>
54auto 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
62template <typename F, typename Ret, typename... Args>
63auto 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
69template <auto MemberFunction, typename Class, typename Ret, typename... Args>
70auto 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
76template <auto FreeFunction, typename Ret, typename... Args>
77auto 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
83template <std::size_t N>
84struct 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
125template <typename F, typename = decltype(&F::operator())>
126struct 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