TestCompactPair.cpp (6321B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include <type_traits> 8 9 #include "mozilla/Assertions.h" 10 #include "mozilla/CompactPair.h" 11 12 using mozilla::CompactPair; 13 using mozilla::MakeCompactPair; 14 15 // Sizes aren't part of the guaranteed CompactPair interface, but we want to 16 // verify our attempts at compactness through EBO are moderately functional, 17 // *somewhere*. 18 #define INSTANTIATE(T1, T2, name, size) \ 19 MOZ_GLOBINIT CompactPair<T1, T2> name##_1(T1(0), T2(0)); \ 20 static_assert(sizeof(name##_1.first()) > 0, \ 21 "first method should work on CompactPair<" #T1 ", " #T2 ">"); \ 22 \ 23 static_assert(sizeof(name##_1.second()) > 0, \ 24 "second method should work on CompactPair<" #T1 ", " #T2 ">"); \ 25 \ 26 static_assert(sizeof(name##_1) == (size), \ 27 "CompactPair<" #T1 ", " #T2 "> has an unexpected size"); \ 28 \ 29 MOZ_GLOBINIT CompactPair<T2, T1> name##_2(T2(0), T1(0)); \ 30 static_assert(sizeof(name##_2.first()) > 0, \ 31 "first method should work on CompactPair<" #T2 ", " #T1 ">"); \ 32 \ 33 static_assert(sizeof(name##_2.second()) > 0, \ 34 "second method should work on CompactPair<" #T2 ", " #T1 ">"); \ 35 \ 36 static_assert(sizeof(name##_2) == (size), \ 37 "CompactPair<" #T2 ", " #T1 "> has an unexpected size"); 38 39 static constexpr std::size_t sizemax(std::size_t a, std::size_t b) { 40 return (a > b) ? a : b; 41 } 42 43 INSTANTIATE(int, int, prim1, 2 * sizeof(int)); 44 INSTANTIATE(int, long, prim2, 45 sizeof(long) + sizemax(sizeof(int), alignof(long))); 46 47 struct EmptyClass { 48 explicit EmptyClass(int) {} 49 }; 50 struct NonEmpty { 51 char mC; 52 explicit NonEmpty(int) : mC('\0') {} 53 }; 54 55 INSTANTIATE(int, EmptyClass, both1, sizeof(int)); 56 INSTANTIATE(int, NonEmpty, both2, sizeof(int) + alignof(int)); 57 INSTANTIATE(EmptyClass, NonEmpty, both3, 1); 58 59 struct A { 60 char dummy; 61 explicit A(int) : dummy('\0') {} 62 }; 63 struct B : A { 64 explicit B(int aI) : A(aI) {} 65 }; 66 67 INSTANTIATE(A, A, class1, 2); 68 INSTANTIATE(A, B, class2, 2); 69 INSTANTIATE(A, EmptyClass, class3, 1); 70 71 struct EmptyNonMovableNonDefaultConstructible { 72 explicit EmptyNonMovableNonDefaultConstructible(int) {} 73 74 EmptyNonMovableNonDefaultConstructible( 75 const EmptyNonMovableNonDefaultConstructible&) = delete; 76 EmptyNonMovableNonDefaultConstructible( 77 EmptyNonMovableNonDefaultConstructible&&) = delete; 78 EmptyNonMovableNonDefaultConstructible& operator=( 79 const EmptyNonMovableNonDefaultConstructible&) = delete; 80 EmptyNonMovableNonDefaultConstructible& operator=( 81 EmptyNonMovableNonDefaultConstructible&&) = delete; 82 }; 83 84 static void TestInPlaceConstruction() { 85 constexpr int firstValue = 42; 86 constexpr int secondValue = 43; 87 88 { 89 const CompactPair<EmptyNonMovableNonDefaultConstructible, int> pair{ 90 std::piecewise_construct, std::tuple(firstValue), 91 std::tuple(secondValue)}; 92 MOZ_RELEASE_ASSERT(pair.second() == secondValue); 93 } 94 95 { 96 const CompactPair<int, EmptyNonMovableNonDefaultConstructible> pair{ 97 std::piecewise_construct, std::tuple(firstValue), 98 std::tuple(secondValue)}; 99 MOZ_RELEASE_ASSERT(pair.first() == firstValue); 100 } 101 102 { 103 const CompactPair<int, int> pair{std::piecewise_construct, 104 std::tuple(firstValue), 105 std::tuple(secondValue)}; 106 MOZ_RELEASE_ASSERT(pair.first() == firstValue); 107 MOZ_RELEASE_ASSERT(pair.second() == secondValue); 108 } 109 110 { 111 const CompactPair<EmptyNonMovableNonDefaultConstructible, 112 EmptyNonMovableNonDefaultConstructible> 113 pair{std::piecewise_construct, std::tuple(firstValue), 114 std::tuple(secondValue)}; 115 116 // nothing to assert here... 117 } 118 } 119 120 struct OtherEmpty : EmptyClass { 121 explicit OtherEmpty(int aI) : EmptyClass(aI) {} 122 }; 123 124 // C++11 requires distinct objects of the same type, within the same "most 125 // derived object", to have different addresses. CompactPair allocates its 126 // elements as two bases, a base and a member, or two members. If the two 127 // elements have non-zero size or are unrelated, no big deal. But if they're 128 // both empty and related, something -- possibly both -- must be inflated. 129 // Exactly which are inflated depends which CompactPairHelper specialization is 130 // used. We could potentially assert something about size for this case, but 131 // whatever we could assert would be very finicky. Plus it's two empty classes 132 // -- hardly likely. So don't bother trying to assert anything about this case. 133 // INSTANTIATE(EmptyClass, OtherEmpty, class4, ...something finicky...); 134 135 int main() { 136 A a(0); 137 B b(0); 138 const A constA(0); 139 const B constB(0); 140 141 // Check that MakeCompactPair generates CompactPair objects of the correct 142 // types. 143 static_assert( 144 std::is_same_v<decltype(MakeCompactPair(A(0), B(0))), CompactPair<A, B>>, 145 "MakeCompactPair should strip rvalue references"); 146 static_assert( 147 std::is_same_v<decltype(MakeCompactPair(a, b)), CompactPair<A, B>>, 148 "MakeCompactPair should strip lvalue references"); 149 static_assert(std::is_same_v<decltype(MakeCompactPair(constA, constB)), 150 CompactPair<A, B>>, 151 "MakeCompactPair should strip CV-qualifiers"); 152 153 // Check that copy assignment and move assignment work. 154 a = constA; 155 a = A(0); 156 157 TestInPlaceConstruction(); 158 159 return 0; 160 }