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 // NOLINTBEGIN(modernize*)
9 namespace {
10
11 namespace test_function_traits {
12 static_assert(
13 std::is_same<
14 void,
15 typename function_traits<void(int, float)>::return_type>::value,
16 "");
17 static_assert(
18 std::is_same<int, typename function_traits<int(int, float)>::return_type>::
19 value,
20 "");
21 static_assert(
22 std::is_same<
23 typelist::typelist<int, float>,
24 typename function_traits<void(int, float)>::parameter_types>::value,
25 "");
26 static_assert(
27 std::is_same<
28 typelist::typelist<int, float>,
29 typename function_traits<int(int, float)>::parameter_types>::value,
30 "");
31
32 static_assert(
33 std::is_same<
34 bool,
35 typename make_function_traits_t<bool, typelist::typelist<int, float>>::
36 return_type>::value,
37 "");
38 static_assert(
39 std::is_same<
40 void,
41 typename make_function_traits_t<void, typelist::typelist<int, float>>::
42 return_type>::value,
43 "");
44 static_assert(
45 std::is_same<
46 typelist::typelist<int, float>,
47 typename make_function_traits_t<bool, typelist::typelist<int, float>>::
48 parameter_types>::value,
49 "");
50 static_assert(
51 std::is_same<
52 typelist::typelist<int, float>,
53 typename make_function_traits_t<void, typelist::typelist<int, float>>::
54 parameter_types>::value,
55 "");
56 static_assert(
57 std::is_same<
58 bool(int, float),
59 typename make_function_traits_t<bool, typelist::typelist<int, float>>::
60 func_type>::value,
61 "");
62 static_assert(
63 std::is_same<
64 void(int, float),
65 typename make_function_traits_t<void, typelist::typelist<int, float>>::
66 func_type>::value,
67 "");
68 } // namespace test_function_traits
69
70 struct MovableOnly {
MovableOnly__anonbe8862e80111::MovableOnly71 constexpr MovableOnly(int val_) : val(val_) { /* no default constructor */
72 }
73 MovableOnly(const MovableOnly&) = delete;
74 MovableOnly(MovableOnly&&) = default;
75 MovableOnly& operator=(const MovableOnly&) = delete;
76 MovableOnly& operator=(MovableOnly&&) = default;
77
operator ==(const MovableOnly & lhs,const MovableOnly & rhs)78 friend bool operator==(const MovableOnly& lhs, const MovableOnly& rhs) {
79 return lhs.val == rhs.val;
80 }
81
82 private:
83 int val;
84 };
85
86 template <class T>
87 using is_my_movable_only_class =
88 std::is_same<MovableOnly, std::remove_cv_t<std::remove_reference_t<T>>>;
89
90 struct CopyCounting {
91 int move_count;
92 int copy_count;
93
CopyCounting__anonbe8862e80111::CopyCounting94 CopyCounting() : move_count(0), copy_count(0) {}
CopyCounting__anonbe8862e80111::CopyCounting95 CopyCounting(const CopyCounting& rhs)
96 : move_count(rhs.move_count), copy_count(rhs.copy_count + 1) {}
CopyCounting__anonbe8862e80111::CopyCounting97 CopyCounting(CopyCounting&& rhs) noexcept
98 : move_count(rhs.move_count + 1), copy_count(rhs.copy_count) {}
operator =__anonbe8862e80111::CopyCounting99 CopyCounting& operator=(const CopyCounting& rhs) {
100 move_count = rhs.move_count;
101 copy_count = rhs.copy_count + 1;
102 return *this;
103 }
operator =__anonbe8862e80111::CopyCounting104 CopyCounting& operator=(CopyCounting&& rhs) noexcept {
105 move_count = rhs.move_count + 1;
106 copy_count = rhs.copy_count;
107 return *this;
108 }
109 };
110
111 template <class T>
112 using is_my_copy_counting_class =
113 std::is_same<CopyCounting, std::remove_cv_t<std::remove_reference_t<T>>>;
114
115 namespace test_tuple_elements {
116 // note: not testing empty selection, as some compilers will raise
117 // "parameter set but not used" in tuple_elements(). a good example
118 // of the friction that comes with using these tools
119
TEST(MetaprogrammingTest,TupleElements_subsetSelection)120 TEST(MetaprogrammingTest, TupleElements_subsetSelection) {
121 auto x = std::make_tuple(0, "HEY", 2.0);
122 auto y = tuple_elements(x, std::index_sequence<0, 2>());
123 auto z = std::make_tuple(0, 2.0);
124 EXPECT_EQ(y, z);
125 }
126
TEST(MetaprogrammingTest,TupleElements_reorderSelection)127 TEST(MetaprogrammingTest, TupleElements_reorderSelection) {
128 auto x = std::make_tuple(0, "HEY", 2.0);
129 auto y = tuple_elements(x, std::index_sequence<0, 2, 1>());
130 auto z = std::make_tuple(0, 2.0, "HEY");
131 EXPECT_EQ(y, z);
132 }
133 } // namespace test_tuple_elements
134
135 namespace test_tuple_take {
136 // note: not testing empty prefix, see note on empty selection above.
137
TEST(MetaprogrammingTest,TupleTake_nonemptyPrefix)138 TEST(MetaprogrammingTest, TupleTake_nonemptyPrefix) {
139 auto x = std::make_tuple(0, "HEY", 2.0);
140 auto y = tuple_take<decltype(x), 2>(x);
141 auto z = std::make_tuple(0, "HEY");
142 EXPECT_EQ(y, z);
143 }
144
TEST(MetaprogrammingTest,TupleTake_fullPrefix)145 TEST(MetaprogrammingTest, TupleTake_fullPrefix) {
146 auto x = std::make_tuple(0, "HEY", 2.0);
147 auto y = tuple_take<decltype(x), 3>(x);
148 EXPECT_EQ(x, y);
149 }
150
TEST(MetaprogrammingTest,TupleTake_negative)151 TEST(MetaprogrammingTest, TupleTake_negative) {
152 auto x = std::make_tuple(0, "HEY", 2.0);
153 auto y = tuple_take<decltype(x), -2>(x);
154 auto z = std::make_tuple("HEY", 2.0);
155 EXPECT_EQ(y, z);
156 }
157 } // namespace test_tuple_take
158
159 namespace test_tuple_slice {
TEST(MetaprogrammingTest,TupleSlice_middle)160 TEST(MetaprogrammingTest, TupleSlice_middle) {
161 auto x = std::make_tuple(0, "HEY", 2.0, false);
162 auto y = tuple_slice<decltype(x), 1, 2>(x);
163 auto z = std::make_tuple("HEY", 2.0);
164 EXPECT_EQ(y, z);
165 }
166
TEST(MetaprogrammingTest,TupleSlice_full)167 TEST(MetaprogrammingTest, TupleSlice_full) {
168 auto x = std::make_tuple(0, "HEY", 2.0);
169 auto y = tuple_slice<decltype(x), 0, 3>(x);
170 EXPECT_EQ(x, y);
171 }
172 } // namespace test_tuple_slice
173
174 namespace test_tuple_map {
TEST(MetaprogrammingTest,TupleMap_simple)175 TEST(MetaprogrammingTest, TupleMap_simple) {
176 auto result = tuple_map(
177 std::tuple<int32_t, int32_t, int32_t>(3, 4, 5),
178 [](int32_t a) -> int16_t { return static_cast<int16_t>(a + 1); });
179 static_assert(
180 std::is_same<std::tuple<int16_t, int16_t, int16_t>, decltype(result)>::
181 value,
182 "");
183 EXPECT_EQ(4, std::get<0>(result));
184 EXPECT_EQ(5, std::get<1>(result));
185 EXPECT_EQ(6, std::get<2>(result));
186 }
187
TEST(MetaprogrammingTest,TupleMap_mapperTakesDifferentButConvertibleType)188 TEST(MetaprogrammingTest, TupleMap_mapperTakesDifferentButConvertibleType) {
189 auto result = tuple_map(
190 std::tuple<int32_t, int32_t, int32_t>(3, 4, 5),
191 [](int64_t a) -> int16_t { return static_cast<int16_t>(a + 1); });
192 static_assert(
193 std::is_same<std::tuple<int16_t, int16_t, int16_t>, decltype(result)>::
194 value,
195 "");
196 EXPECT_EQ(4, std::get<0>(result));
197 EXPECT_EQ(5, std::get<1>(result));
198 EXPECT_EQ(6, std::get<2>(result));
199 }
200
TEST(MetaprogrammingTest,TupleMap_mapperTakesConstRef)201 TEST(MetaprogrammingTest, TupleMap_mapperTakesConstRef) {
202 auto result = tuple_map(
203 std::tuple<int32_t, int32_t, int32_t>(3, 4, 5),
204 [](const int32_t& a) -> int16_t { return static_cast<int16_t>(a + 1); });
205 static_assert(
206 std::is_same<std::tuple<int16_t, int16_t, int16_t>, decltype(result)>::
207 value,
208 "");
209 EXPECT_EQ(4, std::get<0>(result));
210 EXPECT_EQ(5, std::get<1>(result));
211 EXPECT_EQ(6, std::get<2>(result));
212 }
213
TEST(MetaprogrammingTest,TupleMap_mapsToDifferentTypes)214 TEST(MetaprogrammingTest, TupleMap_mapsToDifferentTypes) {
215 struct Mapper {
216 std::string operator()(int32_t a) const {
217 return std::to_string(a);
218 }
219 int32_t operator()(const std::string& a) const {
220 return atoi(a.c_str());
221 }
222 };
223 auto result = tuple_map(std::tuple<int32_t, std::string>(3, "4"), Mapper());
224 static_assert(
225 std::is_same<std::tuple<std::string, int32_t>, decltype(result)>::value,
226 "");
227 EXPECT_EQ("3", std::get<0>(result));
228 EXPECT_EQ(4, std::get<1>(result));
229 }
230
TEST(MetaprogrammingTest,TupleMap_differentiatesLRValueReferences)231 TEST(MetaprogrammingTest, TupleMap_differentiatesLRValueReferences) {
232 struct Mapper {
233 std::string operator()(std::string&& a) const {
234 return "moved";
235 }
236 std::string operator()(const std::string& a) const {
237 return "copied";
238 }
239 };
240 std::string str1, str2;
241 auto result = tuple_map(
242 std::tuple<const std::string&, std::string&&>(str1, std::move(str2)),
243 Mapper());
244 static_assert(
245 std::is_same<std::tuple<std::string, std::string>, decltype(result)>::
246 value,
247 "");
248 EXPECT_EQ("copied", std::get<0>(result));
249 EXPECT_EQ("moved", std::get<1>(result));
250 }
251
TEST(MetaprogrammingTest,TupleMap_canWorkWithMovableOnlyType)252 TEST(MetaprogrammingTest, TupleMap_canWorkWithMovableOnlyType) {
253 auto result = tuple_map(
254 std::tuple<MovableOnly>(MovableOnly(7)), [](MovableOnly a) { return a; });
255 static_assert(
256 std::is_same<std::tuple<MovableOnly>, decltype(result)>::value, "");
257 EXPECT_EQ(MovableOnly(7), std::get<0>(result));
258 }
259
TEST(MetaprogrammingTest,TupleMap_doesntUnecessarilyCopyValues)260 TEST(MetaprogrammingTest, TupleMap_doesntUnecessarilyCopyValues) {
261 auto result = tuple_map(
262 std::tuple<CopyCounting>(CopyCounting()),
263 [](CopyCounting a) { return a; });
264 static_assert(
265 std::is_same<std::tuple<CopyCounting>, decltype(result)>::value, "");
266 EXPECT_EQ(4, std::get<0>(result).move_count);
267 EXPECT_EQ(0, std::get<0>(result).copy_count);
268 }
269
TEST(MetaprogrammingTest,TupleMap_doesntUnecessarilyMoveValues)270 TEST(MetaprogrammingTest, TupleMap_doesntUnecessarilyMoveValues) {
271 CopyCounting a;
272 auto result = tuple_map(
273 std::tuple<CopyCounting&&>(std::move(a)),
274 [](CopyCounting&& a) -> CopyCounting&& { return std::move(a); });
275 static_assert(
276 std::is_same<std::tuple<CopyCounting&&>, decltype(result)>::value, "");
277 EXPECT_EQ(&a, &std::get<0>(result));
278 EXPECT_EQ(0, std::get<0>(result).move_count);
279 EXPECT_EQ(0, std::get<0>(result).copy_count);
280 }
281
TEST(MetaprogrammingTest,TupleMap_canBeUsedWithAutoLambdas)282 TEST(MetaprogrammingTest, TupleMap_canBeUsedWithAutoLambdas) {
283 struct A final {
284 int32_t func() {
285 return 5;
286 }
287 };
288 struct B final {
289 std::string func() {
290 return "5";
291 }
292 };
293 auto result =
294 tuple_map(std::make_tuple(A(), B()), [](auto a) { return a.func(); });
295 static_assert(
296 std::is_same<std::tuple<int32_t, std::string>, decltype(result)>::value,
297 "");
298 EXPECT_EQ(5, std::get<0>(result));
299 EXPECT_EQ("5", std::get<1>(result));
300 }
301 } // namespace test_tuple_map
302
303 } // namespace
304 // NOLINTEND(modernize*)
305