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 #include <ftl/function.h>
18 #include <gtest/gtest.h>
19
20 #include <array>
21 #include <cstddef>
22 #include <cstdint>
23 #include <string_view>
24 #include <type_traits>
25
26 namespace android::test {
27 namespace {
28
29 // Create an alias to composite requirements defined by the trait class `T` for easier testing.
30 template <typename T, typename S>
31 inline constexpr bool is_opaquely_storable = (T::template require_trivially_copyable<S> &&
32 T::template require_trivially_destructible<S> &&
33 T::template require_will_fit_in_opaque_storage<S> &&
34 T::template require_alignment_compatible<S>);
35
36 // `I` gives a count of sizeof(std::intptr_t) bytes , and `J` gives a raw count of bytes
37 template <size_t I, size_t J = 0>
38 struct KnownSizeFunctionObject {
39 using Data = std::array<std::byte, sizeof(std::intptr_t) * I + J>;
operator ()android::test::__anon020ea97d0111::KnownSizeFunctionObject40 void operator()() const {};
41 Data data{};
42 };
43
44 } // namespace
45
46 // static_assert the expected type traits
47 static_assert(std::is_invocable_r_v<void, ftl::Function<void()>>);
48 static_assert(std::is_trivially_copyable_v<ftl::Function<void()>>);
49 static_assert(std::is_trivially_destructible_v<ftl::Function<void()>>);
50 static_assert(std::is_trivially_copy_constructible_v<ftl::Function<void()>>);
51 static_assert(std::is_trivially_move_constructible_v<ftl::Function<void()>>);
52 static_assert(std::is_trivially_copy_assignable_v<ftl::Function<void()>>);
53 static_assert(std::is_trivially_move_assignable_v<ftl::Function<void()>>);
54
55 template <typename T>
56 using function_traits = ftl::details::function_traits<T>;
57
58 // static_assert that the expected value of N is used for known function object sizes.
59 static_assert(function_traits<KnownSizeFunctionObject<0, 0>>::size == 0);
60 static_assert(function_traits<KnownSizeFunctionObject<0, 1>>::size == 0);
61 static_assert(function_traits<KnownSizeFunctionObject<1, 0>>::size == 0);
62 static_assert(function_traits<KnownSizeFunctionObject<1, 1>>::size == 1);
63 static_assert(function_traits<KnownSizeFunctionObject<2, 0>>::size == 1);
64 static_assert(function_traits<KnownSizeFunctionObject<2, 1>>::size == 2);
65
66 // Check that is_function_v works
67 static_assert(!ftl::is_function_v<KnownSizeFunctionObject<0>>);
68 static_assert(!ftl::is_function_v<std::function<void()>>);
69 static_assert(ftl::is_function_v<ftl::Function<void()>>);
70
71 // static_assert what can and cannot be stored inside the opaque storage
72
73 template <size_t N>
74 using function_opaque_storage = ftl::details::function_opaque_storage<N>;
75
76 // Function objects can be stored if they fit.
77 static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<0>>);
78 static_assert(is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<1>>);
79 static_assert(!is_opaquely_storable<function_opaque_storage<0>, KnownSizeFunctionObject<2>>);
80
81 static_assert(is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<2>>);
82 static_assert(!is_opaquely_storable<function_opaque_storage<1>, KnownSizeFunctionObject<3>>);
83
84 static_assert(is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<3>>);
85 static_assert(!is_opaquely_storable<function_opaque_storage<2>, KnownSizeFunctionObject<4>>);
86
87 // Another opaque storage can be stored if it fits. This property is used to copy smaller
88 // ftl::Functions into larger ones.
89 static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<0>::type>);
90 static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<1>::type>);
91 static_assert(is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<2>::type>);
92 static_assert(!is_opaquely_storable<function_opaque_storage<2>, function_opaque_storage<3>::type>);
93
94 // Function objects that aren't trivially copyable or destroyable cannot be stored.
__anon020ea97d0202null95 auto lambda_capturing_unique_ptr = [ptr = std::unique_ptr<void*>()] { static_cast<void>(ptr); };
96 static_assert(
97 !is_opaquely_storable<function_opaque_storage<2>, decltype(lambda_capturing_unique_ptr)>);
98
99 // Keep in sync with "Example usage" in header file.
TEST(Function,Example)100 TEST(Function, Example) {
101 using namespace std::string_view_literals;
102
103 class MyClass {
104 public:
105 void on_event() const {}
106 int on_string(int*, std::string_view) { return 1; }
107
108 auto get_function() {
109 return ftl::make_function([this] { on_event(); });
110 }
111 } cls;
112
113 // A function container with no arguments, and returning no value.
114 ftl::Function<void()> f;
115
116 // Construct a ftl::Function containing a small lambda.
117 f = cls.get_function();
118
119 // Construct a ftl::Function that calls `cls.on_event()`.
120 f = ftl::make_function<&MyClass::on_event>(&cls);
121
122 // Create a do-nothing function.
123 f = ftl::no_op;
124
125 // Invoke the contained function.
126 f();
127
128 // Also invokes it.
129 std::invoke(f);
130
131 // Create a typedef to give a more meaningful name and bound the size.
132 using MyFunction = ftl::Function<int(std::string_view), 2>;
133 int* ptr = nullptr;
134 auto f1 =
135 MyFunction::make([cls = &cls, ptr](std::string_view sv) { return cls->on_string(ptr, sv); });
136 int r = f1("abc"sv);
137
138 // Returns a default-constructed int (0).
139 f1 = ftl::no_op;
140 r = f1("abc"sv);
141 EXPECT_EQ(r, 0);
142 }
143
TEST(Function,BasicOperations)144 TEST(Function, BasicOperations) {
145 // Default constructible.
146 ftl::Function<int()> f;
147
148 // Compares as empty
149 EXPECT_FALSE(f);
150 EXPECT_TRUE(f == nullptr);
151 EXPECT_FALSE(f != nullptr);
152 EXPECT_TRUE(ftl::Function<int()>() == f);
153 EXPECT_FALSE(ftl::Function<int()>() != f);
154
155 // Assigning no_op sets it to not empty.
156 f = ftl::no_op;
157
158 // Verify it can be called, and that it returns a default constructed value.
159 EXPECT_EQ(f(), 0);
160
161 // Comparable when non-empty.
162 EXPECT_TRUE(f);
163 EXPECT_FALSE(f == nullptr);
164 EXPECT_TRUE(f != nullptr);
165 EXPECT_FALSE(ftl::Function<int()>() == f);
166 EXPECT_TRUE(ftl::Function<int()>() != f);
167
168 // Constructing from nullptr means empty.
169 f = ftl::Function<int()>{nullptr};
170 EXPECT_FALSE(f);
171
172 // Assigning nullptr means it is empty.
173 f = nullptr;
174 EXPECT_FALSE(f);
175
176 // Move construction
177 f = ftl::no_op;
178 ftl::Function<int()> g{std::move(f)};
179 EXPECT_TRUE(g != nullptr);
180
181 // Move assignment
182 f = nullptr;
183 f = std::move(g);
184 EXPECT_TRUE(f != nullptr);
185
186 // Copy construction
187 ftl::Function<int()> h{f};
188 EXPECT_TRUE(h != nullptr);
189
190 // Copy assignment
191 g = h;
192 EXPECT_TRUE(g != nullptr);
193 }
194
TEST(Function,CanMoveConstructFromLambda)195 TEST(Function, CanMoveConstructFromLambda) {
196 auto lambda = [] {};
197 ftl::Function<void()> f{std::move(lambda)};
198 }
199
TEST(Function,TerseDeducedConstructAndAssignFromLambda)200 TEST(Function, TerseDeducedConstructAndAssignFromLambda) {
201 auto f = ftl::Function([] { return 1; });
202 EXPECT_EQ(f(), 1);
203
204 f = [] { return 2; };
205 EXPECT_EQ(f(), 2);
206 }
207
208 namespace {
209
210 struct ImplicitConversionsHelper {
exactandroid::test::__anon020ea97d0811::ImplicitConversionsHelper211 auto exact(int) -> int { return 0; }
inexactandroid::test::__anon020ea97d0811::ImplicitConversionsHelper212 auto inexact(long) -> short { return 0; }
213 // TODO: Switch to `auto templated(auto x)` with C++20
214 template <typename T>
templatedandroid::test::__anon020ea97d0811::ImplicitConversionsHelper215 T templated(T x) {
216 return x;
217 }
218
static_exactandroid::test::__anon020ea97d0811::ImplicitConversionsHelper219 static auto static_exact(int) -> int { return 0; }
static_inexactandroid::test::__anon020ea97d0811::ImplicitConversionsHelper220 static auto static_inexact(long) -> short { return 0; }
221 // TODO: Switch to `static auto static_templated(auto x)` with C++20
222 template <typename T>
static_templatedandroid::test::__anon020ea97d0811::ImplicitConversionsHelper223 static T static_templated(T x) {
224 return x;
225 }
226 };
227
228 } // namespace
229
TEST(Function,ImplicitConversions)230 TEST(Function, ImplicitConversions) {
231 using Function = ftl::Function<int(int)>;
232 auto check = [](Function f) { return f(0); };
233 auto exact = [](int) -> int { return 0; };
234 auto inexact = [](long) -> short { return 0; };
235 auto templated = [](auto x) { return x; };
236
237 ImplicitConversionsHelper helper;
238
239 // Note, `check(nullptr)` would crash, so we can only check if it would be invocable.
240 static_assert(std::is_invocable_v<decltype(check), decltype(nullptr)>);
241
242 // Note: We invoke each of these to fully expand all the templates involved.
243 EXPECT_EQ(check(ftl::no_op), 0);
244
245 EXPECT_EQ(check(exact), 0);
246 EXPECT_EQ(check(inexact), 0);
247 EXPECT_EQ(check(templated), 0);
248
249 EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::exact>(&helper)), 0);
250 EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::inexact>(&helper)), 0);
251 EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::templated<int>>(&helper)), 0);
252
253 EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_exact>()), 0);
254 EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_inexact>()), 0);
255 EXPECT_EQ(check(Function::make<&ImplicitConversionsHelper::static_templated<int>>()), 0);
256 }
257
TEST(Function,MakeWithNonConstMemberFunction)258 TEST(Function, MakeWithNonConstMemberFunction) {
259 struct Observer {
260 bool called = false;
261 void setCalled() { called = true; }
262 } observer;
263
264 auto f = ftl::make_function<&Observer::setCalled>(&observer);
265
266 f();
267
268 EXPECT_TRUE(observer.called);
269
270 EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
271 }
272
TEST(Function,MakeWithConstMemberFunction)273 TEST(Function, MakeWithConstMemberFunction) {
274 struct Observer {
275 mutable bool called = false;
276 void setCalled() const { called = true; }
277 } observer;
278
279 const auto f = ftl::make_function<&Observer::setCalled>(&observer);
280
281 f();
282
283 EXPECT_TRUE(observer.called);
284
285 EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
286 }
287
TEST(Function,MakeWithConstClassPointer)288 TEST(Function, MakeWithConstClassPointer) {
289 const struct Observer {
290 mutable bool called = false;
291 void setCalled() const { called = true; }
292 } observer;
293
294 const auto f = ftl::make_function<&Observer::setCalled>(&observer);
295
296 f();
297
298 EXPECT_TRUE(observer.called);
299
300 EXPECT_TRUE(f == ftl::Function<void()>::make<&Observer::setCalled>(&observer));
301 }
302
TEST(Function,MakeWithNonCapturingLambda)303 TEST(Function, MakeWithNonCapturingLambda) {
304 auto f = ftl::make_function([](int a, int b) { return a + b; });
305 EXPECT_EQ(f(1, 2), 3);
306 }
307
TEST(Function,MakeWithCapturingLambda)308 TEST(Function, MakeWithCapturingLambda) {
309 bool called = false;
310 auto f = ftl::make_function([&called](int a, int b) {
311 called = true;
312 return a + b;
313 });
314 EXPECT_EQ(f(1, 2), 3);
315 EXPECT_TRUE(called);
316 }
317
TEST(Function,MakeWithCapturingMutableLambda)318 TEST(Function, MakeWithCapturingMutableLambda) {
319 bool called = false;
320 auto f = ftl::make_function([&called](int a, int b) mutable {
321 called = true;
322 return a + b;
323 });
324 EXPECT_EQ(f(1, 2), 3);
325 EXPECT_TRUE(called);
326 }
327
TEST(Function,MakeWithThreePointerCapturingLambda)328 TEST(Function, MakeWithThreePointerCapturingLambda) {
329 bool my_bool = false;
330 int my_int = 0;
331 float my_float = 0.f;
332
333 auto f = ftl::make_function(
334 [ptr_bool = &my_bool, ptr_int = &my_int, ptr_float = &my_float](int a, int b) mutable {
335 *ptr_bool = true;
336 *ptr_int = 1;
337 *ptr_float = 1.f;
338
339 return a + b;
340 });
341
342 EXPECT_EQ(f(1, 2), 3);
343
344 EXPECT_TRUE(my_bool);
345 EXPECT_EQ(my_int, 1);
346 EXPECT_EQ(my_float, 1.f);
347 }
348
TEST(Function,MakeWithFreeFunction)349 TEST(Function, MakeWithFreeFunction) {
350 auto f = ftl::make_function<&std::make_unique<int, int>>();
351 std::unique_ptr<int> unique_int = f(1);
352 ASSERT_TRUE(unique_int);
353 EXPECT_EQ(*unique_int, 1);
354 }
355
TEST(Function,CopyToLarger)356 TEST(Function, CopyToLarger) {
357 int counter = 0;
358 ftl::Function<void()> a{[ptr_counter = &counter] { (*ptr_counter)++; }};
359 ftl::Function<void(), 1> b = a;
360 ftl::Function<void(), 2> c = a;
361
362 EXPECT_EQ(counter, 0);
363 a();
364 EXPECT_EQ(counter, 1);
365 b();
366 EXPECT_EQ(counter, 2);
367 c();
368 EXPECT_EQ(counter, 3);
369
370 b = [ptr_counter = &counter] { (*ptr_counter) += 2; };
371 c = [ptr_counter = &counter] { (*ptr_counter) += 3; };
372
373 b();
374 EXPECT_EQ(counter, 5);
375 c();
376 EXPECT_EQ(counter, 8);
377 }
378
379 } // namespace android::test
380