blob: 5ebe164942490273ec032d51bfacd3ac4a8be5c5 [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001// Copyright 2018 The Abseil Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// Helper class to perform the Empty Base Optimization.
16// Ts can contain classes and non-classes, empty or not. For the ones that
17// are empty classes, we perform the optimization. If all types in Ts are empty
18// classes, then CompressedTuple<Ts...> is itself an empty class.
19//
20// To access the members, use member get<N>() function.
21//
22// Eg:
23// absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
24// t3);
25// assert(value.get<0>() == 7);
26// T1& t1 = value.get<1>();
27// const T2& t2 = value.get<2>();
28// ...
29//
30// https://en.cppreference.com/w/cpp/language/ebo
31
32#ifndef ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
33#define ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_
34
35#include <initializer_list>
36#include <tuple>
37#include <type_traits>
38#include <utility>
39
40#include "absl/utility/utility.h"
41
42#if defined(_MSC_VER) && !defined(__NVCC__)
43// We need to mark these classes with this declspec to ensure that
44// CompressedTuple happens.
45#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC __declspec(empty_bases)
46#else
47#define ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
48#endif
49
50namespace absl {
Austin Schuhb4691e92020-12-31 12:37:18 -080051ABSL_NAMESPACE_BEGIN
Austin Schuh36244a12019-09-21 17:52:38 -070052namespace container_internal {
53
54template <typename... Ts>
55class CompressedTuple;
56
57namespace internal_compressed_tuple {
58
59template <typename D, size_t I>
60struct Elem;
61template <typename... B, size_t I>
62struct Elem<CompressedTuple<B...>, I>
63 : std::tuple_element<I, std::tuple<B...>> {};
64template <typename D, size_t I>
65using ElemT = typename Elem<D, I>::type;
66
67// Use the __is_final intrinsic if available. Where it's not available, classes
68// declared with the 'final' specifier cannot be used as CompressedTuple
69// elements.
70// TODO(sbenza): Replace this with std::is_final in C++14.
71template <typename T>
72constexpr bool IsFinal() {
73#if defined(__clang__) || defined(__GNUC__)
74 return __is_final(T);
75#else
76 return false;
77#endif
78}
79
80// We can't use EBCO on other CompressedTuples because that would mean that we
81// derive from multiple Storage<> instantiations with the same I parameter,
82// and potentially from multiple identical Storage<> instantiations. So anytime
83// we use type inheritance rather than encapsulation, we mark
84// CompressedTupleImpl, to make this easy to detect.
85struct uses_inheritance {};
86
87template <typename T>
88constexpr bool ShouldUseBase() {
89 return std::is_class<T>::value && std::is_empty<T>::value && !IsFinal<T>() &&
90 !std::is_base_of<uses_inheritance, T>::value;
91}
92
93// The storage class provides two specializations:
94// - For empty classes, it stores T as a base class.
95// - For everything else, it stores T as a member.
96template <typename T, size_t I,
97#if defined(_MSC_VER)
98 bool UseBase =
99 ShouldUseBase<typename std::enable_if<true, T>::type>()>
100#else
101 bool UseBase = ShouldUseBase<T>()>
102#endif
103struct Storage {
104 T value;
105 constexpr Storage() = default;
106 template <typename V>
107 explicit constexpr Storage(absl::in_place_t, V&& v)
108 : value(absl::forward<V>(v)) {}
109 constexpr const T& get() const& { return value; }
110 T& get() & { return value; }
111 constexpr const T&& get() const&& { return absl::move(*this).value; }
112 T&& get() && { return std::move(*this).value; }
113};
114
115template <typename T, size_t I>
116struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC Storage<T, I, true> : T {
117 constexpr Storage() = default;
118
119 template <typename V>
120 explicit constexpr Storage(absl::in_place_t, V&& v)
121 : T(absl::forward<V>(v)) {}
122
123 constexpr const T& get() const& { return *this; }
124 T& get() & { return *this; }
125 constexpr const T&& get() const&& { return absl::move(*this); }
126 T&& get() && { return std::move(*this); }
127};
128
129template <typename D, typename I, bool ShouldAnyUseBase>
130struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl;
131
132template <typename... Ts, size_t... I, bool ShouldAnyUseBase>
133struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
134 CompressedTuple<Ts...>, absl::index_sequence<I...>, ShouldAnyUseBase>
135 // We use the dummy identity function through std::integral_constant to
136 // convince MSVC of accepting and expanding I in that context. Without it
137 // you would get:
138 // error C3548: 'I': parameter pack cannot be used in this context
139 : uses_inheritance,
140 Storage<Ts, std::integral_constant<size_t, I>::value>... {
141 constexpr CompressedTupleImpl() = default;
142 template <typename... Vs>
143 explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
144 : Storage<Ts, I>(absl::in_place, absl::forward<Vs>(args))... {}
145 friend CompressedTuple<Ts...>;
146};
147
148template <typename... Ts, size_t... I>
149struct ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTupleImpl<
150 CompressedTuple<Ts...>, absl::index_sequence<I...>, false>
151 // We use the dummy identity function as above...
152 : Storage<Ts, std::integral_constant<size_t, I>::value, false>... {
153 constexpr CompressedTupleImpl() = default;
154 template <typename... Vs>
155 explicit constexpr CompressedTupleImpl(absl::in_place_t, Vs&&... args)
156 : Storage<Ts, I, false>(absl::in_place, absl::forward<Vs>(args))... {}
157 friend CompressedTuple<Ts...>;
158};
159
160std::false_type Or(std::initializer_list<std::false_type>);
161std::true_type Or(std::initializer_list<bool>);
162
163// MSVC requires this to be done separately rather than within the declaration
164// of CompressedTuple below.
165template <typename... Ts>
166constexpr bool ShouldAnyUseBase() {
167 return decltype(
168 Or({std::integral_constant<bool, ShouldUseBase<Ts>()>()...})){};
169}
170
171template <typename T, typename V>
Austin Schuhb4691e92020-12-31 12:37:18 -0800172using TupleElementMoveConstructible =
173 typename std::conditional<std::is_reference<T>::value,
174 std::is_convertible<V, T>,
175 std::is_constructible<T, V&&>>::type;
176
177template <bool SizeMatches, class T, class... Vs>
178struct TupleMoveConstructible : std::false_type {};
179
180template <class... Ts, class... Vs>
181struct TupleMoveConstructible<true, CompressedTuple<Ts...>, Vs...>
182 : std::integral_constant<
183 bool, absl::conjunction<
184 TupleElementMoveConstructible<Ts, Vs&&>...>::value> {};
185
186template <typename T>
187struct compressed_tuple_size;
188
189template <typename... Es>
190struct compressed_tuple_size<CompressedTuple<Es...>>
191 : public std::integral_constant<std::size_t, sizeof...(Es)> {};
192
193template <class T, class... Vs>
194struct TupleItemsMoveConstructible
195 : std::integral_constant<
196 bool, TupleMoveConstructible<compressed_tuple_size<T>::value ==
197 sizeof...(Vs),
198 T, Vs...>::value> {};
Austin Schuh36244a12019-09-21 17:52:38 -0700199
200} // namespace internal_compressed_tuple
201
202// Helper class to perform the Empty Base Class Optimization.
203// Ts can contain classes and non-classes, empty or not. For the ones that
204// are empty classes, we perform the CompressedTuple. If all types in Ts are
205// empty classes, then CompressedTuple<Ts...> is itself an empty class. (This
206// does not apply when one or more of those empty classes is itself an empty
207// CompressedTuple.)
208//
209// To access the members, use member .get<N>() function.
210//
211// Eg:
212// absl::container_internal::CompressedTuple<int, T1, T2, T3> value(7, t1, t2,
213// t3);
214// assert(value.get<0>() == 7);
215// T1& t1 = value.get<1>();
216// const T2& t2 = value.get<2>();
217// ...
218//
219// https://en.cppreference.com/w/cpp/language/ebo
220template <typename... Ts>
221class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple
222 : private internal_compressed_tuple::CompressedTupleImpl<
223 CompressedTuple<Ts...>, absl::index_sequence_for<Ts...>,
224 internal_compressed_tuple::ShouldAnyUseBase<Ts...>()> {
225 private:
226 template <int I>
227 using ElemT = internal_compressed_tuple::ElemT<CompressedTuple, I>;
228
229 template <int I>
230 using StorageT = internal_compressed_tuple::Storage<ElemT<I>, I>;
231
232 public:
233 // There seems to be a bug in MSVC dealing in which using '=default' here will
234 // cause the compiler to ignore the body of other constructors. The work-
235 // around is to explicitly implement the default constructor.
236#if defined(_MSC_VER)
237 constexpr CompressedTuple() : CompressedTuple::CompressedTupleImpl() {}
238#else
239 constexpr CompressedTuple() = default;
240#endif
241 explicit constexpr CompressedTuple(const Ts&... base)
242 : CompressedTuple::CompressedTupleImpl(absl::in_place, base...) {}
243
Austin Schuhb4691e92020-12-31 12:37:18 -0800244 template <typename First, typename... Vs,
Austin Schuh36244a12019-09-21 17:52:38 -0700245 absl::enable_if_t<
246 absl::conjunction<
247 // Ensure we are not hiding default copy/move constructors.
248 absl::negation<std::is_same<void(CompressedTuple),
Austin Schuhb4691e92020-12-31 12:37:18 -0800249 void(absl::decay_t<First>)>>,
250 internal_compressed_tuple::TupleItemsMoveConstructible<
251 CompressedTuple<Ts...>, First, Vs...>>::value,
Austin Schuh36244a12019-09-21 17:52:38 -0700252 bool> = true>
Austin Schuhb4691e92020-12-31 12:37:18 -0800253 explicit constexpr CompressedTuple(First&& first, Vs&&... base)
Austin Schuh36244a12019-09-21 17:52:38 -0700254 : CompressedTuple::CompressedTupleImpl(absl::in_place,
Austin Schuhb4691e92020-12-31 12:37:18 -0800255 absl::forward<First>(first),
Austin Schuh36244a12019-09-21 17:52:38 -0700256 absl::forward<Vs>(base)...) {}
257
258 template <int I>
259 ElemT<I>& get() & {
Austin Schuhb4691e92020-12-31 12:37:18 -0800260 return StorageT<I>::get();
Austin Schuh36244a12019-09-21 17:52:38 -0700261 }
262
263 template <int I>
264 constexpr const ElemT<I>& get() const& {
265 return StorageT<I>::get();
266 }
267
268 template <int I>
269 ElemT<I>&& get() && {
270 return std::move(*this).StorageT<I>::get();
271 }
272
273 template <int I>
274 constexpr const ElemT<I>&& get() const&& {
275 return absl::move(*this).StorageT<I>::get();
276 }
277};
278
279// Explicit specialization for a zero-element tuple
280// (needed to avoid ambiguous overloads for the default constructor).
281template <>
282class ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC CompressedTuple<> {};
283
284} // namespace container_internal
Austin Schuhb4691e92020-12-31 12:37:18 -0800285ABSL_NAMESPACE_END
Austin Schuh36244a12019-09-21 17:52:38 -0700286} // namespace absl
287
288#undef ABSL_INTERNAL_COMPRESSED_TUPLE_DECLSPEC
289
290#endif // ABSL_CONTAINER_INTERNAL_COMPRESSED_TUPLE_H_