blob: 3538ca4eae2fa5f3e93865db9a012d102d542c2b [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 <cstddef>
20#include <functional>
21#include <type_traits>
22#include <utility>
23
24#include <ftl/details/function.h>
25
26namespace android::ftl {
27
28// ftl::Function<F, N> is a container for function object, and can mostly be used in place of
29// std::function<F>.
30//
31// Unlike std::function<F>, a ftl::Function<F, N>:
32//
33// * Uses a static amount of memory (controlled by N), and never any dynamic allocation.
34// * Satisfies the std::is_trivially_copyable<> trait.
35// * Satisfies the std::is_trivially_destructible<> trait.
36//
37// However those same limits are also required from the contained function object in turn.
38//
39// The size of a ftl::Function<F, N> is guaranteed to be:
40//
41// sizeof(std::intptr_t) * (N + 2)
42//
43// A ftl::Function<F, N> can always be implicitly converted to a larger size ftl::Function<F, M>.
44// Trying to convert the other way leads to a compilation error.
45//
46// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns
47// false in this state. It is undefined behavior to attempt to invoke the function in this state.
48//
49// The ftl::Function<F, N> can also be constructed or assigned from ftl::no_op. This sets up the
50// ftl::Function to be non-empty, with a function that when called does nothing except
51// default-constructs a return value.
52//
53// The ftl::make_function() helpers construct a ftl::Function<F, N>, including deducing the
54// values of F and N from the arguments it is given.
55//
56// The static ftl::Function<F, N>::make() helpers construct a ftl::Function<F, N> without that
57// deduction, and also allow for implicit argument conversion if the target being called needs them.
58//
59// The construction helpers allow any of the following types of functions to be stored:
60//
61// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small
62// capture, or other "functor". The requirements are:
63//
64// 1) The function object must be trivial to destroy (in fact, the destructor will never
65// actually be called once copied to the internal storage).
66// 2) The function object must be trivial to copy (the raw bytes will be copied as the
67// ftl::Function<F, N> is copied/moved).
68// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1),
69// and it cannot require stricter alignment than alignof(std::intptr_t).
70//
71// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is
72// enough to capture `this`, which is why N=0 is the default.
73//
74// * A member function, with the address passed as the template value argument to the construction
75// helper function, along with the instance pointer needed to invoke it passed as an ordinary
76// argument.
77//
78// ftl::make_function<&Class::member_function>(this);
79//
80// Note that the indicated member function will be invoked non-virtually. If you need it to be
81// invoked virtually, you should invoke it yourself with a small lambda like so:
82//
83// ftl::function([this] { virtual_member_function(); });
84//
85// * An ordinary function ("free function"), with the address of the function passed as a template
86// value argument.
87//
88// ftl::make_function<&std::atoi>();
89//
90// As with the member function helper, as the function is known at compile time, it will be called
91// directly.
92//
93// Example usage:
94//
95// class MyClass {
96// public:
97// void on_event() const {}
98// int on_string(int*, std::string_view) { return 1; }
99//
100// auto get_function() {
101// return ftl::function([this] { on_event(); });
102// }
103// } cls;
104//
105// // A function container with no arguments, and returning no value.
106// ftl::Function<void()> f;
107//
108// // Construct a ftl::Function containing a small lambda.
109// f = cls.get_function();
110//
111// // Construct a ftl::Function that calls `cls.on_event()`.
112// f = ftl::function<&MyClass::on_event>(&cls);
113//
114// // Create a do-nothing function.
115// f = ftl::no_op;
116//
117// // Invoke the contained function.
118// f();
119//
120// // Also invokes it.
121// std::invoke(f);
122//
123// // Create a typedef to give a more meaningful name and bound the size.
124// using MyFunction = ftl::Function<int(std::string_view), 2>;
125// int* ptr = nullptr;
126// auto f1 = MyFunction::make_function(
127// [cls = &cls, ptr](std::string_view sv) {
128// return cls->on_string(ptr, sv);
129// });
130// int r = f1("abc"sv);
131//
132// // Returns a default-constructed int (0).
133// f1 = ftl::no_op;
134// r = f1("abc"sv);
135// assert(r == 0);
136
137template <typename F, std::size_t N = 0>
138class Function;
139
140// Used to construct a Function that does nothing.
141struct NoOpTag {};
142
143constexpr NoOpTag no_op;
144
145// Detects that a type is a `ftl::Function<F, N>` regardless of what `F` and `N` are.
146template <typename>
147struct is_function : public std::false_type {};
148
149template <typename F, std::size_t N>
150struct is_function<Function<F, N>> : public std::true_type {};
151
152template <typename T>
153constexpr bool is_function_v = is_function<T>::value;
154
155template <typename Ret, typename... Args, std::size_t N>
156class Function<Ret(Args...), N> final {
157 // Enforce a valid size, with an arbitrary maximum allowed size for the container of
158 // sizeof(std::intptr_t) * 16, though that maximum can be relaxed.
159 static_assert(N <= details::kFunctionMaximumN);
160
161 using OpaqueStorageTraits = details::function_opaque_storage<N>;
162
163 public:
164 // Defining result_type allows ftl::Function to be substituted for std::function.
165 using result_type = Ret;
166
167 // Constructs an empty ftl::Function.
168 Function() = default;
169
170 // Constructing or assigning from nullptr_t also creates an empty ftl::Function.
171 Function(std::nullptr_t) {}
172 Function& operator=(std::nullptr_t) { return *this = Function(nullptr); }
173
174 // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which
175 // returns a default constructed return value.
176 Function(NoOpTag) : function_(details::bind_opaque_no_op<Ret, Args...>()) {}
177 Function& operator=(NoOpTag) { return *this = Function(no_op); }
178
179 // Constructing/assigning from a function object stores a copy of that function object, however:
180 // * It must be trivially copyable, as the implementation makes a copy with memcpy().
181 // * It must be trivially destructible, as the implementation doesn't destroy the copy!
182 // * It must fit in the limited internal storage, which enforces size/alignment restrictions.
183
184 template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>>
185 Function(const F& f)
186 : opaque_(OpaqueStorageTraits::opaque_copy(f)),
187 function_(details::bind_opaque_function_object<F, Ret, Args...>(f)) {}
188
189 template <typename F, typename = std::enable_if_t<std::is_invocable_r_v<Ret, F, Args...>>>
190 Function& operator=(const F& f) noexcept {
191 return *this = Function{OpaqueStorageTraits::opaque_copy(f),
192 details::bind_opaque_function_object<F, Ret, Args...>(f)};
193 }
194
195 // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else.
196
197 template <std::size_t M>
198 Function(const Function<Ret(Args...), M>& other)
199 : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, function_(other.function_) {}
200
201 template <std::size_t M>
202 auto& operator=(const Function<Ret(Args...), M>& other) {
203 return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), other.function_};
204 }
205
206 // Returns true if a function is set.
207 explicit operator bool() const { return function_ != nullptr; }
208
209 // Checks if the other function has the same contents as this one.
210 bool operator==(const Function& other) const {
211 return other.opaque_ == opaque_ && other.function_ == function_;
212 }
213 bool operator!=(const Function& other) const { return !operator==(other); }
214
215 // Alternative way of testing for a function being set.
216 bool operator==(std::nullptr_t) const { return function_ == nullptr; }
217 bool operator!=(std::nullptr_t) const { return function_ != nullptr; }
218
219 // Invokes the function.
220 Ret operator()(Args... args) const {
221 return std::invoke(function_, opaque_.data(), std::forward<Args>(args)...);
222 }
223
224 // Creation helper for function objects, such as lambdas.
225 template <typename F>
226 static auto make(const F& f) -> decltype(Function{f}) {
227 return Function{f};
228 }
229
230 // Creation helper for a class pointer and a compile-time chosen member function to call.
231 template <auto MemberFunction, typename Class>
232 static auto make(Class* instance) -> decltype(Function{
233 details::bind_member_function<MemberFunction>(instance,
234 static_cast<Ret (*)(Args...)>(nullptr))}) {
235 return Function{details::bind_member_function<MemberFunction>(
236 instance, static_cast<Ret (*)(Args...)>(nullptr))};
237 }
238
239 // Creation helper for a compile-time chosen free function to call.
240 template <auto FreeFunction>
241 static auto make() -> decltype(Function{
242 details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))}) {
243 return Function{
244 details::bind_free_function<FreeFunction>(static_cast<Ret (*)(Args...)>(nullptr))};
245 }
246
247 private:
248 // Needed so a Function<F, M> can be converted to a Function<F, N>.
249 template <typename, std::size_t>
250 friend class Function;
251
252 // The function pointer type of function stored in `function_`. The first argument is always
253 // `&opaque_`.
254 using StoredFunction = Ret(void*, Args...);
255
256 // The type of the opaque storage, used to hold an appropriate function object.
257 // The type stored here is ONLY known to the StoredFunction.
258 // We always use at least one std::intptr_t worth of storage, and always a multiple of that size.
259 using OpaqueStorage = typename OpaqueStorageTraits::type;
260
261 // Internal constructor for creating from a raw opaque blob + function pointer.
262 Function(const OpaqueStorage& opaque, StoredFunction* function)
263 : opaque_(opaque), function_(function) {}
264
265 // Note: `mutable` so that `operator() const` can use it.
266 mutable OpaqueStorage opaque_{};
267 StoredFunction* function_{nullptr};
268};
269
270// Makes a ftl::Function given a function object `F`.
271template <typename F, typename T = details::function_traits<F>>
272Function(const F&) -> Function<typename T::type, T::size>;
273
274template <typename F>
275auto make_function(const F& f) -> decltype(Function{f}) {
276 return Function{f};
277}
278
279// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`.
280template <auto MemberFunction, typename Class>
281auto make_function(Class* instance)
282 -> decltype(Function{details::bind_member_function<MemberFunction>(
283 instance,
284 static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))}) {
285 return Function{details::bind_member_function<MemberFunction>(
286 instance, static_cast<details::remove_member_function_pointer_t<MemberFunction>*>(nullptr))};
287}
288
289// Makes a ftl::Function given an ordinary free function.
290template <auto FreeFunction>
291auto make_function() -> decltype(Function{
292 details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))}) {
293 return Function{
294 details::bind_free_function<FreeFunction>(static_cast<decltype(FreeFunction)>(nullptr))};
295}
296
297} // namespace android::ftl