1 /*
2 * Copyright (C) 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 #define LOG_TAG "inplace_function_tests"
18
19 #include <mediautils/InPlaceFunction.h>
20
21 #include <type_traits>
22
23 #include <gtest/gtest.h>
24 #include <log/log.h>
25
26 using namespace android;
27 using namespace android::mediautils;
28
29 struct BigCallable {
BigCallableBigCallable30 BigCallable(size_t* x, size_t val1, size_t val2) : ptr(x), a(val1), b(val2) {}
31 size_t* ptr;
32 size_t a;
33 size_t b;
operator ()BigCallable34 size_t operator()(size_t input) const {
35 *ptr += a * 100 + b * 10 + input;
36 return 8;
37 }
38 };
39
TEST(InPlaceFunctionTests,Basic)40 TEST(InPlaceFunctionTests, Basic) {
41 size_t x = 5;
42 InPlaceFunction<size_t(size_t)> func;
43 {
44 BigCallable test{&x, 2, 3};
45 func = test;
46 }
47 EXPECT_EQ(func(2), 8ull);
48 EXPECT_EQ(x, 232ull + 5);
49 }
50
TEST(InPlaceFunctionTests,Invalid)51 TEST(InPlaceFunctionTests, Invalid) {
52 InPlaceFunction<size_t(size_t)> func;
53 EXPECT_TRUE(!func);
54 InPlaceFunction<size_t(size_t)> func2{nullptr};
55 EXPECT_TRUE(!func2);
56 InPlaceFunction<size_t(size_t)> func3 = [](size_t x) { return x; };
57 EXPECT_TRUE(!(!func3));
58 func3 = nullptr;
59 EXPECT_TRUE(!func3);
60 }
61
TEST(InPlaceFunctionTests,MultiArg)62 TEST(InPlaceFunctionTests, MultiArg) {
63 InPlaceFunction<size_t(size_t, size_t, size_t)> func = [](size_t a, size_t b, size_t c) {
64 return a + b + c;
65 };
66 EXPECT_EQ(func(2, 3, 5), 2ull + 3 + 5);
67 }
68 struct Record {
RecordRecord69 Record(size_t m, size_t c, size_t d) : move_called(m), copy_called(c), dtor_called(d) {}
RecordRecord70 Record() {}
71 size_t move_called = 0;
72 size_t copy_called = 0;
73 size_t dtor_called = 0;
operator <<(std::ostream & os,const Record & record)74 friend std::ostream& operator<<(std::ostream& os, const Record& record) {
75 return os << "Record, moves: " << record.move_called << ", copies: " << record.copy_called
76 << ", dtor: " << record.dtor_called << '\n';
77 }
78 };
79
operator ==(const Record & lhs,const Record & rhs)80 bool operator==(const Record& lhs, const Record& rhs) {
81 return lhs.move_called == rhs.move_called && lhs.copy_called == rhs.copy_called &&
82 lhs.dtor_called == rhs.dtor_called;
83 }
84
85 struct Noisy {
86 Record& ref;
87 size_t state;
NoisyNoisy88 Noisy(Record& record, size_t val) : ref(record), state(val) {}
NoisyNoisy89 Noisy(const Noisy& other) : ref(other.ref), state(other.state) { ref.copy_called++; }
90
NoisyNoisy91 Noisy(Noisy&& other) : ref(other.ref), state(other.state) { ref.move_called++; }
~NoisyNoisy92 ~Noisy() { ref.dtor_called++; }
93
operator ()Noisy94 size_t operator()() { return state; }
95 };
96
TEST(InPlaceFunctionTests,CtorForwarding)97 TEST(InPlaceFunctionTests, CtorForwarding) {
98 Record record;
99 Noisy noisy{record, 17};
100 InPlaceFunction<size_t()> func{noisy};
101 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
102 EXPECT_EQ(func(), 17ull);
103 Record record2;
104 Noisy noisy2{record2, 13};
105 InPlaceFunction<size_t()> func2{std::move(noisy2)};
106 EXPECT_EQ(record2, Record(1, 0, 0)); // move, copy, dtor
107 EXPECT_EQ(func2(), 13ull);
108 }
109
TEST(InPlaceFunctionTests,FunctionCtorForwarding)110 TEST(InPlaceFunctionTests, FunctionCtorForwarding) {
111 {
112 Record record;
113 Noisy noisy{record, 17};
114 InPlaceFunction<size_t()> func{noisy};
115 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
116 EXPECT_EQ(func(), 17ull);
117 InPlaceFunction<size_t()> func2{func};
118 EXPECT_EQ(record, Record(0, 2, 0)); // move, copy, dtor
119 EXPECT_EQ(func2(), 17ull);
120 }
121 Record record;
122 Noisy noisy{record, 13};
123 InPlaceFunction<size_t()> func{noisy};
124 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
125 EXPECT_EQ(func(), 13ull);
126 InPlaceFunction<size_t()> func2{std::move(func)};
127 EXPECT_EQ(record, Record(1, 1, 0)); // move, copy, dtor
128 EXPECT_EQ(func2(), 13ull);
129 // We expect moved from functions to still be valid
130 EXPECT_TRUE(!(!func));
131 EXPECT_EQ(static_cast<bool>(func), static_cast<bool>(func2));
132 EXPECT_EQ(func(), 13ull);
133 }
134
TEST(InPlaceFunctionTests,Dtor)135 TEST(InPlaceFunctionTests, Dtor) {
136 Record record;
137 {
138 InPlaceFunction<size_t()> func;
139 {
140 Noisy noisy{record, 17};
141 func = noisy;
142 }
143 EXPECT_EQ(func(), 17ull);
144 EXPECT_EQ(record.dtor_called, 1ull);
145 }
146 EXPECT_EQ(record.dtor_called, 2ull);
147 }
148
TEST(InPlaceFunctionTests,Assignment)149 TEST(InPlaceFunctionTests, Assignment) {
150 {
151 Record record;
152 Record record2;
153 Noisy noisy{record, 17};
154 Noisy noisy2{record2, 5};
155 InPlaceFunction<size_t()> func{noisy};
156 EXPECT_EQ(func(), 17ull);
157 EXPECT_EQ(record.dtor_called, 0ull);
158 func = noisy2;
159 EXPECT_EQ(record.dtor_called, 1ull);
160 EXPECT_EQ(record2, Record(0, 1, 0)); // move, copy, dtor
161 EXPECT_EQ(func(), 5ull);
162 }
163 {
164 Record record;
165 Record record2;
166 Noisy noisy{record, 17};
167 Noisy noisy2{record2, 5};
168 InPlaceFunction<size_t()> func{noisy};
169 EXPECT_EQ(func(), 17ull);
170 EXPECT_EQ(record.dtor_called, 0ull);
171 func = std::move(noisy2);
172 EXPECT_EQ(record.dtor_called, 1ull);
173 EXPECT_EQ(record2, Record(1, 0, 0)); // move, copy, dtor
174 EXPECT_EQ(func(), 5ull);
175 }
176
177 {
178 Record record;
179 Record record2;
180 Noisy noisy{record, 17};
181 Noisy noisy2{record2, 13};
182 {
183 InPlaceFunction<size_t()> func{noisy};
184 EXPECT_EQ(func(), 17ull);
185 InPlaceFunction<size_t()> func2{noisy2};
186 EXPECT_EQ(record2, Record(0, 1, 0)); // move, copy, dtor
187 EXPECT_EQ(record.dtor_called, 0ull);
188 func = func2;
189 EXPECT_EQ(record.dtor_called, 1ull);
190 EXPECT_EQ(func(), 13ull);
191 EXPECT_EQ(record2, Record(0, 2, 0)); // move, copy, dtor
192 EXPECT_TRUE(static_cast<bool>(func2));
193 EXPECT_EQ(func2(), 13ull);
194 }
195 EXPECT_EQ(record2, Record(0, 2, 2)); // move, copy, dtor
196 }
197
198 {
199 Record record;
200 Record record2;
201 Noisy noisy{record, 17};
202 Noisy noisy2{record2, 13};
203 {
204 InPlaceFunction<size_t()> func{noisy};
205 EXPECT_EQ(func(), 17ull);
206 InPlaceFunction<size_t()> func2{noisy2};
207 EXPECT_EQ(record.dtor_called, 0ull);
208 EXPECT_EQ(record2, Record(0, 1, 0)); // move, copy, dtor
209 func = std::move(func2);
210 EXPECT_EQ(record.dtor_called, 1ull);
211 EXPECT_EQ(func(), 13ull);
212 EXPECT_EQ(record2, Record(1, 1, 0)); // move, copy, dtor
213 // Moved from function is still valid
214 EXPECT_TRUE(static_cast<bool>(func2));
215 EXPECT_EQ(func2(), 13ull);
216 }
217 EXPECT_EQ(record2, Record(1, 1, 2)); // move, copy, dtor
218 }
219 }
220
TEST(InPlaceFunctionTests,Swap)221 TEST(InPlaceFunctionTests, Swap) {
222 Record record1;
223 Record record2;
224 InPlaceFunction<size_t()> func1 = Noisy{record1, 5};
225 InPlaceFunction<size_t()> func2 = Noisy{record2, 7};
226 EXPECT_EQ(record1, Record(1, 0, 1)); // move, copy, dtor
227 EXPECT_EQ(record2, Record(1, 0, 1)); // move, copy, dtor
228 EXPECT_EQ(func1(), 5ull);
229 EXPECT_EQ(func2(), 7ull);
230 func1.swap(func2);
231 EXPECT_EQ(record1, Record(2, 0, 2)); // move, copy, dtor
232 // An additional move and destroy into the temporary object
233 EXPECT_EQ(record2, Record(3, 0, 3)); // move, copy, dtor
234 EXPECT_EQ(func1(), 7ull);
235 EXPECT_EQ(func2(), 5ull);
236 }
237
TEST(InPlaceFunctionTests,Conversion)238 TEST(InPlaceFunctionTests, Conversion) {
239 Record record;
240 Noisy noisy{record, 15};
241 {
242 InPlaceFunction<size_t(), 16> func2 = noisy;
243 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
244 {
245 InPlaceFunction<size_t(), 32> func{func2};
246 EXPECT_EQ(record, Record(0, 2, 0)); // move, copy, dtor
247 EXPECT_EQ(func2(), func());
248 }
249 EXPECT_EQ(record, Record(0, 2, 1)); // move, copy, dtor
250 }
251 EXPECT_EQ(record, Record(0, 2, 2)); // move, copy, dtor
252 }
253
TEST(InPlaceFunctionTests,ConversionMove)254 TEST(InPlaceFunctionTests, ConversionMove) {
255 Record record;
256 Noisy noisy{record, 15};
257 {
258 InPlaceFunction<size_t(), 16> func2 = noisy;
259 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
260 {
261 InPlaceFunction<size_t(), 32> func{std::move(func2)};
262 EXPECT_EQ(record, Record(1, 1, 0)); // move, copy, dtor
263 EXPECT_EQ(func2(), func());
264 }
265 EXPECT_EQ(record, Record(1, 1, 1)); // move, copy, dtor
266 }
267 EXPECT_EQ(record, Record(1, 1, 2)); // move, copy, dtor
268 }
269
TEST(InPlaceFunctionTests,ConversionAssign)270 TEST(InPlaceFunctionTests, ConversionAssign) {
271 Record record;
272 Noisy noisy{record, 15};
273 {
274 InPlaceFunction<size_t(), 32> func;
275 {
276 InPlaceFunction<size_t(), 16> func2 = noisy;
277 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
278 func = func2;
279 EXPECT_EQ(record, Record(0, 2, 0)); // move, copy, dtor
280 EXPECT_EQ(func2(), func());
281 }
282 EXPECT_EQ(record, Record(0, 2, 1)); // move, copy, dtor
283 }
284 EXPECT_EQ(record, Record(0, 2, 2)); // move, copy, dtor
285 }
286
TEST(InPlaceFunctionTests,ConversionAssignMove)287 TEST(InPlaceFunctionTests, ConversionAssignMove) {
288 Record record;
289 Noisy noisy{record, 15};
290 {
291 InPlaceFunction<size_t(), 32> func;
292 {
293 InPlaceFunction<size_t(), 16> func2 = noisy;
294 EXPECT_EQ(record, Record(0, 1, 0)); // move, copy, dtor
295 func = std::move(func2);
296 EXPECT_EQ(record, Record(1, 1, 0)); // move, copy, dtor
297 EXPECT_EQ(func2(), func());
298 }
299 EXPECT_EQ(record, Record(1, 1, 1)); // move, copy, dtor
300 }
301 EXPECT_EQ(record, Record(1, 1, 2)); // move, copy, dtor
302 }
303
304 struct NoMoveCopy {
305 NoMoveCopy() = default;
306 NoMoveCopy(const NoMoveCopy&) = delete;
307 NoMoveCopy(NoMoveCopy&&) = delete;
308 };
309 struct TestCallable {
operator ()TestCallable310 NoMoveCopy& operator()(NoMoveCopy& x) { return x; }
311 };
312
TEST(InPlaceFunctionTests,ArgumentForwarding)313 TEST(InPlaceFunctionTests, ArgumentForwarding) {
314 const auto lambd = [](NoMoveCopy& x) -> NoMoveCopy& { return x; };
315 InPlaceFunction<NoMoveCopy&(NoMoveCopy&)> func = lambd;
316 const auto lambd2 = [](NoMoveCopy&& x) -> NoMoveCopy&& { return std::move(x); };
317 InPlaceFunction<NoMoveCopy && (NoMoveCopy &&)> func2 = lambd2;
318 auto lvalue = NoMoveCopy{};
319 func(lvalue);
320 func2(NoMoveCopy{});
321 InPlaceFunction<void(NoMoveCopy&)> func3 = [](const NoMoveCopy&) {};
322 func3(lvalue);
323 InPlaceFunction<void(NoMoveCopy &&)> func4 = [](const NoMoveCopy&) {};
324 func4(std::move(lvalue));
325 InPlaceFunction<void(const NoMoveCopy&)> func5 = [](const NoMoveCopy&) {};
326 func5(lvalue);
327 InPlaceFunction<void(const NoMoveCopy&&)> func6 = [](const NoMoveCopy&) {};
328 func6(std::move(lvalue));
329 InPlaceFunction<void(const NoMoveCopy&&)> func7 = [](const NoMoveCopy&&) {};
330 func7(std::move(lvalue));
331 InPlaceFunction<void(NoMoveCopy &&)> func8 = [](const NoMoveCopy&&) {};
332 func8(std::move(lvalue));
333
334 {
335 Record record;
336 Noisy noisy{record, 5};
337 const auto lambd3 = [](Noisy) {};
338 InPlaceFunction<void(Noisy)> func3{lambd3};
339 EXPECT_EQ(record, Record(0, 0, 0)); // move, copy, dtor
340 func3(std::move(noisy));
341 EXPECT_EQ(record, Record(2, 0, 2)); // move, copy, dtor
342 }
343
344 {
345 Record record;
346 Noisy noisy{record, 5};
347 const auto lambd3 = [](Noisy) {};
348 InPlaceFunction<void(Noisy)> func3{lambd3};
349 EXPECT_EQ(record, Record(0, 0, 0)); // move, copy, dtor
350 func3(noisy);
351 EXPECT_EQ(record, Record(1, 1, 2)); // move, copy, dtor
352 }
353 }
354
TEST(InPlaceFunctionTests,VoidFunction)355 TEST(InPlaceFunctionTests, VoidFunction) {
356 InPlaceFunction<void(size_t)> func = [](size_t x) -> size_t { return x; };
357 func(5);
358 InPlaceFunction<void(void)> func2 = []() -> size_t { return 5; };
359 func2();
360 }
foo()361 NoMoveCopy foo() {
362 return NoMoveCopy();
363 }
364 struct Test {
operator ()Test365 NoMoveCopy operator()() { return NoMoveCopy{}; }
366 };
367
TEST(InPlaceFunctionTests,FullElision)368 TEST(InPlaceFunctionTests, FullElision) {
369 InPlaceFunction<NoMoveCopy()> func = foo;
370 }
371
TEST(InPlaceFunctionTests,ReturnConversion)372 TEST(InPlaceFunctionTests, ReturnConversion) {
373 const auto lambd = [](int&& x) -> int&& { return std::move(x); };
374 InPlaceFunction<int && (int&& x)> func = lambd;
375 func(5);
376 InPlaceFunction<void(int)> func3 = [](double) {};
377 func3(5);
378 InPlaceFunction<double()> func4 = []() -> int { return 5; };
379 func4();
380 }
381
382 struct Overloaded {
operator ()Overloaded383 int operator()() & { return 2; }
operator ()Overloaded384 int operator()() const& { return 3; }
operator ()Overloaded385 int operator()() && { return 4; }
operator ()Overloaded386 int operator()() const&& { return 5; }
387 };
388
TEST(InPlaceFunctionTests,OverloadResolution)389 TEST(InPlaceFunctionTests, OverloadResolution) {
390 InPlaceFunction<int()> func = Overloaded{};
391 EXPECT_EQ(func(), 2);
392 EXPECT_EQ(std::move(func()), 2);
393 }
394
395 template <class T, class U, class = void>
396 struct can_assign : std::false_type {};
397
398 template <class T, class U>
399 struct can_assign<T, U, typename std::void_t<decltype(T().operator=(U()))>> : std::true_type {};
400
401 template <class From, class To, bool Expected>
402 static constexpr bool Convertible =
403 (can_assign<To, From>::value ==
404 std::is_constructible_v<To, From>)&&(std::is_constructible_v<To, From> == Expected);
405
406 struct TooBig {
407 std::array<uint64_t, 5> big = {1, 2, 3, 4, 5};
operator ()TooBig408 size_t operator()() { return static_cast<size_t>(big[0] + big[1] + big[2] + big[3] + big[4]); }
409 };
410 static_assert(sizeof(TooBig) == 40);
411 struct NotCallable {};
412 struct WrongArg {
operator ()WrongArg413 void operator()(NotCallable) {}
414 };
415 struct WrongRet {
operator ()WrongRet416 NotCallable operator()(size_t) { return NotCallable{}; }
417 };
418
419 static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 32>, true>);
420 static_assert(
421 Convertible<InPlaceFunction<size_t(size_t), 32>, InPlaceFunction<size_t(), 32>, false>);
422 static_assert(Convertible<InPlaceFunction<void(), 32>, InPlaceFunction<size_t(), 32>, false>);
423 static_assert(Convertible<TooBig, InPlaceFunction<size_t(), 32>, false>);
424 static_assert(Convertible<TooBig, InPlaceFunction<size_t(), 40>, true>);
425 static_assert(Convertible<NotCallable, InPlaceFunction<size_t(), 40>, false>);
426 static_assert(Convertible<WrongArg, InPlaceFunction<void(size_t), 40>, false>);
427 static_assert(Convertible<WrongRet, InPlaceFunction<size_t(size_t), 40>, false>);
428 // Void returning functions are modelled by any return type
429 static_assert(Convertible<WrongRet, InPlaceFunction<void(size_t), 40>, true>);
430
431 // Check constructibility/assignability from smaller function types
432 static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 24>, false>);
433 static_assert(Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(), 40>, true>);
434 static_assert(
435 Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<size_t(size_t), 40>, false>);
436 static_assert(
437 Convertible<InPlaceFunction<size_t(), 32>, InPlaceFunction<NotCallable(), 40>, false>);
438
439 struct BadLambd {
operator ()BadLambd440 int operator()(int&& x) { return std::move(x); }
441 };
442
443 static_assert(Convertible<BadLambd, InPlaceFunction<int(int&&), 32>, true>);
444 static_assert(Convertible<BadLambd, InPlaceFunction<int&(int&&), 32>, false>);
445 static_assert(Convertible<BadLambd, InPlaceFunction<const int&(int&&), 32>, false>);
446 static_assert(Convertible<BadLambd, InPlaceFunction<int && (int&&), 32>, false>);
447 static_assert(Convertible<BadLambd, InPlaceFunction<const int && (int&&), 32>, false>);
448
449 struct Base {};
450 struct Derived : Base {};
451 struct Converted {
ConvertedConverted452 Converted(const Derived&) {}
453 };
454
455 struct ConvertCallable {
operator ()ConvertCallable456 Derived operator()() { return Derived{}; }
operator ()ConvertCallable457 Derived& operator()(Derived& x) { return x; }
operator ()ConvertCallable458 Derived&& operator()(Derived&& x) { return std::move(x); }
operator ()ConvertCallable459 const Derived& operator()(const Derived& x) { return x; }
operator ()ConvertCallable460 const Derived&& operator()(const Derived&& x) { return std::move(x); }
461 };
462
463 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived&()>, false>);
464 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base&()>, false>);
465 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived()>, true>);
466 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base()>, true>);
467 static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted()>, true>);
468 static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted&()>, false>);
469 static_assert(Convertible<ConvertCallable, InPlaceFunction<Converted && ()>, false>);
470 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Converted&()>, false>);
471 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Converted && ()>, false>);
472
473 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived&(Derived&)>, true>);
474 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base&(Derived&)>, true>);
475
476 static_assert(Convertible<ConvertCallable, InPlaceFunction<Derived && (Derived &&)>, true>);
477 static_assert(Convertible<ConvertCallable, InPlaceFunction<Base && (Derived &&)>, true>);
478
479 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(const Derived&)>, true>);
480 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(const Derived&)>, true>);
481
482 static_assert(
483 Convertible<ConvertCallable, InPlaceFunction<const Derived && (const Derived&&)>, true>);
484 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base && (const Derived&&)>, true>);
485
486 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(Derived&)>, true>);
487 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(Derived&)>, true>);
488
489 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived && (Derived &&)>, true>);
490 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base && (Derived &&)>, true>);
491
492 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Derived&(Derived&&)>, true>);
493 static_assert(Convertible<ConvertCallable, InPlaceFunction<const Base&(Derived&&)>, true>);
494