type_t, a generalization of void_t
[folly.git] / folly / test / TraitsTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
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 <folly/Traits.h>
18
19 #include <cstring>
20 #include <string>
21 #include <type_traits>
22 #include <utility>
23
24 #include <folly/ScopeGuard.h>
25 #include <folly/portability/GTest.h>
26
27 using namespace folly;
28 using namespace std;
29
30 namespace {
31
32 FOLLY_CREATE_HAS_MEMBER_TYPE_TRAITS(has_member_type_x, x);
33 } // namespace
34
35 TEST(Traits, has_member_type) {
36   struct membership_no {};
37   struct membership_yes {
38     using x = void;
39   };
40
41   EXPECT_TRUE((is_same<false_type, has_member_type_x<membership_no>>::value));
42   EXPECT_TRUE((is_same<true_type, has_member_type_x<membership_yes>>::value));
43 }
44
45 //  Note: FOLLY_CREATE_HAS_MEMBER_FN_TRAITS tests are in
46 //  folly/test/HasMemberFnTraitsTest.cpp.
47
48 struct T1 {}; // old-style IsRelocatable, below
49 struct T2 {}; // old-style IsRelocatable, below
50 struct T3 { typedef std::true_type IsRelocatable; };
51 struct T4 { typedef std::true_type IsTriviallyCopyable; };
52 struct T5 : T3 {};
53
54 struct F1 {};
55 struct F2 { typedef int IsRelocatable; };
56 struct F3 : T3 { typedef std::false_type IsRelocatable; };
57 struct F4 : T1 {};
58
59 namespace folly {
60 template <>
61 struct IsRelocatable<T1> : std::true_type {};
62 template <>
63 FOLLY_ASSUME_RELOCATABLE(T2);
64 } // namespace folly
65
66 TEST(Traits, scalars) {
67   EXPECT_TRUE(IsRelocatable<int>::value);
68   EXPECT_TRUE(IsRelocatable<bool>::value);
69   EXPECT_TRUE(IsRelocatable<double>::value);
70   EXPECT_TRUE(IsRelocatable<void*>::value);
71 }
72
73 TEST(Traits, containers) {
74   EXPECT_TRUE  (IsRelocatable<vector<F1>>::value);
75   EXPECT_TRUE((IsRelocatable<pair<F1, F1>>::value));
76   EXPECT_TRUE ((IsRelocatable<pair<T1, T2>>::value));
77 }
78
79 TEST(Traits, original) {
80   EXPECT_TRUE(IsRelocatable<T1>::value);
81   EXPECT_TRUE(IsRelocatable<T2>::value);
82 }
83
84 TEST(Traits, typedefd) {
85   EXPECT_TRUE (IsRelocatable<T3>::value);
86   EXPECT_TRUE (IsRelocatable<T5>::value);
87   EXPECT_FALSE(IsRelocatable<F2>::value);
88   EXPECT_FALSE(IsRelocatable<F3>::value);
89 }
90
91 TEST(Traits, unset) {
92   EXPECT_TRUE(IsRelocatable<F1>::value);
93   EXPECT_TRUE(IsRelocatable<F4>::value);
94 }
95
96 TEST(Traits, bitprop) {
97   EXPECT_TRUE(IsTriviallyCopyable<T4>::value);
98   EXPECT_TRUE(IsRelocatable<T4>::value);
99 }
100
101 TEST(Traits, bitAndInit) {
102   EXPECT_TRUE (IsTriviallyCopyable<int>::value);
103   EXPECT_FALSE(IsTriviallyCopyable<vector<int>>::value);
104   EXPECT_TRUE (IsZeroInitializable<int>::value);
105   EXPECT_FALSE(IsZeroInitializable<vector<int>>::value);
106 }
107
108 TEST(Trait, logicOperators) {
109   static_assert(Conjunction<true_type>::value, "");
110   static_assert(!Conjunction<false_type>::value, "");
111   static_assert(is_same<Conjunction<true_type>::type, true_type>::value, "");
112   static_assert(is_same<Conjunction<false_type>::type, false_type>::value, "");
113   static_assert(Conjunction<true_type, true_type>::value, "");
114   static_assert(!Conjunction<true_type, false_type>::value, "");
115
116   static_assert(Disjunction<true_type>::value, "");
117   static_assert(!Disjunction<false_type>::value, "");
118   static_assert(is_same<Disjunction<true_type>::type, true_type>::value, "");
119   static_assert(is_same<Disjunction<false_type>::type, false_type>::value, "");
120   static_assert(Disjunction<true_type, true_type>::value, "");
121   static_assert(Disjunction<true_type, false_type>::value, "");
122
123   static_assert(!Negation<true_type>::value, "");
124   static_assert(Negation<false_type>::value, "");
125 }
126
127 TEST(Traits, is_negative) {
128   EXPECT_TRUE(folly::is_negative(-1));
129   EXPECT_FALSE(folly::is_negative(0));
130   EXPECT_FALSE(folly::is_negative(1));
131   EXPECT_FALSE(folly::is_negative(0u));
132   EXPECT_FALSE(folly::is_negative(1u));
133
134   EXPECT_TRUE(folly::is_non_positive(-1));
135   EXPECT_TRUE(folly::is_non_positive(0));
136   EXPECT_FALSE(folly::is_non_positive(1));
137   EXPECT_TRUE(folly::is_non_positive(0u));
138   EXPECT_FALSE(folly::is_non_positive(1u));
139 }
140
141 TEST(Traits, relational) {
142   // We test, especially, the edge cases to make sure we don't
143   // trip -Wtautological-comparisons
144
145   EXPECT_FALSE((folly::less_than<uint8_t, 0u,   uint8_t>(0u)));
146   EXPECT_FALSE((folly::less_than<uint8_t, 0u,   uint8_t>(254u)));
147   EXPECT_FALSE((folly::less_than<uint8_t, 255u, uint8_t>(255u)));
148   EXPECT_TRUE( (folly::less_than<uint8_t, 255u, uint8_t>(254u)));
149
150   EXPECT_FALSE((folly::greater_than<uint8_t, 0u,   uint8_t>(0u)));
151   EXPECT_TRUE( (folly::greater_than<uint8_t, 0u,   uint8_t>(254u)));
152   EXPECT_FALSE((folly::greater_than<uint8_t, 255u, uint8_t>(255u)));
153   EXPECT_FALSE((folly::greater_than<uint8_t, 255u, uint8_t>(254u)));
154 }
155
156 #if FOLLY_HAVE_INT128_T
157
158 TEST(Traits, int128) {
159   EXPECT_TRUE(
160       (::std::is_same<::std::make_unsigned<__int128_t>::type, __uint128_t>::
161            value));
162   EXPECT_TRUE((
163       ::std::is_same<::std::make_signed<__int128_t>::type, __int128_t>::value));
164   EXPECT_TRUE(
165       (::std::is_same<::std::make_unsigned<__uint128_t>::type, __uint128_t>::
166            value));
167   EXPECT_TRUE(
168       (::std::is_same<::std::make_signed<__uint128_t>::type, __int128_t>::
169            value));
170   EXPECT_TRUE((::std::is_arithmetic<__int128_t>::value));
171   EXPECT_TRUE((::std::is_arithmetic<__uint128_t>::value));
172   EXPECT_TRUE((::std::is_integral<__int128_t>::value));
173   EXPECT_TRUE((::std::is_integral<__uint128_t>::value));
174   EXPECT_FALSE((::std::is_unsigned<__int128_t>::value));
175   EXPECT_TRUE((::std::is_signed<__int128_t>::value));
176   EXPECT_TRUE((::std::is_unsigned<__uint128_t>::value));
177   EXPECT_FALSE((::std::is_signed<__uint128_t>::value));
178   EXPECT_TRUE((::std::is_fundamental<__int128_t>::value));
179   EXPECT_TRUE((::std::is_fundamental<__uint128_t>::value));
180   EXPECT_TRUE((::std::is_scalar<__int128_t>::value));
181   EXPECT_TRUE((::std::is_scalar<__uint128_t>::value));
182 }
183
184 #endif // FOLLY_HAVE_INT128_T
185
186 template <typename T, typename... Args>
187 void testIsRelocatable(Args&&... args) {
188   if (!IsRelocatable<T>::value) {
189     return;
190   }
191
192   // We use placement new on zeroed memory to avoid garbage subsections
193   char vsrc[sizeof(T)] = { 0 };
194   char vdst[sizeof(T)] = { 0 };
195   char vcpy[sizeof(T)];
196
197   T* src = new (vsrc) T(std::forward<Args>(args)...);
198   SCOPE_EXIT { src->~T(); };
199   std::memcpy(vcpy, vsrc, sizeof(T));
200   T deep(*src);
201   T* dst = new (vdst) T(std::move(*src));
202   SCOPE_EXIT { dst->~T(); };
203
204   EXPECT_EQ(deep, *dst);
205 #pragma GCC diagnostic push
206 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
207   EXPECT_EQ(deep, *reinterpret_cast<T*>(vcpy));
208 #pragma GCC diagnostic pop
209
210   // This test could technically fail; however, this is what relocation
211   // almost always means, so it's a good test to have
212   EXPECT_EQ(std::memcmp(vcpy, vdst, sizeof(T)), 0);
213 }
214
215 TEST(Traits, actuallyRelocatable) {
216   // Ensure that we test stack and heap allocation for strings with in-situ
217   // capacity
218   testIsRelocatable<std::string>("1");
219   testIsRelocatable<std::string>(sizeof(std::string) + 1, 'x');
220
221   testIsRelocatable<std::vector<char>>(5, 'g');
222 }
223
224 namespace {
225 // has_value_type<T>::value is true if T has a nested type `value_type`
226 template <class T, class = void>
227 struct has_value_type : std::false_type {};
228
229 template <class T>
230 struct has_value_type<T, folly::void_t<typename T::value_type>>
231     : std::true_type {};
232
233 struct some_tag {};
234
235 template <typename T>
236 struct container {
237   template <class... Args>
238   container(
239       folly::type_t<some_tag, decltype(T(std::declval<Args>()...))>,
240       Args&&...) {}
241 };
242 } // namespace
243
244 TEST(Traits, void_t) {
245   EXPECT_TRUE((::std::is_same<folly::void_t<>, void>::value));
246   EXPECT_TRUE((::std::is_same<folly::void_t<int>, void>::value));
247   EXPECT_TRUE((::std::is_same<folly::void_t<int, short>, void>::value));
248   EXPECT_TRUE(
249       (::std::is_same<folly::void_t<int, short, std::string>, void>::value));
250   EXPECT_TRUE((::has_value_type<std::string>::value));
251   EXPECT_FALSE((::has_value_type<int>::value));
252 }
253
254 TEST(Traits, type_t) {
255   EXPECT_TRUE((::std::is_same<folly::type_t<float>, float>::value));
256   EXPECT_TRUE((::std::is_same<folly::type_t<float, int>, float>::value));
257   EXPECT_TRUE((::std::is_same<folly::type_t<float, int, short>, float>::value));
258   EXPECT_TRUE(
259       (::std::is_same<folly::type_t<float, int, short, std::string>, float>::
260            value));
261   EXPECT_TRUE((
262       ::std::is_constructible<::container<std::string>, some_tag, std::string>::
263           value));
264   EXPECT_FALSE(
265       (::std::is_constructible<::container<std::string>, some_tag, float>::
266            value));
267 }