compressed_tuple_test.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. // Copyright 2018 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include "absl/container/internal/compressed_tuple.h"
  15. #include <memory>
  16. #include <string>
  17. #include "gmock/gmock.h"
  18. #include "gtest/gtest.h"
  19. #include "absl/container/internal/test_instance_tracker.h"
  20. #include "absl/memory/memory.h"
  21. #include "absl/types/any.h"
  22. #include "absl/types/optional.h"
  23. #include "absl/utility/utility.h"
  24. // These are declared at global scope purely so that error messages
  25. // are smaller and easier to understand.
  26. enum class CallType { kConstRef, kConstMove };
  27. template <int>
  28. struct Empty {
  29. constexpr CallType value() const& { return CallType::kConstRef; }
  30. constexpr CallType value() const&& { return CallType::kConstMove; }
  31. };
  32. template <typename T>
  33. struct NotEmpty {
  34. T value;
  35. };
  36. template <typename T, typename U>
  37. struct TwoValues {
  38. T value1;
  39. U value2;
  40. };
  41. namespace absl {
  42. ABSL_NAMESPACE_BEGIN
  43. namespace container_internal {
  44. namespace {
  45. using absl::test_internal::CopyableMovableInstance;
  46. using absl::test_internal::InstanceTracker;
  47. TEST(CompressedTupleTest, Sizeof) {
  48. EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
  49. EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
  50. EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>, Empty<1>>));
  51. EXPECT_EQ(sizeof(int),
  52. sizeof(CompressedTuple<int, Empty<0>, Empty<1>, Empty<2>>));
  53. EXPECT_EQ(sizeof(TwoValues<int, double>),
  54. sizeof(CompressedTuple<int, NotEmpty<double>>));
  55. EXPECT_EQ(sizeof(TwoValues<int, double>),
  56. sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>>));
  57. EXPECT_EQ(sizeof(TwoValues<int, double>),
  58. sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
  59. }
  60. TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) {
  61. InstanceTracker tracker;
  62. CompressedTuple<CopyableMovableInstance> x1(CopyableMovableInstance(1));
  63. EXPECT_EQ(tracker.instances(), 1);
  64. EXPECT_EQ(tracker.copies(), 0);
  65. EXPECT_LE(tracker.moves(), 1);
  66. EXPECT_EQ(x1.get<0>().value(), 1);
  67. }
  68. TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) {
  69. InstanceTracker tracker;
  70. CopyableMovableInstance i1(1);
  71. CompressedTuple<CopyableMovableInstance> x1(std::move(i1));
  72. EXPECT_EQ(tracker.instances(), 2);
  73. EXPECT_EQ(tracker.copies(), 0);
  74. EXPECT_LE(tracker.moves(), 1);
  75. EXPECT_EQ(x1.get<0>().value(), 1);
  76. }
  77. TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) {
  78. InstanceTracker tracker;
  79. CopyableMovableInstance i1(1);
  80. CopyableMovableInstance i2(2);
  81. Empty<0> empty;
  82. CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
  83. x1(std::move(i1), i2, empty);
  84. EXPECT_EQ(x1.get<0>().value(), 1);
  85. EXPECT_EQ(x1.get<1>().value(), 2);
  86. EXPECT_EQ(tracker.copies(), 0);
  87. EXPECT_EQ(tracker.moves(), 1);
  88. }
  89. struct IncompleteType;
  90. CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>
  91. MakeWithIncomplete(CopyableMovableInstance i1,
  92. IncompleteType& t, // NOLINT
  93. Empty<0> empty) {
  94. return CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>{
  95. std::move(i1), t, empty};
  96. }
  97. struct IncompleteType {};
  98. TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) {
  99. InstanceTracker tracker;
  100. CopyableMovableInstance i1(1);
  101. Empty<0> empty;
  102. struct DerivedType : IncompleteType {int value = 0;};
  103. DerivedType fd;
  104. fd.value = 7;
  105. CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> x1 =
  106. MakeWithIncomplete(std::move(i1), fd, empty);
  107. EXPECT_EQ(x1.get<0>().value(), 1);
  108. EXPECT_EQ(static_cast<DerivedType&>(x1.get<1>()).value, 7);
  109. EXPECT_EQ(tracker.copies(), 0);
  110. EXPECT_EQ(tracker.moves(), 2);
  111. }
  112. TEST(CompressedTupleTest,
  113. OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) {
  114. InstanceTracker tracker;
  115. CopyableMovableInstance i1(1);
  116. CopyableMovableInstance i2(2);
  117. CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
  118. x1(std::move(i1), i2, {}); // NOLINT
  119. EXPECT_EQ(x1.get<0>().value(), 1);
  120. EXPECT_EQ(x1.get<1>().value(), 2);
  121. EXPECT_EQ(tracker.instances(), 3);
  122. // We are forced into the `const Ts&...` constructor (invoking copies)
  123. // because we need it to deduce the type of `{}`.
  124. // std::tuple also has this behavior.
  125. // Note, this test is proof that this is expected behavior, but it is not
  126. // _desired_ behavior.
  127. EXPECT_EQ(tracker.copies(), 1);
  128. EXPECT_EQ(tracker.moves(), 0);
  129. }
  130. TEST(CompressedTupleTest, OneCopyOnLValueConstruction) {
  131. InstanceTracker tracker;
  132. CopyableMovableInstance i1(1);
  133. CompressedTuple<CopyableMovableInstance> x1(i1);
  134. EXPECT_EQ(tracker.copies(), 1);
  135. EXPECT_EQ(tracker.moves(), 0);
  136. tracker.ResetCopiesMovesSwaps();
  137. CopyableMovableInstance i2(2);
  138. const CopyableMovableInstance& i2_ref = i2;
  139. CompressedTuple<CopyableMovableInstance> x2(i2_ref);
  140. EXPECT_EQ(tracker.copies(), 1);
  141. EXPECT_EQ(tracker.moves(), 0);
  142. }
  143. TEST(CompressedTupleTest, OneMoveOnRValueAccess) {
  144. InstanceTracker tracker;
  145. CopyableMovableInstance i1(1);
  146. CompressedTuple<CopyableMovableInstance> x(std::move(i1));
  147. tracker.ResetCopiesMovesSwaps();
  148. CopyableMovableInstance i2 = std::move(x).get<0>();
  149. EXPECT_EQ(tracker.copies(), 0);
  150. EXPECT_EQ(tracker.moves(), 1);
  151. }
  152. TEST(CompressedTupleTest, OneCopyOnLValueAccess) {
  153. InstanceTracker tracker;
  154. CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
  155. EXPECT_EQ(tracker.copies(), 0);
  156. EXPECT_EQ(tracker.moves(), 1);
  157. CopyableMovableInstance t = x.get<0>();
  158. EXPECT_EQ(tracker.copies(), 1);
  159. EXPECT_EQ(tracker.moves(), 1);
  160. }
  161. TEST(CompressedTupleTest, ZeroCopyOnRefAccess) {
  162. InstanceTracker tracker;
  163. CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
  164. EXPECT_EQ(tracker.copies(), 0);
  165. EXPECT_EQ(tracker.moves(), 1);
  166. CopyableMovableInstance& t1 = x.get<0>();
  167. const CopyableMovableInstance& t2 = x.get<0>();
  168. EXPECT_EQ(tracker.copies(), 0);
  169. EXPECT_EQ(tracker.moves(), 1);
  170. EXPECT_EQ(t1.value(), 0);
  171. EXPECT_EQ(t2.value(), 0);
  172. }
  173. TEST(CompressedTupleTest, Access) {
  174. struct S {
  175. std::string x;
  176. };
  177. CompressedTuple<int, Empty<0>, S> x(7, {}, S{"ABC"});
  178. EXPECT_EQ(sizeof(x), sizeof(TwoValues<int, S>));
  179. EXPECT_EQ(7, x.get<0>());
  180. EXPECT_EQ("ABC", x.get<2>().x);
  181. }
  182. TEST(CompressedTupleTest, NonClasses) {
  183. CompressedTuple<int, const char*> x(7, "ABC");
  184. EXPECT_EQ(7, x.get<0>());
  185. EXPECT_STREQ("ABC", x.get<1>());
  186. }
  187. TEST(CompressedTupleTest, MixClassAndNonClass) {
  188. CompressedTuple<int, const char*, Empty<0>, NotEmpty<double>> x(7, "ABC", {},
  189. {1.25});
  190. struct Mock {
  191. int v;
  192. const char* p;
  193. double d;
  194. };
  195. EXPECT_EQ(sizeof(x), sizeof(Mock));
  196. EXPECT_EQ(7, x.get<0>());
  197. EXPECT_STREQ("ABC", x.get<1>());
  198. EXPECT_EQ(1.25, x.get<3>().value);
  199. }
  200. TEST(CompressedTupleTest, Nested) {
  201. CompressedTuple<int, CompressedTuple<int>,
  202. CompressedTuple<int, CompressedTuple<int>>>
  203. x(1, CompressedTuple<int>(2),
  204. CompressedTuple<int, CompressedTuple<int>>(3, CompressedTuple<int>(4)));
  205. EXPECT_EQ(1, x.get<0>());
  206. EXPECT_EQ(2, x.get<1>().get<0>());
  207. EXPECT_EQ(3, x.get<2>().get<0>());
  208. EXPECT_EQ(4, x.get<2>().get<1>().get<0>());
  209. CompressedTuple<Empty<0>, Empty<0>,
  210. CompressedTuple<Empty<0>, CompressedTuple<Empty<0>>>>
  211. y;
  212. std::set<Empty<0>*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(),
  213. &y.get<2>().get<1>().get<0>()};
  214. #ifdef _MSC_VER
  215. // MSVC has a bug where many instances of the same base class are layed out in
  216. // the same address when using __declspec(empty_bases).
  217. // This will be fixed in a future version of MSVC.
  218. int expected = 1;
  219. #else
  220. int expected = 4;
  221. #endif
  222. EXPECT_EQ(expected, sizeof(y));
  223. EXPECT_EQ(expected, empties.size());
  224. EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size());
  225. EXPECT_EQ(4 * sizeof(char),
  226. sizeof(CompressedTuple<CompressedTuple<char, char>,
  227. CompressedTuple<char, char>>));
  228. EXPECT_TRUE((std::is_empty<CompressedTuple<Empty<0>, Empty<1>>>::value));
  229. // Make sure everything still works when things are nested.
  230. struct CT_Empty : CompressedTuple<Empty<0>> {};
  231. CompressedTuple<Empty<0>, CT_Empty> nested_empty;
  232. auto contained = nested_empty.get<0>();
  233. auto nested = nested_empty.get<1>().get<0>();
  234. EXPECT_TRUE((std::is_same<decltype(contained), decltype(nested)>::value));
  235. }
  236. TEST(CompressedTupleTest, Reference) {
  237. int i = 7;
  238. std::string s = "Very long string that goes in the heap";
  239. CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s);
  240. // Sanity check. We should have not moved from `s`
  241. EXPECT_EQ(s, "Very long string that goes in the heap");
  242. EXPECT_EQ(x.get<0>(), x.get<1>());
  243. EXPECT_NE(&x.get<0>(), &x.get<1>());
  244. EXPECT_EQ(&x.get<1>(), &i);
  245. EXPECT_EQ(x.get<2>(), x.get<3>());
  246. EXPECT_NE(&x.get<2>(), &x.get<3>());
  247. EXPECT_EQ(&x.get<3>(), &s);
  248. }
  249. TEST(CompressedTupleTest, NoElements) {
  250. CompressedTuple<> x;
  251. static_cast<void>(x); // Silence -Wunused-variable.
  252. EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value);
  253. }
  254. TEST(CompressedTupleTest, MoveOnlyElements) {
  255. CompressedTuple<std::unique_ptr<std::string>> str_tup(
  256. absl::make_unique<std::string>("str"));
  257. CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>,
  258. std::unique_ptr<int>>
  259. x(std::move(str_tup), absl::make_unique<int>(5));
  260. EXPECT_EQ(*x.get<0>().get<0>(), "str");
  261. EXPECT_EQ(*x.get<1>(), 5);
  262. std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>();
  263. std::unique_ptr<int> x1 = std::move(x).get<1>();
  264. EXPECT_EQ(*x0, "str");
  265. EXPECT_EQ(*x1, 5);
  266. }
  267. TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) {
  268. CompressedTuple<std::unique_ptr<std::string>> base(
  269. absl::make_unique<std::string>("str"));
  270. EXPECT_EQ(*base.get<0>(), "str");
  271. CompressedTuple<std::unique_ptr<std::string>> copy(std::move(base));
  272. EXPECT_EQ(*copy.get<0>(), "str");
  273. }
  274. TEST(CompressedTupleTest, AnyElements) {
  275. any a(std::string("str"));
  276. CompressedTuple<any, any&> x(any(5), a);
  277. EXPECT_EQ(absl::any_cast<int>(x.get<0>()), 5);
  278. EXPECT_EQ(absl::any_cast<std::string>(x.get<1>()), "str");
  279. a = 0.5f;
  280. EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5);
  281. }
  282. TEST(CompressedTupleTest, Constexpr) {
  283. struct NonTrivialStruct {
  284. constexpr NonTrivialStruct() = default;
  285. constexpr int value() const { return v; }
  286. int v = 5;
  287. };
  288. struct TrivialStruct {
  289. TrivialStruct() = default;
  290. constexpr int value() const { return v; }
  291. int v;
  292. };
  293. constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
  294. 7, 1.25, CompressedTuple<int>(5), {});
  295. constexpr int x0 = x.get<0>();
  296. constexpr double x1 = x.get<1>();
  297. constexpr int x2 = x.get<2>().get<0>();
  298. constexpr CallType x3 = x.get<3>().value();
  299. EXPECT_EQ(x0, 7);
  300. EXPECT_EQ(x1, 1.25);
  301. EXPECT_EQ(x2, 5);
  302. EXPECT_EQ(x3, CallType::kConstRef);
  303. #if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
  304. constexpr CompressedTuple<Empty<0>, TrivialStruct, int> trivial = {};
  305. constexpr CallType trivial0 = trivial.get<0>().value();
  306. constexpr int trivial1 = trivial.get<1>().value();
  307. constexpr int trivial2 = trivial.get<2>();
  308. EXPECT_EQ(trivial0, CallType::kConstRef);
  309. EXPECT_EQ(trivial1, 0);
  310. EXPECT_EQ(trivial2, 0);
  311. #endif
  312. constexpr CompressedTuple<Empty<0>, NonTrivialStruct, absl::optional<int>>
  313. non_trivial = {};
  314. constexpr CallType non_trivial0 = non_trivial.get<0>().value();
  315. constexpr int non_trivial1 = non_trivial.get<1>().value();
  316. constexpr absl::optional<int> non_trivial2 = non_trivial.get<2>();
  317. EXPECT_EQ(non_trivial0, CallType::kConstRef);
  318. EXPECT_EQ(non_trivial1, 5);
  319. EXPECT_EQ(non_trivial2, absl::nullopt);
  320. static constexpr char data[] = "DEF";
  321. constexpr CompressedTuple<const char*> z(data);
  322. constexpr const char* z1 = z.get<0>();
  323. EXPECT_EQ(std::string(z1), std::string(data));
  324. #if defined(__clang__)
  325. // An apparent bug in earlier versions of gcc claims these are ambiguous.
  326. constexpr int x2m = absl::move(x.get<2>()).get<0>();
  327. constexpr CallType x3m = absl::move(x).get<3>().value();
  328. EXPECT_EQ(x2m, 5);
  329. EXPECT_EQ(x3m, CallType::kConstMove);
  330. #endif
  331. }
  332. #if defined(__clang__) || defined(__GNUC__)
  333. TEST(CompressedTupleTest, EmptyFinalClass) {
  334. struct S final {
  335. int f() const { return 5; }
  336. };
  337. CompressedTuple<S> x;
  338. EXPECT_EQ(x.get<0>().f(), 5);
  339. }
  340. #endif
  341. } // namespace
  342. } // namespace container_internal
  343. ABSL_NAMESPACE_END
  344. } // namespace absl