Actual source code: utility.hpp

  1: #pragma once

  3: #include <petsc/private/cpp/macros.hpp>
  4: #include <petsc/private/cpp/type_traits.hpp>

  6: #include <utility>
  7: #include <cstdint> // std::uint32_t

  9: namespace Petsc
 10: {

 12: namespace util
 13: {

 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: }

 28: template <class T, T... idx>
 29: struct integer_sequence {
 30:   static_assert(std::is_integral<T>::value, "");

 32:   using value_type = T;

 34:   static constexpr std::size_t size() noexcept { return sizeof...(idx); }
 35: };

 37:   #ifndef __has_builtin
 39:   #endif

 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: {

 51: template <class T, int N, T... idx>
 52: struct make_sequence : make_sequence<T, N - 1, T(N - 1), idx...> { };

 54: template <class T, T... idx>
 55: struct make_sequence<T, 0, idx...> {
 56:   using type = integer_sequence<T, idx...>;
 57: };

 59: } // namespace detail

 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

 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)>;

 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: // ==========================================================================================

100: namespace detail
101: {

103: template <bool t_empty, bool u_empty>
104: struct compressed_pair_selector;

106: template <>
107: struct compressed_pair_selector<false, false> : std::integral_constant<int, 0> { };

109: template <>
110: struct compressed_pair_selector<true, false> : std::integral_constant<int, 1> { };

112: template <>
113: struct compressed_pair_selector<false, true> : std::integral_constant<int, 2> { };

115: template <>
116: struct compressed_pair_selector<true, true> : std::integral_constant<int, 3> { };

118: template <typename T, typename U, int selector>
119: class compressed_pair_impl;

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>;

126: public:
127:   using base_type::base_type;
128:   using typename base_type::first_type;
129:   using typename base_type::second_type;

131:   first_type       &first() noexcept { return static_cast<base_type &>(*this).first; }
132:   const first_type &first() const noexcept { return static_cast<const base_type &>(*this).first; }

134:   second_type       &second() noexcept { return static_cast<base_type &>(*this).second; }
135:   const second_type &second() const noexcept { return static_cast<const base_type &>(*this).second; }
136: };

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;

143: public:
144:   using base_type::base_type;
145:   using first_type  = T;
146:   using second_type = U;

148:   compressed_pair_impl() = default;

150:   compressed_pair_impl(first_type x, second_type y) : base_type(std::move_if_noexcept(x)), second_(std::move_if_noexcept(y)) { }

152:   compressed_pair_impl(second_type x) : second_(std::move_if_noexcept(x)) { }

154:   first_type       &first() noexcept { return *this; }
155:   const first_type &first() const noexcept { return *this; }

157:   second_type       &second() noexcept { return second_; }
158:   const second_type &second() const noexcept { return second_; }

160: private:
161:   second_type second_;
162: };

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;

169: public:
170:   using base_type::base_type;
171:   using first_type  = T;
172:   using second_type = U;

174:   compressed_pair_impl() = default;

176:   compressed_pair_impl(first_type x, second_type y) : base_type(std::move_if_noexcept(y)), first_(std::move_if_noexcept(x)) { }

178:   compressed_pair_impl(first_type x) : first_(std::move_if_noexcept(x)) { }

180:   first_type       &first() noexcept { return first_; }
181:   const first_type &first() const noexcept { return first_; }

183:   second_type       &second() noexcept { return *this; }
184:   const second_type &second() const noexcept { return *this; }

186: private:
187:   first_type first_;
188: };

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;

196: public:
197:   using first_type  = T;
198:   using second_type = U;

200:   using first_type::first_type;
201:   using second_type::second_type;

203:   compressed_pair_impl() = default;

205:   compressed_pair_impl(first_type x, second_type y) : first_type(std::move_if_noexcept(x)), second_type(std::move_if_noexcept(y)) { }

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
213:   first_type       &first() noexcept { return static_cast<first_type &>(*this); }
214:   const first_type &first() const noexcept { return static_cast<const first_type &>(*this); }

216:   second_type       &second() noexcept { return static_cast<second_type &>(*this); }
217:   const second_type &second() const noexcept { return static_cast<const second_type &>(*this); }
218: };

220: } // namespace detail

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>;

233: public:
234:   using base_type::base_type;
235: };

237: // intel compilers don't implement empty base optimization, so these tests fail

240: namespace compressed_pair_test
241: {

243: struct Empty { };

245: static_assert(std::is_empty<Empty>::value, "");
246: static_assert(sizeof(Empty) == 1, "");

248: struct Empty2 { };

250: static_assert(std::is_empty<Empty2>::value, "");
251: static_assert(sizeof(Empty2) == 1, "");

253: struct NotEmpty {
254:   std::uint32_t d{};
255: };

257: static_assert(!std::is_empty<NotEmpty>::value, "");
258: static_assert(sizeof(NotEmpty) > 1, "");

260: struct EmptyMember {
261:   Empty  m{};
262:   Empty2 m2{};
263: };

265: static_assert(!std::is_empty<EmptyMember>::value, "");
266: static_assert(sizeof(EmptyMember) > 1, "");

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, "");

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, "");

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)), "");

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)), "");

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)), "");

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)), "");

293: } // namespace compressed_pair_test

295: #endif

297: } // namespace util

299: } // namespace Petsc