1 #pragma once 2 3 #include <petsc/private/cpp/macros.hpp> 4 #include <petsc/private/cpp/type_traits.hpp> 5 6 #include <utility> 7 #include <cstdint> // std::uint32_t 8 9 namespace Petsc 10 { 11 12 namespace util 13 { 14 15 #if PETSC_CPP_VERSION >= 14 // C++14 16 using std::exchange; 17 using std::integer_sequence; 18 using std::make_integer_sequence; 19 #else 20 template <class T, class U = T> 21 inline T exchange(T &orig, U &&new_value) 22 { 23 T old_value = std::move(orig); 24 orig = std::forward<U>(new_value); 25 return old_value; 26 } 27 28 template <class T, T... idx> 29 struct integer_sequence { 30 static_assert(std::is_integral<T>::value, ""); 31 32 using value_type = T; 33 34 static constexpr std::size_t size() noexcept { return sizeof...(idx); } 35 }; 36 37 #if !defined(__has_builtin) 38 #define __has_builtin(x) 0 39 #endif 40 41 #if __has_builtin(__make_integer_seq) // clang, MSVC 42 template <class T, T N> 43 using make_integer_sequence = __make_integer_seq<integer_sequence, T, N>; 44 #elif defined(__GNUC__) && __GNUC__ >= 8 // gcc 45 template <class T, T N> 46 using make_integer_sequence = integer_sequence<T, __integer_pack(N)...>; 47 #else // __slow__ version 48 namespace detail 49 { 50 51 template <class T, int N, T... idx> 52 struct make_sequence : make_sequence<T, N - 1, T(N - 1), idx...> { }; 53 54 template <class T, T... idx> 55 struct make_sequence<T, 0, idx...> { 56 using type = integer_sequence<T, idx...>; 57 }; 58 59 } // namespace detail 60 61 template <class T, T N> 62 using make_integer_sequence = typename detail::make_sequence<T, int(N)>::type; 63 #endif // __has_builtin(__make_integer_seq) 64 #endif // C++14 65 66 template <std::size_t... idx> 67 using index_sequence = integer_sequence<std::size_t, idx...>; 68 template <std::size_t N> 69 using make_index_sequence = make_integer_sequence<std::size_t, N>; 70 template <class... T> 71 using index_sequence_for = make_index_sequence<sizeof...(T)>; 72 73 // ========================================================================================== 74 // compressed_pair 75 // 76 // Like std::pair except that it potentially stores both first and second as base classes if 77 // either or both are "empty" classes. This allows empty-base-optimization to kick in. Normally 78 // in C++ a structure must have a minimum memory footprint of 1 byte. For example 79 // 80 // struct Foo { }; // empty! 81 // 82 // struct Bar 83 // { 84 // Foo f; // even though Foo is empty, member 'f' will always occupy 1 byte in memory 85 // }; 86 // 87 // This restriction does not hold for base classes however, so changing the above declarations 88 // to 89 // 90 // struct Foo { }; // empty! 91 // 92 // struct Bar : Foo 93 // { 94 // 95 // }; 96 // 97 // Results in Bar now potentially occupying no space whatsoever. 98 // ========================================================================================== 99 100 namespace detail 101 { 102 103 template <bool t_empty, bool u_empty> 104 struct compressed_pair_selector; 105 106 template <> 107 struct compressed_pair_selector<false, false> : std::integral_constant<int, 0> { }; 108 109 template <> 110 struct compressed_pair_selector<true, false> : std::integral_constant<int, 1> { }; 111 112 template <> 113 struct compressed_pair_selector<false, true> : std::integral_constant<int, 2> { }; 114 115 template <> 116 struct compressed_pair_selector<true, true> : std::integral_constant<int, 3> { }; 117 118 template <typename T, typename U, int selector> 119 class compressed_pair_impl; 120 121 // selector = 0, neither are empty, derive directly from std::pair 122 template <typename T, typename U> 123 class compressed_pair_impl<T, U, 0> : std::pair<T, U> { 124 using base_type = std::pair<T, U>; 125 126 public: 127 using base_type::base_type; 128 using typename base_type::first_type; 129 using typename base_type::second_type; 130 first()131 first_type &first() noexcept { return static_cast<base_type &>(*this).first; } first() const132 const first_type &first() const noexcept { return static_cast<const base_type &>(*this).first; } 133 second()134 second_type &second() noexcept { return static_cast<base_type &>(*this).second; } second() const135 const second_type &second() const noexcept { return static_cast<const base_type &>(*this).second; } 136 }; 137 138 // selector = 1, T is empty 139 template <typename T, typename U> 140 class compressed_pair_impl<T, U, 1> : T { 141 using base_type = T; 142 143 public: 144 using base_type::base_type; 145 using first_type = T; 146 using second_type = U; 147 148 compressed_pair_impl() = default; 149 compressed_pair_impl(first_type x,second_type y)150 compressed_pair_impl(first_type x, second_type y) : base_type(std::move_if_noexcept(x)), second_(std::move_if_noexcept(y)) { } 151 compressed_pair_impl(second_type x)152 compressed_pair_impl(second_type x) : second_(std::move_if_noexcept(x)) { } 153 first()154 first_type &first() noexcept { return *this; } first() const155 const first_type &first() const noexcept { return *this; } 156 second()157 second_type &second() noexcept { return second_; } second() const158 const second_type &second() const noexcept { return second_; } 159 160 private: 161 second_type second_; 162 }; 163 164 // selector = 2, U is empty 165 template <typename T, typename U> 166 class compressed_pair_impl<T, U, 2> : U { 167 using base_type = U; 168 169 public: 170 using base_type::base_type; 171 using first_type = T; 172 using second_type = U; 173 174 compressed_pair_impl() = default; 175 compressed_pair_impl(first_type x,second_type y)176 compressed_pair_impl(first_type x, second_type y) : base_type(std::move_if_noexcept(y)), first_(std::move_if_noexcept(x)) { } 177 compressed_pair_impl(first_type x)178 compressed_pair_impl(first_type x) : first_(std::move_if_noexcept(x)) { } 179 first()180 first_type &first() noexcept { return first_; } first() const181 const first_type &first() const noexcept { return first_; } 182 second()183 second_type &second() noexcept { return *this; } second() const184 const second_type &second() const noexcept { return *this; } 185 186 private: 187 first_type first_; 188 }; 189 190 // selector = 3, T and U are both empty 191 template <typename T, typename U> 192 class compressed_pair_impl<T, U, 3> : T, U { 193 using first_base_type = T; 194 using second_base_type = U; 195 196 public: 197 using first_type = T; 198 using second_type = U; 199 200 using first_type::first_type; 201 using second_type::second_type; 202 203 compressed_pair_impl() = default; 204 compressed_pair_impl(first_type x,second_type y)205 compressed_pair_impl(first_type x, second_type y) : first_type(std::move_if_noexcept(x)), second_type(std::move_if_noexcept(y)) { } 206 207 // Casts are needed to disambiguate case where T or U derive from one another, for example 208 // 209 // struct T { }; 210 // struct U : T { }; 211 // 212 // In this case both U and T are able to satisfy "conversion" to T first()213 first_type &first() noexcept { return static_cast<first_type &>(*this); } first() const214 const first_type &first() const noexcept { return static_cast<const first_type &>(*this); } 215 second()216 second_type &second() noexcept { return static_cast<second_type &>(*this); } second() const217 const second_type &second() const noexcept { return static_cast<const second_type &>(*this); } 218 }; 219 220 } // namespace detail 221 222 // clang-format off 223 template <typename T, typename U> 224 class compressed_pair 225 : public detail::compressed_pair_impl< 226 T, U, 227 detail::compressed_pair_selector<std::is_empty<T>::value, std::is_empty<U>::value>::value 228 > 229 // clang-format on 230 { 231 using base_type = detail::compressed_pair_impl<T, U, detail::compressed_pair_selector<std::is_empty<T>::value, std::is_empty<U>::value>::value>; 232 233 public: 234 using base_type::base_type; 235 }; 236 237 // intel compilers don't implement empty base optimization, so these tests fail 238 #if !defined(__INTEL_COMPILER) && !defined(__ICL) 239 240 namespace compressed_pair_test 241 { 242 243 struct Empty { }; 244 245 static_assert(std::is_empty<Empty>::value, ""); 246 static_assert(sizeof(Empty) == 1, ""); 247 248 struct Empty2 { }; 249 250 static_assert(std::is_empty<Empty2>::value, ""); 251 static_assert(sizeof(Empty2) == 1, ""); 252 253 struct NotEmpty { 254 std::uint32_t d{}; 255 }; 256 257 static_assert(!std::is_empty<NotEmpty>::value, ""); 258 static_assert(sizeof(NotEmpty) > 1, ""); 259 260 struct EmptyMember { 261 Empty m{}; 262 Empty2 m2{}; 263 }; 264 265 static_assert(!std::is_empty<EmptyMember>::value, ""); 266 static_assert(sizeof(EmptyMember) > 1, ""); 267 268 // empty-empty should only be 1 byte since both are compressed out 269 static_assert(std::is_empty<compressed_pair<Empty, Empty2>>::value, ""); 270 static_assert(sizeof(compressed_pair<Empty, Empty2>) == 1, ""); 271 272 // flipping template param order changes nothing 273 static_assert(std::is_empty<compressed_pair<Empty2, Empty>>::value, ""); 274 static_assert(sizeof(compressed_pair<Empty2, Empty>) == 1, ""); 275 276 // empty-not_empty should be less than sum of sizes, since empty is compressed out 277 static_assert(!std::is_empty<compressed_pair<Empty, NotEmpty>>::value, ""); 278 static_assert(sizeof(compressed_pair<Empty, NotEmpty>) < (sizeof(Empty) + sizeof(NotEmpty)), ""); 279 280 // flipping template param order changes nothing 281 static_assert(!std::is_empty<compressed_pair<NotEmpty, Empty>>::value, ""); 282 static_assert(sizeof(compressed_pair<NotEmpty, Empty>) < (sizeof(NotEmpty) + sizeof(Empty)), ""); 283 284 // empty_member-not_empty should also be greater than or equal to sum of sizes (g.t. because 285 // potential padding) because neither is compressed away 286 static_assert(!std::is_empty<compressed_pair<EmptyMember, NotEmpty>>::value, ""); 287 static_assert(sizeof(compressed_pair<EmptyMember, NotEmpty>) >= (sizeof(EmptyMember) + sizeof(NotEmpty)), ""); 288 289 // flipping template param order changes nothing 290 static_assert(!std::is_empty<compressed_pair<NotEmpty, EmptyMember>>::value, ""); 291 static_assert(sizeof(compressed_pair<NotEmpty, EmptyMember>) >= (sizeof(NotEmpty) + sizeof(EmptyMember)), ""); 292 293 } // namespace compressed_pair_test 294 295 #endif 296 297 } // namespace util 298 299 } // namespace Petsc 300