abseil-cpp/absl/container/internal/compressed_tuple_test.cc
Go to the documentation of this file.
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 
15 #include "absl/container/internal/compressed_tuple.h"
16 
17 #include <memory>
18 #include <string>
19 
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 #include "absl/container/internal/test_instance_tracker.h"
23 #include "absl/memory/memory.h"
24 #include "absl/types/any.h"
25 #include "absl/types/optional.h"
26 #include "absl/utility/utility.h"
27 
28 // These are declared at global scope purely so that error messages
29 // are smaller and easier to understand.
30 enum class CallType { kConstRef, kConstMove };
31 
32 template <int>
33 struct Empty {
34  constexpr CallType value() const& { return CallType::kConstRef; }
35  constexpr CallType value() const&& { return CallType::kConstMove; }
36 };
37 
38 template <typename T>
39 struct NotEmpty {
41 };
42 
43 template <typename T, typename U>
44 struct TwoValues {
46  U value2;
47 };
48 
49 
50 namespace absl {
52 namespace container_internal {
53 namespace {
54 
57 
58 TEST(CompressedTupleTest, Sizeof) {
59  EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int>));
60  EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>>));
61  EXPECT_EQ(sizeof(int), sizeof(CompressedTuple<int, Empty<0>, Empty<1>>));
62  EXPECT_EQ(sizeof(int),
63  sizeof(CompressedTuple<int, Empty<0>, Empty<1>, Empty<2>>));
64 
66  sizeof(CompressedTuple<int, NotEmpty<double>>));
68  sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>>));
70  sizeof(CompressedTuple<int, Empty<0>, NotEmpty<double>, Empty<1>>));
71 }
72 
73 TEST(CompressedTupleTest, OneMoveOnRValueConstructionTemp) {
74  InstanceTracker tracker;
75  CompressedTuple<CopyableMovableInstance> x1(CopyableMovableInstance(1));
76  EXPECT_EQ(tracker.instances(), 1);
77  EXPECT_EQ(tracker.copies(), 0);
78  EXPECT_LE(tracker.moves(), 1);
79  EXPECT_EQ(x1.get<0>().value(), 1);
80 }
81 
82 TEST(CompressedTupleTest, OneMoveOnRValueConstructionMove) {
83  InstanceTracker tracker;
84 
85  CopyableMovableInstance i1(1);
86  CompressedTuple<CopyableMovableInstance> x1(std::move(i1));
87  EXPECT_EQ(tracker.instances(), 2);
88  EXPECT_EQ(tracker.copies(), 0);
89  EXPECT_LE(tracker.moves(), 1);
90  EXPECT_EQ(x1.get<0>().value(), 1);
91 }
92 
93 TEST(CompressedTupleTest, OneMoveOnRValueConstructionMixedTypes) {
94  InstanceTracker tracker;
95  CopyableMovableInstance i1(1);
96  CopyableMovableInstance i2(2);
98  CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
99  x1(std::move(i1), i2, empty);
100  EXPECT_EQ(x1.get<0>().value(), 1);
101  EXPECT_EQ(x1.get<1>().value(), 2);
102  EXPECT_EQ(tracker.copies(), 0);
103  EXPECT_EQ(tracker.moves(), 1);
104 }
105 
106 struct IncompleteType;
107 CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>
108 MakeWithIncomplete(CopyableMovableInstance i1,
109  IncompleteType& t, // NOLINT
110  Empty<0> empty) {
111  return CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>>{
112  std::move(i1), t, empty};
113 }
114 
115 struct IncompleteType {};
116 TEST(CompressedTupleTest, OneMoveOnRValueConstructionWithIncompleteType) {
117  InstanceTracker tracker;
118  CopyableMovableInstance i1(1);
119  Empty<0> empty;
120  struct DerivedType : IncompleteType {int value = 0;};
121  DerivedType fd;
122  fd.value = 7;
123 
124  CompressedTuple<CopyableMovableInstance, IncompleteType&, Empty<0>> x1 =
125  MakeWithIncomplete(std::move(i1), fd, empty);
126 
127  EXPECT_EQ(x1.get<0>().value(), 1);
128  EXPECT_EQ(static_cast<DerivedType&>(x1.get<1>()).value, 7);
129 
130  EXPECT_EQ(tracker.copies(), 0);
131  EXPECT_EQ(tracker.moves(), 2);
132 }
133 
134 TEST(CompressedTupleTest,
135  OneMoveOnRValueConstructionMixedTypes_BraceInitPoisonPillExpected) {
136  InstanceTracker tracker;
137  CopyableMovableInstance i1(1);
138  CopyableMovableInstance i2(2);
139  CompressedTuple<CopyableMovableInstance, CopyableMovableInstance&, Empty<0>>
140  x1(std::move(i1), i2, {}); // NOLINT
141  EXPECT_EQ(x1.get<0>().value(), 1);
142  EXPECT_EQ(x1.get<1>().value(), 2);
143  EXPECT_EQ(tracker.instances(), 3);
144  // We are forced into the `const Ts&...` constructor (invoking copies)
145  // because we need it to deduce the type of `{}`.
146  // std::tuple also has this behavior.
147  // Note, this test is proof that this is expected behavior, but it is not
148  // _desired_ behavior.
149  EXPECT_EQ(tracker.copies(), 1);
150  EXPECT_EQ(tracker.moves(), 0);
151 }
152 
153 TEST(CompressedTupleTest, OneCopyOnLValueConstruction) {
154  InstanceTracker tracker;
155  CopyableMovableInstance i1(1);
156 
157  CompressedTuple<CopyableMovableInstance> x1(i1);
158  EXPECT_EQ(tracker.copies(), 1);
159  EXPECT_EQ(tracker.moves(), 0);
160 
161  tracker.ResetCopiesMovesSwaps();
162 
163  CopyableMovableInstance i2(2);
164  const CopyableMovableInstance& i2_ref = i2;
165  CompressedTuple<CopyableMovableInstance> x2(i2_ref);
166  EXPECT_EQ(tracker.copies(), 1);
167  EXPECT_EQ(tracker.moves(), 0);
168 }
169 
170 TEST(CompressedTupleTest, OneMoveOnRValueAccess) {
171  InstanceTracker tracker;
172  CopyableMovableInstance i1(1);
173  CompressedTuple<CopyableMovableInstance> x(std::move(i1));
174  tracker.ResetCopiesMovesSwaps();
175 
176  CopyableMovableInstance i2 = std::move(x).get<0>();
177  EXPECT_EQ(tracker.copies(), 0);
178  EXPECT_EQ(tracker.moves(), 1);
179 }
180 
181 TEST(CompressedTupleTest, OneCopyOnLValueAccess) {
182  InstanceTracker tracker;
183 
184  CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
185  EXPECT_EQ(tracker.copies(), 0);
186  EXPECT_EQ(tracker.moves(), 1);
187 
188  CopyableMovableInstance t = x.get<0>();
189  EXPECT_EQ(tracker.copies(), 1);
190  EXPECT_EQ(tracker.moves(), 1);
191 }
192 
193 TEST(CompressedTupleTest, ZeroCopyOnRefAccess) {
194  InstanceTracker tracker;
195 
196  CompressedTuple<CopyableMovableInstance> x(CopyableMovableInstance(0));
197  EXPECT_EQ(tracker.copies(), 0);
198  EXPECT_EQ(tracker.moves(), 1);
199 
200  CopyableMovableInstance& t1 = x.get<0>();
201  const CopyableMovableInstance& t2 = x.get<0>();
202  EXPECT_EQ(tracker.copies(), 0);
203  EXPECT_EQ(tracker.moves(), 1);
204  EXPECT_EQ(t1.value(), 0);
205  EXPECT_EQ(t2.value(), 0);
206 }
207 
208 TEST(CompressedTupleTest, Access) {
209  struct S {
210  std::string x;
211  };
212  CompressedTuple<int, Empty<0>, S> x(7, {}, S{"ABC"});
213  EXPECT_EQ(sizeof(x), sizeof(TwoValues<int, S>));
214  EXPECT_EQ(7, x.get<0>());
215  EXPECT_EQ("ABC", x.get<2>().x);
216 }
217 
218 TEST(CompressedTupleTest, NonClasses) {
219  CompressedTuple<int, const char*> x(7, "ABC");
220  EXPECT_EQ(7, x.get<0>());
221  EXPECT_STREQ("ABC", x.get<1>());
222 }
223 
224 TEST(CompressedTupleTest, MixClassAndNonClass) {
225  CompressedTuple<int, const char*, Empty<0>, NotEmpty<double>> x(7, "ABC", {},
226  {1.25});
227  struct Mock {
228  int v;
229  const char* p;
230  double d;
231  };
232  EXPECT_EQ(sizeof(x), sizeof(Mock));
233  EXPECT_EQ(7, x.get<0>());
234  EXPECT_STREQ("ABC", x.get<1>());
235  EXPECT_EQ(1.25, x.get<3>().value);
236 }
237 
238 TEST(CompressedTupleTest, Nested) {
239  CompressedTuple<int, CompressedTuple<int>,
240  CompressedTuple<int, CompressedTuple<int>>>
241  x(1, CompressedTuple<int>(2),
242  CompressedTuple<int, CompressedTuple<int>>(3, CompressedTuple<int>(4)));
243  EXPECT_EQ(1, x.get<0>());
244  EXPECT_EQ(2, x.get<1>().get<0>());
245  EXPECT_EQ(3, x.get<2>().get<0>());
246  EXPECT_EQ(4, x.get<2>().get<1>().get<0>());
247 
248  CompressedTuple<Empty<0>, Empty<0>,
249  CompressedTuple<Empty<0>, CompressedTuple<Empty<0>>>>
250  y;
251  std::set<Empty<0>*> empties{&y.get<0>(), &y.get<1>(), &y.get<2>().get<0>(),
252  &y.get<2>().get<1>().get<0>()};
253 #ifdef _MSC_VER
254  // MSVC has a bug where many instances of the same base class are layed out in
255  // the same address when using __declspec(empty_bases).
256  // This will be fixed in a future version of MSVC.
257  int expected = 1;
258 #else
259  int expected = 4;
260 #endif
261  EXPECT_EQ(expected, sizeof(y));
262  EXPECT_EQ(expected, empties.size());
263  EXPECT_EQ(sizeof(y), sizeof(Empty<0>) * empties.size());
264 
265  EXPECT_EQ(4 * sizeof(char),
266  sizeof(CompressedTuple<CompressedTuple<char, char>,
267  CompressedTuple<char, char>>));
268  EXPECT_TRUE((std::is_empty<CompressedTuple<Empty<0>, Empty<1>>>::value));
269 
270  // Make sure everything still works when things are nested.
271  struct CT_Empty : CompressedTuple<Empty<0>> {};
272  CompressedTuple<Empty<0>, CT_Empty> nested_empty;
273  auto contained = nested_empty.get<0>();
274  auto nested = nested_empty.get<1>().get<0>();
275  EXPECT_TRUE((std::is_same<decltype(contained), decltype(nested)>::value));
276 }
277 
278 TEST(CompressedTupleTest, Reference) {
279  int i = 7;
280  std::string s = "Very long string that goes in the heap";
281  CompressedTuple<int, int&, std::string, std::string&> x(i, i, s, s);
282 
283  // Sanity check. We should have not moved from `s`
284  EXPECT_EQ(s, "Very long string that goes in the heap");
285 
286  EXPECT_EQ(x.get<0>(), x.get<1>());
287  EXPECT_NE(&x.get<0>(), &x.get<1>());
288  EXPECT_EQ(&x.get<1>(), &i);
289 
290  EXPECT_EQ(x.get<2>(), x.get<3>());
291  EXPECT_NE(&x.get<2>(), &x.get<3>());
292  EXPECT_EQ(&x.get<3>(), &s);
293 }
294 
295 TEST(CompressedTupleTest, NoElements) {
296  CompressedTuple<> x;
297  static_cast<void>(x); // Silence -Wunused-variable.
298  EXPECT_TRUE(std::is_empty<CompressedTuple<>>::value);
299 }
300 
301 TEST(CompressedTupleTest, MoveOnlyElements) {
302  CompressedTuple<std::unique_ptr<std::string>> str_tup(
303  absl::make_unique<std::string>("str"));
304 
305  CompressedTuple<CompressedTuple<std::unique_ptr<std::string>>,
306  std::unique_ptr<int>>
307  x(std::move(str_tup), absl::make_unique<int>(5));
308 
309  EXPECT_EQ(*x.get<0>().get<0>(), "str");
310  EXPECT_EQ(*x.get<1>(), 5);
311 
312  std::unique_ptr<std::string> x0 = std::move(x.get<0>()).get<0>();
313  std::unique_ptr<int> x1 = std::move(x).get<1>();
314 
315  EXPECT_EQ(*x0, "str");
316  EXPECT_EQ(*x1, 5);
317 }
318 
319 TEST(CompressedTupleTest, MoveConstructionMoveOnlyElements) {
320  CompressedTuple<std::unique_ptr<std::string>> base(
321  absl::make_unique<std::string>("str"));
322  EXPECT_EQ(*base.get<0>(), "str");
323 
324  CompressedTuple<std::unique_ptr<std::string>> copy(std::move(base));
325  EXPECT_EQ(*copy.get<0>(), "str");
326 }
327 
328 TEST(CompressedTupleTest, AnyElements) {
329  any a(std::string("str"));
330  CompressedTuple<any, any&> x(any(5), a);
331  EXPECT_EQ(absl::any_cast<int>(x.get<0>()), 5);
332  EXPECT_EQ(absl::any_cast<std::string>(x.get<1>()), "str");
333 
334  a = 0.5f;
335  EXPECT_EQ(absl::any_cast<float>(x.get<1>()), 0.5);
336 }
337 
338 TEST(CompressedTupleTest, Constexpr) {
339  struct NonTrivialStruct {
340  constexpr NonTrivialStruct() = default;
341  constexpr int value() const { return v; }
342  int v = 5;
343  };
344  struct TrivialStruct {
345  TrivialStruct() = default;
346  constexpr int value() const { return v; }
347  int v;
348  };
349  constexpr CompressedTuple<int, double, CompressedTuple<int>, Empty<0>> x(
350  7, 1.25, CompressedTuple<int>(5), {});
351  constexpr int x0 = x.get<0>();
352  constexpr double x1 = x.get<1>();
353  constexpr int x2 = x.get<2>().get<0>();
354  constexpr CallType x3 = x.get<3>().value();
355 
356  EXPECT_EQ(x0, 7);
357  EXPECT_EQ(x1, 1.25);
358  EXPECT_EQ(x2, 5);
360 
361 #if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4
362  constexpr CompressedTuple<Empty<0>, TrivialStruct, int> trivial = {};
363  constexpr CallType trivial0 = trivial.get<0>().value();
364  constexpr int trivial1 = trivial.get<1>().value();
365  constexpr int trivial2 = trivial.get<2>();
366 
367  EXPECT_EQ(trivial0, CallType::kConstRef);
368  EXPECT_EQ(trivial1, 0);
369  EXPECT_EQ(trivial2, 0);
370 #endif
371 
372  constexpr CompressedTuple<Empty<0>, NonTrivialStruct, absl::optional<int>>
373  non_trivial = {};
374  constexpr CallType non_trivial0 = non_trivial.get<0>().value();
375  constexpr int non_trivial1 = non_trivial.get<1>().value();
376  constexpr absl::optional<int> non_trivial2 = non_trivial.get<2>();
377 
378  EXPECT_EQ(non_trivial0, CallType::kConstRef);
379  EXPECT_EQ(non_trivial1, 5);
380  EXPECT_EQ(non_trivial2, absl::nullopt);
381 
382  static constexpr char data[] = "DEF";
383  constexpr CompressedTuple<const char*> z(data);
384  constexpr const char* z1 = z.get<0>();
386 
387 #if defined(__clang__)
388  // An apparent bug in earlier versions of gcc claims these are ambiguous.
389  constexpr int x2m = absl::move(x.get<2>()).get<0>();
390  constexpr CallType x3m = absl::move(x).get<3>().value();
391  EXPECT_EQ(x2m, 5);
393 #endif
394 }
395 
396 #if defined(__clang__) || defined(__GNUC__)
397 TEST(CompressedTupleTest, EmptyFinalClass) {
398  struct S final {
399  int f() const { return 5; }
400  };
401  CompressedTuple<S> x;
402  EXPECT_EQ(x.get<0>().f(), 5);
403 }
404 #endif
405 
406 // TODO(b/214288561): enable this test.
407 TEST(CompressedTupleTest, DISABLED_NestedEbo) {
408  struct Empty1 {};
409  struct Empty2 {};
410  CompressedTuple<Empty1, CompressedTuple<Empty2>, int> x;
411  CompressedTuple<Empty1, Empty2, int> y;
412  // Currently fails with sizeof(x) == 8, sizeof(y) == 4.
413  EXPECT_EQ(sizeof(x), sizeof(y));
414 }
415 
416 } // namespace
417 } // namespace container_internal
419 } // namespace absl
absl::flags_internal::Sizeof
size_t Sizeof(FlagOpFn op)
Definition: abseil-cpp/absl/flags/internal/flag.h:136
const
#define const
Definition: bloaty/third_party/zlib/zconf.h:230
TwoValues::value2
U value2
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:46
S
#define S(s)
CallType
CallType
Definition: api_fuzzer.cc:317
y
const double y
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3611
copy
static int copy(grpc_slice_buffer *input, grpc_slice_buffer *output)
Definition: message_compress.cc:145
testing::internal::string
::std::string string
Definition: bloaty/third_party/protobuf/third_party/googletest/googletest/include/gtest/internal/gtest-port.h:881
absl::TEST
TEST(NotificationTest, SanityTest)
Definition: abseil-cpp/absl/synchronization/notification_test.cc:126
CallType::kConstRef
@ kConstRef
a
int a
Definition: abseil-cpp/absl/container/internal/hash_policy_traits_test.cc:88
EXPECT_LE
#define EXPECT_LE(val1, val2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2030
TwoValues::value1
T value1
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:45
z
Uncopyable z
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3612
ABSL_NAMESPACE_END
#define ABSL_NAMESPACE_END
Definition: third_party/abseil-cpp/absl/base/config.h:171
T
#define T(upbtypeconst, upbtype, ctype, default_value)
TwoValues
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:44
EXPECT_EQ
#define EXPECT_EQ(a, b)
Definition: iomgr/time_averaged_stats_test.cc:27
NotEmpty::value
T value
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:40
NotEmpty
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:39
ABSL_NAMESPACE_BEGIN
#define ABSL_NAMESPACE_BEGIN
Definition: third_party/abseil-cpp/absl/base/config.h:170
i1
int i1
Definition: abseil-cpp/absl/container/btree_test.cc:2772
absl::move
constexpr absl::remove_reference_t< T > && move(T &&t) noexcept
Definition: abseil-cpp/absl/utility/utility.h:221
EXPECT_NE
#define EXPECT_NE(val1, val2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2028
setup.v
v
Definition: third_party/bloaty/third_party/capstone/bindings/python/setup.py:42
Empty::value
constexpr CallType value() const &&
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:35
absl::optional
Definition: abseil-cpp/absl/types/internal/optional.h:61
Empty::value
constexpr CallType value() const &
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:34
x
int x
Definition: bloaty/third_party/googletest/googlemock/test/gmock-matchers_test.cc:3610
gen_synthetic_protos.base
base
Definition: gen_synthetic_protos.py:31
data
char data[kBufferLength]
Definition: abseil-cpp/absl/strings/internal/str_format/float_conversion.cc:1006
Empty
Definition: abseil-cpp/absl/container/internal/compressed_tuple_test.cc:33
nested
static int nested
Definition: test-callback-stack.c:39
google_benchmark.example.empty
def empty(state)
Definition: example.py:31
Mock
Definition: bloaty/third_party/googletest/googlemock/test/gmock-nice-strict_test.cc:40
value
const char * value
Definition: hpack_parser_table.cc:165
EXPECT_STREQ
#define EXPECT_STREQ(s1, s2)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:2095
absl::inlined_vector_internal::Reference
ValueType< A > & Reference
Definition: abseil-cpp/absl/container/internal/inlined_vector.h:60
absl::test_internal::CopyableMovableInstance
Definition: abseil-cpp/absl/container/internal/test_instance_tracker.h:240
EXPECT_TRUE
#define EXPECT_TRUE(condition)
Definition: bloaty/third_party/googletest/googletest/include/gtest/gtest.h:1967
absl::test_internal::InstanceTracker
Definition: abseil-cpp/absl/container/internal/test_instance_tracker.h:160
tracker
SessionTracker * tracker
Definition: ssl_session_cache_test.cc:38
absl
Definition: abseil-cpp/absl/algorithm/algorithm.h:31
i2
int i2
Definition: abseil-cpp/absl/container/btree_test.cc:2773
t1
Table t1
Definition: abseil-cpp/absl/container/internal/raw_hash_set_allocator_test.cc:185
CallType::kConstMove
@ kConstMove
i
uint64_t i
Definition: abseil-cpp/absl/container/btree_benchmark.cc:230


grpc
Author(s):
autogenerated on Fri May 16 2025 02:58:00