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