1#include <c10/test/util/Macros.h>
2#include <c10/util/Metaprogramming.h>
3#include <gtest/gtest.h>
4#include <cstdlib>
5
6using namespace c10::guts;
7
8namespace {
9
10namespace test_function_traits {
11static_assert(
12 std::is_same<
13 void,
14 typename function_traits<void(int, float)>::return_type>::value,
15 "");
16static_assert(
17 std::is_same<int, typename function_traits<int(int, float)>::return_type>::
18 value,
19 "");
20static_assert(
21 std::is_same<
22 typelist::typelist<int, float>,
23 typename function_traits<void(int, float)>::parameter_types>::value,
24 "");
25static_assert(
26 std::is_same<
27 typelist::typelist<int, float>,
28 typename function_traits<int(int, float)>::parameter_types>::value,
29 "");
30
31static_assert(
32 std::is_same<
33 bool,
34 typename make_function_traits_t<bool, typelist::typelist<int, float>>::
35 return_type>::value,
36 "");
37static_assert(
38 std::is_same<
39 void,
40 typename make_function_traits_t<void, typelist::typelist<int, float>>::
41 return_type>::value,
42 "");
43static_assert(
44 std::is_same<
45 typelist::typelist<int, float>,
46 typename make_function_traits_t<bool, typelist::typelist<int, float>>::
47 parameter_types>::value,
48 "");
49static_assert(
50 std::is_same<
51 typelist::typelist<int, float>,
52 typename make_function_traits_t<void, typelist::typelist<int, float>>::
53 parameter_types>::value,
54 "");
55static_assert(
56 std::is_same<
57 bool(int, float),
58 typename make_function_traits_t<bool, typelist::typelist<int, float>>::
59 func_type>::value,
60 "");
61static_assert(
62 std::is_same<
63 void(int, float),
64 typename make_function_traits_t<void, typelist::typelist<int, float>>::
65 func_type>::value,
66 "");
67} // namespace test_function_traits
68
69struct MovableOnly {
70 constexpr MovableOnly(int val_) : val(val_) { /* no default constructor */
71 }
72 MovableOnly(const MovableOnly&) = delete;
73 MovableOnly(MovableOnly&&) = default;
74 MovableOnly& operator=(const MovableOnly&) = delete;
75 MovableOnly& operator=(MovableOnly&&) = default;
76
77 friend bool operator==(const MovableOnly& lhs, const MovableOnly& rhs) {
78 return lhs.val == rhs.val;
79 }
80
81 private:
82 int val;
83};
84
85template <class T>
86using is_my_movable_only_class =
87 std::is_same<MovableOnly, std::remove_cv_t<std::remove_reference_t<T>>>;
88
89struct CopyCounting {
90 int move_count;
91 int copy_count;
92
93 CopyCounting() : move_count(0), copy_count(0) {}
94 CopyCounting(const CopyCounting& rhs)
95 : move_count(rhs.move_count), copy_count(rhs.copy_count + 1) {}
96 CopyCounting(CopyCounting&& rhs)
97 : move_count(rhs.move_count + 1), copy_count(rhs.copy_count) {}
98 CopyCounting& operator=(const CopyCounting& rhs) {
99 move_count = rhs.move_count;
100 copy_count = rhs.copy_count + 1;
101 return *this;
102 }
103 CopyCounting& operator=(CopyCounting&& rhs) {
104 move_count = rhs.move_count + 1;
105 copy_count = rhs.copy_count;
106 return *this;
107 }
108};
109
110template <class T>
111using is_my_copy_counting_class =
112 std::is_same<CopyCounting, std::remove_cv_t<std::remove_reference_t<T>>>;
113
114namespace test_extract_arg_by_filtered_index {
115class MyClass {};
116
117TEST(MetaprogrammingTest, ExtractArgByFilteredIndex) {
118 auto a1 = extract_arg_by_filtered_index<std::is_integral, 0>(
119 3, "bla", MyClass(), 4, nullptr, 5);
120 auto a2 = extract_arg_by_filtered_index<std::is_integral, 1>(
121 3, "bla", MyClass(), 4, nullptr, 5);
122 auto a3 = extract_arg_by_filtered_index<std::is_integral, 2>(
123 3, "bla", MyClass(), 4, nullptr, 5);
124 EXPECT_EQ(3, a1);
125 EXPECT_EQ(4, a2);
126 EXPECT_EQ(5, a3);
127}
128
129TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_singleInput) {
130 auto a1 = extract_arg_by_filtered_index<std::is_integral, 0>(3);
131 EXPECT_EQ(3, a1);
132}
133
134TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_movableOnly) {
135 MovableOnly a1 = extract_arg_by_filtered_index<is_my_movable_only_class, 0>(
136 3, MovableOnly(3), "test", MovableOnly(1));
137 MovableOnly a2 = extract_arg_by_filtered_index<is_my_movable_only_class, 1>(
138 3, MovableOnly(3), "test", MovableOnly(1));
139 EXPECT_EQ(MovableOnly(3), a1);
140 EXPECT_EQ(MovableOnly(1), a2);
141}
142
143TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_onlyCopiesIfNecessary) {
144 CopyCounting source;
145 CopyCounting source2;
146 CopyCounting a1 = extract_arg_by_filtered_index<is_my_copy_counting_class, 0>(
147 3, CopyCounting(), "test", source, std::move(source2));
148 // NOLINTNEXTLINE(bugprone-use-after-move)
149 CopyCounting a2 = extract_arg_by_filtered_index<is_my_copy_counting_class, 1>(
150 3, CopyCounting(), "test", source, std::move(source2));
151 // NOLINTNEXTLINE(bugprone-use-after-move)
152 CopyCounting a3 = extract_arg_by_filtered_index<is_my_copy_counting_class, 2>(
153 3, CopyCounting(), "test", source, std::move(source2));
154 EXPECT_EQ(1, a1.move_count);
155 EXPECT_EQ(0, a1.copy_count);
156 EXPECT_EQ(0, a2.move_count);
157 EXPECT_EQ(1, a3.move_count);
158 EXPECT_EQ(0, a3.copy_count);
159 EXPECT_EQ(1, a2.copy_count);
160}
161
162TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_onlyMovesIfNecessary) {
163 CopyCounting source;
164 CopyCounting source2;
165 CopyCounting&& a1 =
166 extract_arg_by_filtered_index<is_my_copy_counting_class, 0>(
167 3, std::move(source), "test", std::move(source2));
168 // NOLINTNEXTLINE(bugprone-use-after-move)
169 CopyCounting a2 = extract_arg_by_filtered_index<is_my_copy_counting_class, 1>(
170 3, std::move(source), "test", std::move(source2));
171 EXPECT_EQ(0, a1.move_count);
172 EXPECT_EQ(0, a1.copy_count);
173 EXPECT_EQ(1, a2.move_count);
174 EXPECT_EQ(0, a2.copy_count);
175}
176
177template <class T>
178using is_true = std::true_type;
179
180TEST(
181 MetaprogrammingTest,
182 ExtractArgByFilteredIndex_keepsLValueReferencesIntact) {
183 MyClass obj;
184 MyClass& a1 = extract_arg_by_filtered_index<is_true, 1>(3, obj, "test", obj);
185 EXPECT_EQ(&obj, &a1);
186}
187} // namespace test_extract_arg_by_filtered_index
188
189namespace test_filter_map {
190class MyClass {};
191
192struct map_to_double {
193 template <class T>
194 constexpr double operator()(T a) const {
195 return static_cast<double>(a);
196 }
197};
198
199TEST(MetaprogrammingTest, FilterMap) {
200 auto result = filter_map<double, std::is_integral>(
201 map_to_double(), 3, "bla", MyClass(), 4, nullptr, 5);
202 static_assert(std::is_same<array<double, 3>, decltype(result)>::value, "");
203 constexpr array<double, 3> expected{{3.0, 4.0, 5.0}};
204 EXPECT_EQ(expected, result);
205}
206
207TEST(MetaprogrammingTest, FilterMap_emptyInput) {
208 auto result = filter_map<double, std::is_integral>(map_to_double());
209 static_assert(std::is_same<array<double, 0>, decltype(result)>::value, "");
210 constexpr array<double, 0> expected{{}};
211 EXPECT_EQ(expected, result);
212}
213
214TEST(MetaprogrammingTest, FilterMap_emptyOutput) {
215 auto result = filter_map<double, std::is_integral>(
216 map_to_double(), "bla", MyClass(), nullptr);
217 static_assert(std::is_same<array<double, 0>, decltype(result)>::value, "");
218 constexpr array<double, 0> expected{{}};
219 EXPECT_EQ(expected, result);
220}
221
222TEST(MetaprogrammingTest, FilterMap_movableOnly_byRValue) {
223 struct map_movable_by_rvalue {
224 MovableOnly operator()(MovableOnly&& a) const {
225 return std::move(a);
226 }
227 };
228
229 auto result = filter_map<MovableOnly, is_my_movable_only_class>(
230 map_movable_by_rvalue(),
231 MovableOnly(5),
232 "bla",
233 nullptr,
234 3,
235 MovableOnly(2));
236 static_assert(
237 std::is_same<array<MovableOnly, 2>, decltype(result)>::value, "");
238 constexpr array<MovableOnly, 2> expected{{MovableOnly(5), MovableOnly(2)}};
239 EXPECT_EQ(expected, result);
240}
241
242TEST(MetaprogrammingTest, FilterMap_movableOnly_byValue) {
243 struct map_movable_by_lvalue {
244 MovableOnly operator()(MovableOnly a) const {
245 return a;
246 }
247 };
248
249 auto result = filter_map<MovableOnly, is_my_movable_only_class>(
250 map_movable_by_lvalue(),
251 MovableOnly(5),
252 "bla",
253 nullptr,
254 3,
255 MovableOnly(2));
256 static_assert(
257 std::is_same<array<MovableOnly, 2>, decltype(result)>::value, "");
258 constexpr array<MovableOnly, 2> expected{{MovableOnly(5), MovableOnly(2)}};
259 EXPECT_EQ(expected, result);
260}
261
262// See https://github.com/pytorch/pytorch/issues/35546
263TEST(MetaprogrammingTest, FilterMap_onlyCopiesIfNecessary) {
264 struct map_copy_counting_by_copy {
265 CopyCounting operator()(CopyCounting v) const {
266 return v;
267 }
268 };
269
270 CopyCounting source;
271 CopyCounting source2;
272 auto result = filter_map<CopyCounting, is_my_copy_counting_class>(
273 map_copy_counting_by_copy(),
274 CopyCounting(),
275 "bla",
276 nullptr,
277 3,
278 source,
279 std::move(source2));
280 static_assert(
281 std::is_same<array<CopyCounting, 3>, decltype(result)>::value, "");
282 EXPECT_EQ(0, result[0].copy_count);
283 EXPECT_EQ(2, result[0].move_count);
284 EXPECT_EQ(1, result[1].copy_count);
285 EXPECT_EQ(1, result[1].move_count);
286 EXPECT_EQ(0, result[2].copy_count);
287 EXPECT_EQ(2, result[2].move_count);
288}
289
290TEST(MetaprogrammingTest, FilterMap_onlyMovesIfNecessary_1) {
291 struct map_copy_counting_by_move {
292 CopyCounting operator()(CopyCounting&& v) const {
293 return std::move(v);
294 }
295 };
296
297 CopyCounting source;
298 auto result = filter_map<CopyCounting, is_my_copy_counting_class>(
299 map_copy_counting_by_move(),
300 CopyCounting(),
301 "bla",
302 nullptr,
303 3,
304 std::move(source));
305 static_assert(
306 std::is_same<array<CopyCounting, 2>, decltype(result)>::value, "");
307 EXPECT_EQ(0, result[0].copy_count);
308 EXPECT_EQ(1, result[0].move_count);
309 EXPECT_EQ(0, result[1].copy_count);
310 EXPECT_EQ(1, result[1].move_count);
311}
312
313TEST(MetaprogrammingTest, FilterMap_onlyMovesIfNecessary_2) {
314 struct map_copy_counting_by_pointer {
315 const CopyCounting* operator()(const CopyCounting& v) const {
316 return &v;
317 }
318 };
319
320 CopyCounting source1;
321 CopyCounting source2;
322 auto result = filter_map<const CopyCounting*, is_my_copy_counting_class>(
323 map_copy_counting_by_pointer(),
324 "bla",
325 nullptr,
326 3,
327 source1,
328 std::move(source2));
329 static_assert(
330 std::is_same<array<const CopyCounting*, 2>, decltype(result)>::value, "");
331 EXPECT_EQ(0, result[0]->copy_count);
332 EXPECT_EQ(0, result[0]->move_count);
333 EXPECT_EQ(0, result[1]->copy_count);
334 EXPECT_EQ(0, result[1]->move_count);
335}
336} // namespace test_filter_map
337
338namespace test_tuple_elements {
339// note: not testing empty selection, as some compilers will raise
340// "parameter set but not used" in tuple_elements(). a good example
341// of the friction that comes with using these tools
342
343TEST(MetaprogrammingTest, TupleElements_subsetSelection) {
344 auto x = std::make_tuple(0, "HEY", 2.0);
345 auto y = tuple_elements(x, std::index_sequence<0, 2>());
346 auto z = std::make_tuple(0, 2.0);
347 EXPECT_EQ(y, z);
348}
349
350TEST(MetaprogrammingTest, TupleElements_reorderSelection) {
351 auto x = std::make_tuple(0, "HEY", 2.0);
352 auto y = tuple_elements(x, std::index_sequence<0, 2, 1>());
353 auto z = std::make_tuple(0, 2.0, "HEY");
354 EXPECT_EQ(y, z);
355}
356} // namespace test_tuple_elements
357
358namespace test_tuple_take {
359// note: not testing empty prefix, see note on empty selection above.
360
361TEST(MetaprogrammingTest, TupleTake_nonemptyPrefix) {
362 auto x = std::make_tuple(0, "HEY", 2.0);
363 auto y = tuple_take<decltype(x), 2>(x);
364 auto z = std::make_tuple(0, "HEY");
365 EXPECT_EQ(y, z);
366}
367
368TEST(MetaprogrammingTest, TupleTake_fullPrefix) {
369 auto x = std::make_tuple(0, "HEY", 2.0);
370 auto y = tuple_take<decltype(x), 3>(x);
371 EXPECT_EQ(x, y);
372}
373
374TEST(MetaprogrammingTest, TupleTake_negative) {
375 auto x = std::make_tuple(0, "HEY", 2.0);
376 auto y = tuple_take<decltype(x), -2>(x);
377 auto z = std::make_tuple("HEY", 2.0);
378 EXPECT_EQ(y, z);
379}
380} // namespace test_tuple_take
381
382namespace test_tuple_slice {
383TEST(MetaprogrammingTest, TupleSlice_middle) {
384 auto x = std::make_tuple(0, "HEY", 2.0, false);
385 auto y = tuple_slice<decltype(x), 1, 2>(x);
386 auto z = std::make_tuple("HEY", 2.0);
387 EXPECT_EQ(y, z);
388}
389
390TEST(MetaprogrammingTest, TupleSlice_full) {
391 auto x = std::make_tuple(0, "HEY", 2.0);
392 auto y = tuple_slice<decltype(x), 0, 3>(x);
393 EXPECT_EQ(x, y);
394}
395} // namespace test_tuple_slice
396
397namespace test_tuple_map {
398TEST(MetaprogrammingTest, TupleMap_simple) {
399 auto result = tuple_map(
400 std::tuple<int32_t, int32_t, int32_t>(3, 4, 5),
401 [](int32_t a) -> int16_t { return a + 1; });
402 static_assert(
403 std::is_same<std::tuple<int16_t, int16_t, int16_t>, decltype(result)>::
404 value,
405 "");
406 EXPECT_EQ(4, std::get<0>(result));
407 EXPECT_EQ(5, std::get<1>(result));
408 EXPECT_EQ(6, std::get<2>(result));
409}
410
411TEST(MetaprogrammingTest, TupleMap_mapperTakesDifferentButConvertibleType) {
412 auto result = tuple_map(
413 std::tuple<int32_t, int32_t, int32_t>(3, 4, 5),
414 [](int64_t a) -> int16_t { return a + 1; });
415 static_assert(
416 std::is_same<std::tuple<int16_t, int16_t, int16_t>, decltype(result)>::
417 value,
418 "");
419 EXPECT_EQ(4, std::get<0>(result));
420 EXPECT_EQ(5, std::get<1>(result));
421 EXPECT_EQ(6, std::get<2>(result));
422}
423
424TEST(MetaprogrammingTest, TupleMap_mapperTakesConstRef) {
425 auto result = tuple_map(
426 std::tuple<int32_t, int32_t, int32_t>(3, 4, 5),
427 [](const int32_t& a) -> int16_t { return a + 1; });
428 static_assert(
429 std::is_same<std::tuple<int16_t, int16_t, int16_t>, decltype(result)>::
430 value,
431 "");
432 EXPECT_EQ(4, std::get<0>(result));
433 EXPECT_EQ(5, std::get<1>(result));
434 EXPECT_EQ(6, std::get<2>(result));
435}
436
437TEST(MetaprogrammingTest, TupleMap_mapsToDifferentTypes) {
438 struct Mapper {
439 std::string operator()(int32_t a) const {
440 return std::to_string(a);
441 }
442 int32_t operator()(const std::string& a) const {
443 return atoi(a.c_str());
444 }
445 };
446 auto result = tuple_map(std::tuple<int32_t, std::string>(3, "4"), Mapper());
447 static_assert(
448 std::is_same<std::tuple<std::string, int32_t>, decltype(result)>::value,
449 "");
450 EXPECT_EQ("3", std::get<0>(result));
451 EXPECT_EQ(4, std::get<1>(result));
452}
453
454TEST(MetaprogrammingTest, TupleMap_differentiatesLRValueReferences) {
455 struct Mapper {
456 std::string operator()(std::string&& a) const {
457 return "moved";
458 }
459 std::string operator()(const std::string& a) const {
460 return "copied";
461 }
462 };
463 std::string str1, str2;
464 auto result = tuple_map(
465 std::tuple<const std::string&, std::string&&>(str1, std::move(str2)),
466 Mapper());
467 static_assert(
468 std::is_same<std::tuple<std::string, std::string>, decltype(result)>::
469 value,
470 "");
471 EXPECT_EQ("copied", std::get<0>(result));
472 EXPECT_EQ("moved", std::get<1>(result));
473}
474
475TEST(MetaprogrammingTest, TupleMap_canWorkWithMovableOnlyType) {
476 auto result = tuple_map(
477 std::tuple<MovableOnly>(MovableOnly(7)), [](MovableOnly a) { return a; });
478 static_assert(
479 std::is_same<std::tuple<MovableOnly>, decltype(result)>::value, "");
480 EXPECT_EQ(MovableOnly(7), std::get<0>(result));
481}
482
483TEST(MetaprogrammingTest, TupleMap_doesntUnecessarilyCopyValues) {
484 auto result = tuple_map(
485 std::tuple<CopyCounting>(CopyCounting()),
486 [](CopyCounting a) { return a; });
487 static_assert(
488 std::is_same<std::tuple<CopyCounting>, decltype(result)>::value, "");
489 EXPECT_EQ(4, std::get<0>(result).move_count);
490 EXPECT_EQ(0, std::get<0>(result).copy_count);
491}
492
493TEST(MetaprogrammingTest, TupleMap_doesntUnecessarilyMoveValues) {
494 CopyCounting a;
495 auto result = tuple_map(
496 std::tuple<CopyCounting&&>(std::move(a)),
497 [](CopyCounting&& a) -> CopyCounting&& { return std::move(a); });
498 static_assert(
499 std::is_same<std::tuple<CopyCounting&&>, decltype(result)>::value, "");
500 EXPECT_EQ(&a, &std::get<0>(result));
501 EXPECT_EQ(0, std::get<0>(result).move_count);
502 EXPECT_EQ(0, std::get<0>(result).copy_count);
503}
504
505TEST(MetaprogrammingTest, TupleMap_canBeUsedWithAutoLambdas) {
506 struct A final {
507 int32_t func() {
508 return 5;
509 }
510 };
511 struct B final {
512 std::string func() {
513 return "5";
514 }
515 };
516 auto result =
517 tuple_map(std::make_tuple(A(), B()), [](auto a) { return a.func(); });
518 static_assert(
519 std::is_same<std::tuple<int32_t, std::string>, decltype(result)>::value,
520 "");
521 EXPECT_EQ(5, std::get<0>(result));
522 EXPECT_EQ("5", std::get<1>(result));
523}
524} // namespace test_tuple_map
525
526namespace test_tuple_concat {
527TEST(MetaprogrammingTest, TupleConcat_zerotuples) {
528 auto result = tuple_concat();
529 static_assert(std::is_same<std::tuple<>, decltype(result)>::value, "");
530}
531
532TEST(MetaprogrammingTest, TupleConcat_oneemptytuple) {
533 auto result = tuple_concat(std::tuple<>());
534 static_assert(std::is_same<std::tuple<>, decltype(result)>::value, "");
535}
536
537TEST(MetaprogrammingTest, TupleConcat_onenonemptytuple) {
538 auto result = tuple_concat(std::tuple<int64_t>(3));
539 static_assert(std::is_same<std::tuple<int64_t>, decltype(result)>::value, "");
540 EXPECT_EQ(3, std::get<0>(result));
541}
542
543TEST(MetaprogrammingTest, TupleConcat_twotuples) {
544 auto result = tuple_concat(
545 std::tuple<int64_t, std::string>(3, "4"),
546 std::tuple<double, int16_t>(2.3, 15));
547 static_assert(
548 std::is_same<
549 std::tuple<int64_t, std::string, double, int16_t>,
550 decltype(result)>::value,
551 "");
552 EXPECT_EQ(3, std::get<0>(result));
553 EXPECT_EQ("4", std::get<1>(result));
554 EXPECT_EQ(2.3, std::get<2>(result));
555 EXPECT_EQ(15, std::get<3>(result));
556}
557
558TEST(MetaprogrammingTest, TupleConcat_threetuples) {
559 auto result = tuple_concat(
560 std::tuple<int64_t, std::string>(3, "4"),
561 std::tuple<double, int16_t>(2.3, 15),
562 std::tuple<std::string, float>("5", 3.2));
563 static_assert(
564 std::is_same<
565 std::tuple<int64_t, std::string, double, int16_t, std::string, float>,
566 decltype(result)>::value,
567 "");
568 EXPECT_EQ(3, std::get<0>(result));
569 EXPECT_EQ("4", std::get<1>(result));
570 EXPECT_EQ(2.3, std::get<2>(result));
571 EXPECT_EQ(15, std::get<3>(result));
572 EXPECT_EQ("5", std::get<4>(result));
573 EXPECT_EQ(static_cast<float>(3.2), std::get<5>(result));
574}
575
576TEST(MetaprogrammingTest, TupleConcat_emptytupleatbeginning) {
577 auto result = tuple_concat(
578 std::tuple<>(),
579 std::tuple<double, int16_t>(2.3, 15),
580 std::tuple<std::string, float>("5", 3.2));
581 static_assert(
582 std::is_same<
583 std::tuple<double, int16_t, std::string, float>,
584 decltype(result)>::value,
585 "");
586 EXPECT_EQ(2.3, std::get<0>(result));
587 EXPECT_EQ(15, std::get<1>(result));
588 EXPECT_EQ("5", std::get<2>(result));
589 EXPECT_EQ(static_cast<float>(3.2), std::get<3>(result));
590}
591
592TEST(MetaprogrammingTest, TupleConcat_emptytupleinmiddle) {
593 auto result = tuple_concat(
594 std::tuple<double, int16_t>(2.3, 15),
595 std::tuple<>(),
596 std::tuple<std::string, float>("5", 3.2));
597 static_assert(
598 std::is_same<
599 std::tuple<double, int16_t, std::string, float>,
600 decltype(result)>::value,
601 "");
602 EXPECT_EQ(2.3, std::get<0>(result));
603 EXPECT_EQ(15, std::get<1>(result));
604 EXPECT_EQ("5", std::get<2>(result));
605 EXPECT_EQ(static_cast<float>(3.2), std::get<3>(result));
606}
607
608TEST(MetaprogrammingTest, TupleConcat_emptytupleatend) {
609 auto result = tuple_concat(
610 std::tuple<double, int16_t>(2.3, 15),
611 std::tuple<std::string, float>("5", 3.2),
612 std::tuple<>());
613 static_assert(
614 std::is_same<
615 std::tuple<double, int16_t, std::string, float>,
616 decltype(result)>::value,
617 "");
618 EXPECT_EQ(2.3, std::get<0>(result));
619 EXPECT_EQ(15, std::get<1>(result));
620 EXPECT_EQ("5", std::get<2>(result));
621 EXPECT_EQ(static_cast<float>(3.2), std::get<3>(result));
622}
623
624TEST(MetaprogrammingTest, TupleConcat_workswithreferencesandpointers) {
625 double val1 = 2.3;
626 int16_t val2 = 15;
627 std::string val3 = "hello";
628 float val4 = 3.2;
629 auto result = tuple_concat(
630 std::tuple<double&, const int16_t&>(val1, val2),
631 std::tuple<std::string&&, float*>(std::move(val3), &val4));
632 static_assert(
633 std::is_same<
634 std::tuple<double&, const int16_t&, std::string&&, float*>,
635 decltype(result)>::value,
636 "");
637 EXPECT_EQ(2.3, std::get<0>(result));
638 EXPECT_EQ(&val1, &std::get<0>(result));
639 EXPECT_EQ(15, std::get<1>(result));
640 EXPECT_EQ(&val2, &std::get<1>(result));
641 EXPECT_EQ("hello", std::get<2>(result));
642 EXPECT_EQ(&val3, &std::get<2>(result));
643 EXPECT_EQ(static_cast<float>(3.2), *std::get<3>(result));
644 EXPECT_EQ(&val4, std::get<3>(result));
645}
646
647TEST(MetaprogrammingTest, TupleConcat_worksWithMovableOnlyTypes) {
648 auto result = tuple_concat(
649 std::tuple<MovableOnly, MovableOnly>(1, 2), std::tuple<MovableOnly>(3));
650 static_assert(
651 std::is_same<
652 std::tuple<MovableOnly, MovableOnly, MovableOnly>,
653 decltype(result)>::value,
654 "");
655 EXPECT_EQ(MovableOnly(1), std::get<0>(result));
656 EXPECT_EQ(MovableOnly(2), std::get<1>(result));
657 EXPECT_EQ(MovableOnly(3), std::get<2>(result));
658}
659
660TEST(MetaprogrammingTest, TupleConcat_doesntCopyMoreThanNecessary) {
661 auto result = tuple_concat(
662 std::tuple<CopyCounting, CopyCounting>(CopyCounting(), CopyCounting()),
663 std::tuple<CopyCounting>(CopyCounting()),
664 std::tuple<CopyCounting>(CopyCounting()));
665 static_assert(
666 std::is_same<
667 std::tuple<CopyCounting, CopyCounting, CopyCounting, CopyCounting>,
668 decltype(result)>::value,
669 "");
670 EXPECT_EQ(0, std::get<0>(result).copy_count);
671 EXPECT_EQ(0, std::get<1>(result).copy_count);
672 EXPECT_EQ(0, std::get<2>(result).copy_count);
673 EXPECT_EQ(0, std::get<3>(result).copy_count);
674 EXPECT_EQ(2, std::get<0>(result).move_count);
675 EXPECT_EQ(2, std::get<1>(result).move_count);
676 EXPECT_EQ(2, std::get<2>(result).move_count);
677 EXPECT_EQ(2, std::get<3>(result).move_count);
678}
679} // namespace test_tuple_concat
680
681namespace test_concat_iseq {
682using std::index_sequence;
683using std::integer_sequence;
684static_assert(std::is_same<index_sequence<>, concat_iseq_t<>>::value, "");
685static_assert(
686 std::is_same<index_sequence<>, concat_iseq_t<index_sequence<>>>::value,
687 "");
688static_assert(
689 std::is_same<
690 index_sequence<>,
691 concat_iseq_t<index_sequence<>, index_sequence<>>>::value,
692 "");
693static_assert(
694 std::is_same<index_sequence<4>, concat_iseq_t<index_sequence<4>>>::value,
695 "");
696static_assert(
697 std::is_same<
698 index_sequence<4>,
699 concat_iseq_t<index_sequence<4>, index_sequence<>>>::value,
700 "");
701static_assert(
702 std::is_same<
703 index_sequence<4>,
704 concat_iseq_t<index_sequence<>, index_sequence<4>>>::value,
705 "");
706static_assert(
707 std::is_same<
708 index_sequence<4>,
709 concat_iseq_t<index_sequence<>, index_sequence<4>, index_sequence<>>>::
710 value,
711 "");
712static_assert(
713 std::is_same<
714 index_sequence<4, 2>,
715 concat_iseq_t<index_sequence<4>, index_sequence<2>>>::value,
716 "");
717static_assert(
718 std::is_same<
719 index_sequence<4, 2>,
720 concat_iseq_t<
721 index_sequence<>,
722 index_sequence<4, 2>,
723 index_sequence<>>>::value,
724 "");
725static_assert(
726 std::is_same<
727 index_sequence<4, 2, 9>,
728 concat_iseq_t<
729 index_sequence<>,
730 index_sequence<4, 2>,
731 index_sequence<9>>>::value,
732 "");
733
734static_assert(
735 std::is_same<
736 integer_sequence<int8_t, -5, -3>,
737 concat_iseq_t<
738 integer_sequence<int8_t, -5>,
739 integer_sequence<int8_t, -3>>>::value,
740 "");
741} // namespace test_concat_iseq
742
743} // namespace
744