1 | #include <c10/test/util/Macros.h> |
2 | #include <c10/util/Metaprogramming.h> |
3 | #include <gtest/gtest.h> |
4 | #include <cstdlib> |
5 | |
6 | using namespace c10::guts; |
7 | |
8 | namespace { |
9 | |
10 | namespace test_function_traits { |
11 | static_assert( |
12 | std::is_same< |
13 | void, |
14 | typename function_traits<void(int, float)>::return_type>::value, |
15 | "" ); |
16 | static_assert( |
17 | std::is_same<int, typename function_traits<int(int, float)>::return_type>:: |
18 | value, |
19 | "" ); |
20 | static_assert( |
21 | std::is_same< |
22 | typelist::typelist<int, float>, |
23 | typename function_traits<void(int, float)>::parameter_types>::value, |
24 | "" ); |
25 | static_assert( |
26 | std::is_same< |
27 | typelist::typelist<int, float>, |
28 | typename function_traits<int(int, float)>::parameter_types>::value, |
29 | "" ); |
30 | |
31 | static_assert( |
32 | std::is_same< |
33 | bool, |
34 | typename make_function_traits_t<bool, typelist::typelist<int, float>>:: |
35 | return_type>::value, |
36 | "" ); |
37 | static_assert( |
38 | std::is_same< |
39 | void, |
40 | typename make_function_traits_t<void, typelist::typelist<int, float>>:: |
41 | return_type>::value, |
42 | "" ); |
43 | static_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 | "" ); |
49 | static_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 | "" ); |
55 | static_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 | "" ); |
61 | static_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 | |
69 | struct 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 | |
85 | template <class T> |
86 | using is_my_movable_only_class = |
87 | std::is_same<MovableOnly, std::remove_cv_t<std::remove_reference_t<T>>>; |
88 | |
89 | struct 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 | |
110 | template <class T> |
111 | using is_my_copy_counting_class = |
112 | std::is_same<CopyCounting, std::remove_cv_t<std::remove_reference_t<T>>>; |
113 | |
114 | namespace test_extract_arg_by_filtered_index { |
115 | class {}; |
116 | |
117 | TEST(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 | |
129 | TEST(MetaprogrammingTest, ExtractArgByFilteredIndex_singleInput) { |
130 | auto a1 = extract_arg_by_filtered_index<std::is_integral, 0>(3); |
131 | EXPECT_EQ(3, a1); |
132 | } |
133 | |
134 | TEST(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 | |
143 | TEST(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 | |
162 | TEST(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 | |
177 | template <class T> |
178 | using = std::true_type; |
179 | |
180 | TEST( |
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 | |
189 | namespace test_filter_map { |
190 | class MyClass {}; |
191 | |
192 | struct map_to_double { |
193 | template <class T> |
194 | constexpr double operator()(T a) const { |
195 | return static_cast<double>(a); |
196 | } |
197 | }; |
198 | |
199 | TEST(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 | |
207 | TEST(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 | |
214 | TEST(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 | |
222 | TEST(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 | |
242 | TEST(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 |
263 | TEST(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 | |
290 | TEST(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 | |
313 | TEST(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 | |
338 | namespace 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 | |
343 | TEST(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 | |
350 | TEST(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 | |
358 | namespace test_tuple_take { |
359 | // note: not testing empty prefix, see note on empty selection above. |
360 | |
361 | TEST(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 | |
368 | TEST(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 | |
374 | TEST(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 | |
382 | namespace test_tuple_slice { |
383 | TEST(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 | |
390 | TEST(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 | |
397 | namespace test_tuple_map { |
398 | TEST(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 | |
411 | TEST(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 | |
424 | TEST(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 | |
437 | TEST(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 | |
454 | TEST(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 | |
475 | TEST(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 | |
483 | TEST(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 | |
493 | TEST(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 | |
505 | TEST(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 | |
526 | namespace test_tuple_concat { |
527 | TEST(MetaprogrammingTest, TupleConcat_zerotuples) { |
528 | auto result = tuple_concat(); |
529 | static_assert(std::is_same<std::tuple<>, decltype(result)>::value, "" ); |
530 | } |
531 | |
532 | TEST(MetaprogrammingTest, TupleConcat_oneemptytuple) { |
533 | auto result = tuple_concat(std::tuple<>()); |
534 | static_assert(std::is_same<std::tuple<>, decltype(result)>::value, "" ); |
535 | } |
536 | |
537 | TEST(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 | |
543 | TEST(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 | |
558 | TEST(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 | |
576 | TEST(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 | |
592 | TEST(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 | |
608 | TEST(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 | |
624 | TEST(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 | |
647 | TEST(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 | |
660 | TEST(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 | |
681 | namespace test_concat_iseq { |
682 | using std::index_sequence; |
683 | using std::integer_sequence; |
684 | static_assert(std::is_same<index_sequence<>, concat_iseq_t<>>::value, "" ); |
685 | static_assert( |
686 | std::is_same<index_sequence<>, concat_iseq_t<index_sequence<>>>::value, |
687 | "" ); |
688 | static_assert( |
689 | std::is_same< |
690 | index_sequence<>, |
691 | concat_iseq_t<index_sequence<>, index_sequence<>>>::value, |
692 | "" ); |
693 | static_assert( |
694 | std::is_same<index_sequence<4>, concat_iseq_t<index_sequence<4>>>::value, |
695 | "" ); |
696 | static_assert( |
697 | std::is_same< |
698 | index_sequence<4>, |
699 | concat_iseq_t<index_sequence<4>, index_sequence<>>>::value, |
700 | "" ); |
701 | static_assert( |
702 | std::is_same< |
703 | index_sequence<4>, |
704 | concat_iseq_t<index_sequence<>, index_sequence<4>>>::value, |
705 | "" ); |
706 | static_assert( |
707 | std::is_same< |
708 | index_sequence<4>, |
709 | concat_iseq_t<index_sequence<>, index_sequence<4>, index_sequence<>>>:: |
710 | value, |
711 | "" ); |
712 | static_assert( |
713 | std::is_same< |
714 | index_sequence<4, 2>, |
715 | concat_iseq_t<index_sequence<4>, index_sequence<2>>>::value, |
716 | "" ); |
717 | static_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 | "" ); |
725 | static_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 | |
734 | static_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 | |