• 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 #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