1 /*
2 * Copyright 2019 Google LLC
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 "fcp/base/meta.h"
18
19 #include <functional>
20 #include <memory>
21 #include <optional>
22 #include <type_traits>
23 #include <utility>
24 #include <vector>
25
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
28
29 namespace fcp {
30
31 using ::testing::Eq;
32 using ::testing::Not;
33
34 struct R {
35 virtual ~R() = default;
36 virtual bool Virt1(int i) = 0;
37 virtual bool Virt2(int i, int j) = 0;
38 virtual void Virt3() = 0;
NonVirt1fcp::R39 int NonVirt1() { return 1; }
NonVirt2fcp::R40 int NonVirt2() { return 2; }
41 char field;
42 };
43
44 struct S {
45 virtual ~S() = default;
46 virtual bool Virt1(int i) = 0;
47 virtual bool Virt2(int i, int j) = 0;
48 virtual void Virt3() = 0;
NonVirt1fcp::S49 int NonVirt1() { return 1; }
NonVirt2fcp::S50 int NonVirt2() { return 2; }
51 char field;
52 };
53
54 //
55 // Compile-time tests for MemberPointerTraits
56 //
57 #define STATIC_ASSERT_TARGET_TYPE(member, type) \
58 static_assert( \
59 std::is_same<MemberPointerTraits<decltype(&S::member)>::TargetType, \
60 type>::value, \
61 "Incorrect target type from MemberPointerTraits");
62
63 // For some reason the linter otherwise thinks e.g. 'bool(int)' is an old-style
64 // cast.
65 template<typename T>
66 struct Id { using Type = T; };
67
68 STATIC_ASSERT_TARGET_TYPE(Virt1, Id<bool(int)>::Type);
69 STATIC_ASSERT_TARGET_TYPE(Virt2, Id<bool(int, int)>::Type);
70 STATIC_ASSERT_TARGET_TYPE(Virt3, Id<void()>::Type);
71 STATIC_ASSERT_TARGET_TYPE(NonVirt1, Id<int()>::Type);
72 STATIC_ASSERT_TARGET_TYPE(NonVirt2, Id<int()>::Type);
73 STATIC_ASSERT_TARGET_TYPE(field, Id<char>::Type);
74 //
75 // End compile-time tests for MemberPointerTraits
76 //
77
78 template<typename T>
79 struct TypeIdHolder {
80 static constexpr char kTarget = '\0';
81 };
82 template<typename T>
83 constexpr char TypeIdHolder<T>::kTarget;
84
85 // This gives us a unique runtime value per unique type - with which its much
86 // easier to verify uniqueness (below).
87 template<typename T>
TypeId()88 static constexpr char const* TypeId() {
89 return &TypeIdHolder<T>::kTarget;
90 }
91
92 //
93 // LIFT_MEMBER_TO_TYPE
94 //
95
TEST(MetaTest,MemberTagUniqueness)96 TEST(MetaTest, MemberTagUniqueness) {
97 std::vector<char const*> type_ids = {
98 TypeId<LIFT_MEMBER_TO_TYPE(S, Virt1)>(),
99 TypeId<LIFT_MEMBER_TO_TYPE(S, Virt2)>(),
100 TypeId<LIFT_MEMBER_TO_TYPE(S, Virt3)>(),
101 TypeId<LIFT_MEMBER_TO_TYPE(S, NonVirt1)>(),
102 TypeId<LIFT_MEMBER_TO_TYPE(S, NonVirt2)>(),
103 TypeId<LIFT_MEMBER_TO_TYPE(S, field)>(),
104 TypeId<LIFT_MEMBER_TO_TYPE(R, Virt1)>(),
105 TypeId<LIFT_MEMBER_TO_TYPE(R, Virt2)>(),
106 TypeId<LIFT_MEMBER_TO_TYPE(R, Virt3)>(),
107 TypeId<LIFT_MEMBER_TO_TYPE(R, NonVirt1)>(),
108 TypeId<LIFT_MEMBER_TO_TYPE(R, NonVirt2)>(),
109 TypeId<LIFT_MEMBER_TO_TYPE(R, field)>(),
110 };
111
112 for (int i = 0; i < type_ids.size(); i++) {
113 for (int j = 0; j < type_ids.size(); j++) {
114 if (i == j) {
115 continue;
116 }
117 EXPECT_THAT(type_ids[i], Not(Eq(type_ids[j])))
118 << "Member tags must be unique";
119 }
120 }
121 }
122
PokeMemberCase(LIFT_MEMBER_TO_TYPE (S,Virt1))123 int PokeMemberCase(LIFT_MEMBER_TO_TYPE(S, Virt1)) {
124 return 1;
125 }
126
PokeMemberCase(LIFT_MEMBER_TO_TYPE (S,Virt2))127 int PokeMemberCase(LIFT_MEMBER_TO_TYPE(S, Virt2)) {
128 return 2;
129 }
130
131 template<typename R, R S::* M>
PokeMember()132 int PokeMember() {
133 return PokeMemberCase(LIFT_MEMBER_POINTER_TO_TYPE(M){});
134 }
135
TEST(MetaTest,MemberTagDispatch)136 TEST(MetaTest, MemberTagDispatch) {
137 EXPECT_THAT((PokeMember<bool(int), &S::Virt1>()), Eq(1));
138 EXPECT_THAT((PokeMember<bool(int, int), &S::Virt2>()), Eq(2));
139 }
140
141 //
142 // CastContainerElements
143 //
144
145 struct X {
tagfcp::X146 static constexpr int tag() { return 1; }
147 };
148
149 struct Y {
tagfcp::Y150 static constexpr int tag() { return 2; }
151 };
152
153 template<typename T>
154 struct TypedVal {
155 int value;
156
operator ==fcp::TypedVal157 bool operator==(TypedVal<T> const& other) const {
158 return other.value == value;
159 }
operator !=fcp::TypedVal160 bool operator!=(TypedVal<T> const& other) const { return !(*this == other); }
161 };
162
163 struct UntypedVal {
164 int tag;
165 int value;
166 };
167
168 struct CastValByTag {
169 template <typename T>
170 using TargetType = std::optional<TypedVal<T>>;
171
172 template <typename T>
Castfcp::CastValByTag173 TargetType<T> Cast(UntypedVal const& val) const {
174 if (val.tag == T::tag()) {
175 return TypedVal<T>{val.value};
176 } else {
177 return std::nullopt;
178 }
179 }
180 };
181
TEST(MetaTest,CastContainerElements_AllSuccess)182 TEST(MetaTest, CastContainerElements_AllSuccess) {
183 std::vector<UntypedVal> v{
184 {X::tag(), 123},
185 {Y::tag(), 456},
186 {X::tag(), 789}
187 };
188
189 auto actual = CastContainerElements<X, Y, X>(v, CastValByTag{});
190 auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
191 absl::make_optional(TypedVal<Y>{456}),
192 absl::make_optional(TypedVal<X>{789}));
193
194 EXPECT_THAT(actual, Eq(expected));
195 }
196
TEST(MetaTest,CastContainerElements_AllSuccess_Pack)197 TEST(MetaTest, CastContainerElements_AllSuccess_Pack) {
198 std::vector<UntypedVal> v{
199 {X::tag(), 123},
200 {Y::tag(), 456},
201 {X::tag(), 789}
202 };
203
204 // This uses the Pack<> overload instead.
205 auto actual = CastContainerElements(Pack<X, Y, X>{}, v, CastValByTag{});
206 auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
207 absl::make_optional(TypedVal<Y>{456}),
208 absl::make_optional(TypedVal<X>{789}));
209
210 EXPECT_THAT(actual, Eq(expected));
211 }
212
TEST(MetaTest,CastContainerElements_OneFails)213 TEST(MetaTest, CastContainerElements_OneFails) {
214 std::vector<UntypedVal> v{
215 {X::tag(), 123},
216 {X::tag(), 456},
217 {X::tag(), 789}
218 };
219
220 // Second element does not have the tag for Y.
221 auto actual = CastContainerElements<X, Y, X>(v, CastValByTag{});
222 auto expected = std::make_tuple(absl::make_optional(TypedVal<X>{123}),
223 std::optional<TypedVal<Y>>(std::nullopt),
224 absl::make_optional(TypedVal<X>{789}));
225
226 EXPECT_THAT(actual, Eq(expected));
227 }
228
229 //
230 // MAKE_LINK and LinkedType<>
231 //
232
233 namespace links {
234
235 namespace a {
236
237 struct A1 {};
238 struct A2 {};
239 struct A3 {};
240
241 MAKE_LINK(A1, A2);
242
243 } // namespace a
244
245 namespace b {
246
247 struct B1 {};
248
249 MAKE_LINK(B1, a::A3);
250
251 } // namespace b
252
253 } // namespace links
254
255 static_assert(std::is_same<LinkedType<links::a::A1>, links::a::A2>::value,
256 "A1 -> A2");
257 static_assert(HasLinkedType<links::a::A1>(), "A1 -> A2");
258 static_assert(std::is_same<LinkedTypeOrVoid<links::a::A2>, void>::value,
259 "A2 -/>");
260 static_assert(!HasLinkedType<links::a::A2>(), "A2 -/>");
261 static_assert(std::is_same<LinkedTypeOrVoid<links::a::A3>, void>::value,
262 "A3 -/>");
263 static_assert(!HasLinkedType<links::a::A3>(), "A3 -/>");
264 static_assert(std::is_same<LinkedType<links::b::B1>, links::a::A3>::value,
265 "b::B1 -> a::A3");
266 static_assert(HasLinkedType<links::b::B1>(), "b::B1 -> a::A3");
267
268 //
269 // Pack<>
270 //
271
272 template<typename A1, typename A2, size_t I1, size_t I2>
CheckUnpack()273 constexpr Unit CheckUnpack() {
274 static_assert(std::is_same<A1, X>::value, "A1 == X");
275 static_assert(std::is_same<A2, Y>::value, "A2 == Y");
276 static_assert(I1 == 0, "I1 == 0");
277 static_assert(I2 == 1, "I2 == 0");
278 return {};
279 }
280
281 template<typename... A, size_t... I>
UsePack(Pack<A...>,absl::index_sequence<I...>)282 constexpr Unit UsePack(Pack<A...>, absl::index_sequence<I...>) {
283 return CheckUnpack<A..., I...>();
284 }
285
286 template<typename... A>
MakeAndUsePack()287 constexpr Unit MakeAndUsePack() {
288 return UsePack(Pack<A...>{}, Pack<A...>::MakeIndexSequence());
289 }
290
291 static_assert(MakeAndUsePack<X, Y>().True(), "Pack<>");
292
293 //
294 // LiftVoidReturn
295 //
296
TEST(MetaTest,LiftVoidReturn_Void)297 TEST(MetaTest, LiftVoidReturn_Void) {
298 int counter = 0;
299 std::function<void()> f = [&counter]() { counter++; };
300
301 f();
302 EXPECT_THAT(counter, Eq(1));
303 auto f_wrapped = LiftVoidReturn(f);
304 EXPECT_THAT(f_wrapped(), Eq(Unit{}));
305 EXPECT_THAT(counter, Eq(2));
306 }
307
TEST(MetaTest,LiftVoidReturn_Void_Args)308 TEST(MetaTest, LiftVoidReturn_Void_Args) {
309 int counter = 0;
310 std::function<void(int)> f = [&counter](int i) { counter += i; };
311
312 f(10);
313 EXPECT_THAT(counter, Eq(10));
314 auto f_wrapped = LiftVoidReturn(f);
315 EXPECT_THAT(f_wrapped(32), Eq(Unit{}));
316 EXPECT_THAT(counter, Eq(42));
317 }
318
TEST(MetaTest,LiftVoidReturn_NonVoid)319 TEST(MetaTest, LiftVoidReturn_NonVoid) {
320 int counter = 0;
321 std::function<int(int)> f = [&counter](int i) {
322 counter += i;
323 return counter;
324 };
325
326 EXPECT_THAT(f(10), Eq(10));
327 EXPECT_THAT(counter, Eq(10));
328 auto f_wrapped = LiftVoidReturn(f);
329 EXPECT_THAT(f_wrapped(32), Eq(42));
330 EXPECT_THAT(counter, Eq(42));
331 }
332
TEST(MetaTest,LiftVoidReturn_Mutable)333 TEST(MetaTest, LiftVoidReturn_Mutable) {
334 int r = -1;
335 auto f = [&r, counter = 0]() mutable {
336 counter++;
337 r = counter;
338 };
339
340 f();
341 EXPECT_THAT(r, Eq(1));
342 auto f_wrapped = LiftVoidReturn(f);
343 EXPECT_THAT(f_wrapped(), Eq(Unit{}));
344 EXPECT_THAT(r, Eq(2));
345 }
346
TEST(MetaTest,LiftVoidReturn_MutableAndMoveOnly)347 TEST(MetaTest, LiftVoidReturn_MutableAndMoveOnly) {
348 int r = -1;
349 auto f = [&r, counter = std::make_unique<int>(0)]() mutable {
350 (*counter)++;
351 r = *counter;
352 };
353
354 f();
355 EXPECT_THAT(r, Eq(1));
356 auto f_wrapped = LiftVoidReturn(std::move(f));
357 EXPECT_THAT(f_wrapped(), Eq(Unit{}));
358 EXPECT_THAT(r, Eq(2));
359 }
360
361 //
362 // FunctionTraits
363 //
364
365 #define STATIC_ASSERT_FUNCTION_TRAITS(fn, r, ...) \
366 static_assert(std::is_same<FunctionTraits<fn>::ResultType, r>::value, \
367 "Incorrect result type from FunctionTraits"); \
368 static_assert( \
369 std::is_same<FunctionTraits<fn>::ArgPackType, Pack<__VA_ARGS__>>::value, \
370 "Incorrect arg pack from FunctionTraits")
371
372 STATIC_ASSERT_FUNCTION_TRAITS(void(), void);
373 STATIC_ASSERT_FUNCTION_TRAITS(void(int, char), void, int, char);
374 STATIC_ASSERT_FUNCTION_TRAITS(Identity<bool(char const*, int)>, bool,
375 char const*, int);
376
TEST(MetaTest,IsTypeOneOf)377 TEST(MetaTest, IsTypeOneOf) {
378 static_assert(IsTypeOneOf<int, int>());
379 static_assert(IsTypeOneOf<int, int, double>());
380 static_assert(IsTypeOneOf<int, double, int>());
381 static_assert(!IsTypeOneOf<int, bool>());
382 static_assert(!IsTypeOneOf<int, double, char>());
383 }
384
TEST(MetaTest,IsSubsetOf)385 TEST(MetaTest, IsSubsetOf) {
386 using T1 = Pack<int, double>;
387 using T2 = Pack<double, int>;
388 using T3 = Pack<int, double, char>;
389
390 static_assert(IsSubsetOf<T1, T1>::value);
391 static_assert(IsSubsetOf<T1, T2>::value);
392 static_assert(IsSubsetOf<T2, T1>::value);
393 static_assert(IsSubsetOf<T2, T3>::value);
394 static_assert(!IsSubsetOf<T3, T2>::value);
395 }
396
397 } // namespace fcp
398