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
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /* A class holding a pair of objects that tries to conserve storage space. */
9 #ifndef mozilla_CompactPair_h
10 #define mozilla_CompactPair_h
12 #include <type_traits>
16 #include "mozilla/Attributes.h"
22 enum StorageType
{ AsBase
, AsMember
};
24 // Optimize storage using the Empty Base Optimization -- that empty base classes
25 // don't take up space -- to optimize size when one or the other class is
26 // stateless and can be used as a base class.
28 // The extra conditions on storage for B are necessary so that CompactPairHelper
29 // won't ambiguously inherit from either A or B, such that one or the other base
30 // class would be inaccessible.
31 template <typename A
, typename B
,
33 std::is_empty_v
<A
> ? detail::AsBase
: detail::AsMember
,
34 detail::StorageType
= std::is_empty_v
<B
> &&
35 !std::is_base_of
<A
, B
>::value
&&
36 !std::is_base_of
<B
, A
>::value
39 struct CompactPairHelper
;
41 template <typename A
, typename B
>
42 struct CompactPairHelper
<A
, B
, AsMember
, AsMember
> {
44 template <typename
... AArgs
, std::size_t... AIndexes
, typename
... BArgs
,
45 std::size_t... BIndexes
>
46 CompactPairHelper(std::tuple
<AArgs
...>& aATuple
,
47 std::tuple
<BArgs
...>& aBTuple
,
48 std::index_sequence
<AIndexes
...>,
49 std::index_sequence
<BIndexes
...>)
50 : mFirstA(std::forward
<AArgs
>(std::get
<AIndexes
>(aATuple
))...),
51 mSecondB(std::forward
<BArgs
>(std::get
<BIndexes
>(aBTuple
))...) {}
54 template <typename AArg
, typename BArg
>
55 CompactPairHelper(AArg
&& aA
, BArg
&& aB
)
56 : mFirstA(std::forward
<AArg
>(aA
)), mSecondB(std::forward
<BArg
>(aB
)) {}
58 A
& first() { return mFirstA
; }
59 const A
& first() const { return mFirstA
; }
60 B
& second() { return mSecondB
; }
61 const B
& second() const { return mSecondB
; }
63 void swap(CompactPairHelper
& aOther
) {
64 std::swap(mFirstA
, aOther
.mFirstA
);
65 std::swap(mSecondB
, aOther
.mSecondB
);
73 template <typename A
, typename B
>
74 struct CompactPairHelper
<A
, B
, AsMember
, AsBase
> : private B
{
76 template <typename
... AArgs
, std::size_t... AIndexes
, typename
... BArgs
,
77 std::size_t... BIndexes
>
78 CompactPairHelper(std::tuple
<AArgs
...>& aATuple
,
79 std::tuple
<BArgs
...>& aBTuple
,
80 std::index_sequence
<AIndexes
...>,
81 std::index_sequence
<BIndexes
...>)
82 : B(std::forward
<BArgs
>(std::get
<BIndexes
>(aBTuple
))...),
83 mFirstA(std::forward
<AArgs
>(std::get
<AIndexes
>(aATuple
))...) {}
86 template <typename AArg
, typename BArg
>
87 CompactPairHelper(AArg
&& aA
, BArg
&& aB
)
88 : B(std::forward
<BArg
>(aB
)), mFirstA(std::forward
<AArg
>(aA
)) {}
90 A
& first() { return mFirstA
; }
91 const A
& first() const { return mFirstA
; }
92 B
& second() { return *this; }
93 const B
& second() const { return *this; }
95 void swap(CompactPairHelper
& aOther
) {
96 std::swap(mFirstA
, aOther
.mFirstA
);
97 std::swap(static_cast<B
&>(*this), static_cast<B
&>(aOther
));
104 template <typename A
, typename B
>
105 struct CompactPairHelper
<A
, B
, AsBase
, AsMember
> : private A
{
107 template <typename
... AArgs
, std::size_t... AIndexes
, typename
... BArgs
,
108 std::size_t... BIndexes
>
109 CompactPairHelper(std::tuple
<AArgs
...>& aATuple
,
110 std::tuple
<BArgs
...>& aBTuple
,
111 std::index_sequence
<AIndexes
...>,
112 std::index_sequence
<BIndexes
...>)
113 : A(std::forward
<AArgs
>(std::get
<AIndexes
>(aATuple
))...),
114 mSecondB(std::forward
<BArgs
>(std::get
<BIndexes
>(aBTuple
))...) {}
117 template <typename AArg
, typename BArg
>
118 CompactPairHelper(AArg
&& aA
, BArg
&& aB
)
119 : A(std::forward
<AArg
>(aA
)), mSecondB(std::forward
<BArg
>(aB
)) {}
121 A
& first() { return *this; }
122 const A
& first() const { return *this; }
123 B
& second() { return mSecondB
; }
124 const B
& second() const { return mSecondB
; }
126 void swap(CompactPairHelper
& aOther
) {
127 std::swap(static_cast<A
&>(*this), static_cast<A
&>(aOther
));
128 std::swap(mSecondB
, aOther
.mSecondB
);
135 template <typename A
, typename B
>
136 struct CompactPairHelper
<A
, B
, AsBase
, AsBase
> : private A
, private B
{
138 template <typename
... AArgs
, std::size_t... AIndexes
, typename
... BArgs
,
139 std::size_t... BIndexes
>
140 CompactPairHelper(std::tuple
<AArgs
...>& aATuple
,
141 std::tuple
<BArgs
...>& aBTuple
,
142 std::index_sequence
<AIndexes
...>,
143 std::index_sequence
<BIndexes
...>)
144 : A(std::forward
<AArgs
>(std::get
<AIndexes
>(aATuple
))...),
145 B(std::forward
<BArgs
>(std::get
<BIndexes
>(aBTuple
))...) {}
148 template <typename AArg
, typename BArg
>
149 CompactPairHelper(AArg
&& aA
, BArg
&& aB
)
150 : A(std::forward
<AArg
>(aA
)), B(std::forward
<BArg
>(aB
)) {}
152 A
& first() { return static_cast<A
&>(*this); }
153 const A
& first() const { return static_cast<A
&>(*this); }
154 B
& second() { return static_cast<B
&>(*this); }
155 const B
& second() const { return static_cast<B
&>(*this); }
157 void swap(CompactPairHelper
& aOther
) {
158 std::swap(static_cast<A
&>(*this), static_cast<A
&>(aOther
));
159 std::swap(static_cast<B
&>(*this), static_cast<B
&>(aOther
));
163 } // namespace detail
166 * CompactPair is the logical concatenation of an instance of A with an instance
167 * B. Space is conserved when possible. Neither A nor B may be a final class.
169 * In general if space conservation is not critical is preferred to use
172 * It's typically clearer to have individual A and B member fields. Except if
173 * you want the space-conserving qualities of CompactPair, you're probably
174 * better off not using this!
176 * No guarantees are provided about the memory layout of A and B, the order of
177 * initialization or destruction of A and B, and so on. (This is approximately
178 * required to optimize space usage.) The first/second names are merely
181 template <typename A
, typename B
>
182 struct CompactPair
: private detail::CompactPairHelper
<A
, B
> {
183 typedef typename
detail::CompactPairHelper
<A
, B
> Base
;
187 template <typename
... AArgs
, typename
... BArgs
>
188 CompactPair(std::piecewise_construct_t
, std::tuple
<AArgs
...> aFirst
,
189 std::tuple
<BArgs
...> aSecond
)
190 : Base(aFirst
, aSecond
, std::index_sequence_for
<AArgs
...>(),
191 std::index_sequence_for
<BArgs
...>()) {}
193 CompactPair(CompactPair
&& aOther
) = default;
194 CompactPair(const CompactPair
& aOther
) = default;
196 CompactPair
& operator=(CompactPair
&& aOther
) = default;
197 CompactPair
& operator=(const CompactPair
& aOther
) = default;
199 /** The A instance. */
201 /** The B instance. */
204 /** Swap this pair with another pair. */
205 void swap(CompactPair
& aOther
) { Base::swap(aOther
); }
209 * MakeCompactPair allows you to construct a CompactPair instance using type
210 * inference. A call like this:
212 * MakeCompactPair(Foo(), Bar())
214 * will return a CompactPair<Foo, Bar>.
216 template <typename A
, typename B
>
217 CompactPair
<std::remove_cv_t
<std::remove_reference_t
<A
>>,
218 std::remove_cv_t
<std::remove_reference_t
<B
>>>
219 MakeCompactPair(A
&& aA
, B
&& aB
) {
220 return CompactPair
<std::remove_cv_t
<std::remove_reference_t
<A
>>,
221 std::remove_cv_t
<std::remove_reference_t
<B
>>>(
222 std::forward
<A
>(aA
), std::forward
<B
>(aB
));
225 } // namespace mozilla
229 template <typename A
, class B
>
230 void swap(mozilla::CompactPair
<A
, B
>& aX
, mozilla::CompactPair
<A
, B
>& aY
) {
236 #endif /* mozilla_CompactPair_h */