blob: c1f39c16e89b76ff4d52864152ce6657dec20e56 [file] [log] [blame]
Andrew Top2ea22382016-12-08 09:47:36 -08001/* -*- 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/. */
6
7/* A variadic tuple class. */
8
9#ifndef mozilla_Tuple_h
10#define mozilla_Tuple_h
11
12#include "mozilla/Move.h"
13#include "mozilla/Pair.h"
14#include "mozilla/TemplateLib.h"
15#include "mozilla/TypeTraits.h"
16
17#include <stddef.h>
18#include <utility>
19
20namespace mozilla {
21
22namespace detail {
23
24/*
25 * A helper class that allows passing around multiple variadic argument lists
26 * by grouping them.
27 */
28template<typename... Ts>
29struct Group;
30
31/*
32 * CheckConvertibility checks whether each type in a source pack of types
33 * is convertible to the corresponding type in a target pack of types.
34 *
35 * It is intended to be invoked like this:
36 * CheckConvertibility<Group<SourceTypes...>, Group<TargetTypes...>>
37 * 'Group' is used to separate types in the two packs (otherwise if we just
38 * wrote 'CheckConvertibility<SourceTypes..., TargetTypes...', it couldn't
39 * know where the first pack ends and the second begins).
40 *
41 * Note that we need to check explicitly that the two packs are of the same
42 * size, because attempting to simultaneously expand two parameter packs
43 * is an error (and it would be a hard error, because it wouldn't be in the
44 * immediate context of the caller).
45 */
46
47template<typename Source, typename Target, bool SameSize>
48struct CheckConvertibilityImpl;
49
50template<typename Source, typename Target>
51struct CheckConvertibilityImpl<Source, Target, false>
52 : FalseType {};
53
54template<typename... SourceTypes, typename... TargetTypes>
55struct CheckConvertibilityImpl<Group<SourceTypes...>, Group<TargetTypes...>, true>
56 : IntegralConstant<bool, tl::And<IsConvertible<SourceTypes, TargetTypes>::value...>::value> { };
57
58template<typename Source, typename Target>
59struct CheckConvertibility;
60
61template<typename... SourceTypes, typename... TargetTypes>
62struct CheckConvertibility<Group<SourceTypes...>, Group<TargetTypes...>>
63 : CheckConvertibilityImpl<Group<SourceTypes...>, Group<TargetTypes...>,
64 sizeof...(SourceTypes) == sizeof...(TargetTypes)> { };
65
66/*
67 * TupleImpl is a helper class used to implement mozilla::Tuple.
68 * It represents one node in a recursive inheritance hierarchy.
69 * 'Index' is the 0-based index of the tuple element stored in this node;
70 * 'Elements...' are the types of the elements stored in this node and its
71 * base classes.
72 *
73 * Example:
74 * Tuple<int, float, char> inherits from
75 * TupleImpl<0, int, float, char>, which stores the 'int' and inherits from
76 * TupleImpl<1, float, char>, which stores the 'float' and inherits from
77 * TupleImpl<2, char>, which stores the 'char' and inherits from
78 * TupleImpl<3>, which stores nothing and terminates the recursion.
79 *
80 * The purpose of the 'Index' parameter is to allow efficient index-based
81 * access to a tuple element: given a tuple, and an index 'I' that we wish to
82 * access, we can cast the tuple to the base which stores the I'th element
83 * by performing template argument deduction against 'TupleImpl<I, E...>',
84 * where 'I' is specified explicitly and 'E...' is deduced (this is what the
85 * non-member 'Get<N>(t)' function does).
86 *
87 * This implementation strategy is borrowed from libstdc++'s std::tuple
88 * implementation.
89 */
90template<std::size_t Index, typename... Elements>
91struct TupleImpl;
92
93/*
94 * The base case of the inheritance recursion (and also the implementation
95 * of an empty tuple).
96 */
97template<std::size_t Index>
98struct TupleImpl<Index> {};
99
100/*
101 * One node of the recursive inheritance hierarchy. It stores the element at
102 * index 'Index' of a tuple, of type 'HeadT', and inherits from the nodes
103 * that store the remaining elements, of types 'TailT...'.
104 */
105template<std::size_t Index, typename HeadT, typename... TailT>
106struct TupleImpl<Index, HeadT, TailT...>
107 : public TupleImpl<Index + 1, TailT...>
108{
109 typedef TupleImpl<Index + 1, TailT...> Base;
110
111 // Accessors for the head and the tail.
112 // These are static, because the intended usage is for the caller to,
113 // given a tuple, obtain the type B of the base class which stores the
114 // element of interest, and then call B::Head(tuple) to access it.
115 // (Tail() is mostly for internal use, but is exposed for consistency.)
116 static HeadT& Head(TupleImpl& aTuple) { return aTuple.mHead; }
117 static const HeadT& Head(const TupleImpl& aTuple) { return aTuple.mHead; }
118 static Base& Tail(TupleImpl& aTuple) { return aTuple; }
119 static const Base& Tail(const TupleImpl& aTuple) { return aTuple; }
120
121 TupleImpl() : Base(), mHead() { }
122
123 // Construct from const references to the elements.
124 explicit TupleImpl(const HeadT& aHead, const TailT&... aTail)
125 : Base(aTail...), mHead(aHead) { }
126
127 // Construct from objects that are convertible to the elements.
128 // This constructor is enabled only when the argument types are actually
129 // convertible to the element types, otherwise it could become a better
130 // match for certain invocations than the copy constructor.
131 template <typename OtherHeadT, typename... OtherTailT,
132 typename = typename EnableIf<
133 CheckConvertibility<
134 Group<OtherHeadT, OtherTailT...>,
135 Group<HeadT, TailT...>>::value>::Type>
136 explicit TupleImpl(OtherHeadT&& aHead, OtherTailT&&... aTail)
137 : Base(Forward<OtherTailT>(aTail)...), mHead(Forward<OtherHeadT>(aHead)) { }
138
139 // Copy and move constructors.
140 // We'd like to use '= default' to implement these, but MSVC 2013's support
141 // for '= default' is incomplete and this doesn't work.
142 TupleImpl(const TupleImpl& aOther)
143 : Base(Tail(aOther))
144 , mHead(Head(aOther)) {}
145 TupleImpl(TupleImpl&& aOther)
146 : Base(Move(Tail(aOther)))
147 , mHead(Forward<HeadT>(Head(aOther))) {}
148
149 // Assign from a tuple whose elements are convertible to the elements
150 // of this tuple.
151 template <typename... OtherElements,
152 typename = typename EnableIf<
153 sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
154 TupleImpl& operator=(const TupleImpl<Index, OtherElements...>& aOther)
155 {
156 typedef TupleImpl<Index, OtherElements...> OtherT;
157 Head(*this) = OtherT::Head(aOther);
158 Tail(*this) = OtherT::Tail(aOther);
159 return *this;
160 }
161 template <typename... OtherElements,
162 typename = typename EnableIf<
163 sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
164 TupleImpl& operator=(TupleImpl<Index, OtherElements...>&& aOther)
165 {
166 typedef TupleImpl<Index, OtherElements...> OtherT;
167 Head(*this) = Move(OtherT::Head(aOther));
168 Tail(*this) = Move(OtherT::Tail(aOther));
169 return *this;
170 }
171
172 // Copy and move assignment operators.
173 TupleImpl& operator=(const TupleImpl& aOther)
174 {
175 Head(*this) = Head(aOther);
176 Tail(*this) = Tail(aOther);
177 return *this;
178 }
179 TupleImpl& operator=(TupleImpl&& aOther)
180 {
181 Head(*this) = Move(Head(aOther));
182 Tail(*this) = Move(Tail(aOther));
183 return *this;
184 }
185private:
186 HeadT mHead; // The element stored at this index in the tuple.
187};
188
189} // namespace detail
190
191/**
192 * Tuple is a class that stores zero or more objects, whose types are specified
193 * as template parameters. It can be thought of as a generalization of Pair,
194 * (which can be thought of as a 2-tuple).
195 *
196 * Tuple allows index-based access to its elements (with the index having to be
197 * known at compile time) via the non-member function 'Get<N>(tuple)'.
198 */
199template<typename... Elements>
200class Tuple : public detail::TupleImpl<0, Elements...>
201{
202 typedef detail::TupleImpl<0, Elements...> Impl;
203public:
204 // The constructors and assignment operators here are simple wrappers
205 // around those in TupleImpl.
206
207 Tuple() : Impl() { }
208 explicit Tuple(const Elements&... aElements) : Impl(aElements...) { }
209 // Here, we can't just use 'typename... OtherElements' because MSVC will give
210 // a warning "C4520: multiple default constructors specified" (even if no one
211 // actually instantiates the constructor with an empty parameter pack -
212 // that's probably a bug) and we compile with warnings-as-errors.
213 template <typename OtherHead, typename... OtherTail,
214 typename = typename EnableIf<
215 detail::CheckConvertibility<
216 detail::Group<OtherHead, OtherTail...>,
217 detail::Group<Elements...>>::value>::Type>
218 explicit Tuple(OtherHead&& aHead, OtherTail&&... aTail)
219 : Impl(Forward<OtherHead>(aHead), Forward<OtherTail>(aTail)...) { }
220 Tuple(const Tuple& aOther) : Impl(aOther) { }
221 Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }
222
223 template <typename... OtherElements,
224 typename = typename EnableIf<
225 sizeof...(OtherElements) == sizeof...(Elements)>::Type>
226 Tuple& operator=(const Tuple<OtherElements...>& aOther)
227 {
228 static_cast<Impl&>(*this) = aOther;
229 return *this;
230 }
231 template <typename... OtherElements,
232 typename = typename EnableIf<
233 sizeof...(OtherElements) == sizeof...(Elements)>::Type>
234 Tuple& operator=(Tuple<OtherElements...>&& aOther)
235 {
236 static_cast<Impl&>(*this) = Move(aOther);
237 return *this;
238 }
239 Tuple& operator=(const Tuple& aOther)
240 {
241 static_cast<Impl&>(*this) = aOther;
242 return *this;
243 }
244 Tuple& operator=(Tuple&& aOther)
245 {
246 static_cast<Impl&>(*this) = Move(aOther);
247 return *this;
248 }
249};
250
251/**
252 * Specialization of Tuple for two elements.
253 * This is created to support construction and assignment from a Pair or std::pair.
254 */
255template <typename A, typename B>
256class Tuple<A, B> : public detail::TupleImpl<0, A, B>
257{
258 typedef detail::TupleImpl<0, A, B> Impl;
259
260public:
261 // The constructors and assignment operators here are simple wrappers
262 // around those in TupleImpl.
263
264 Tuple() : Impl() { }
265 explicit Tuple(const A& aA, const B& aB) : Impl(aA, aB) { }
266 template <typename AArg, typename BArg,
267 typename = typename EnableIf<
268 detail::CheckConvertibility<
269 detail::Group<AArg, BArg>,
270 detail::Group<A, B>>::value>::Type>
271 explicit Tuple(AArg&& aA, BArg&& aB)
272 : Impl(Forward<AArg>(aA), Forward<BArg>(aB)) { }
273 Tuple(const Tuple& aOther) : Impl(aOther) { }
274 Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }
275 explicit Tuple(const Pair<A, B>& aOther)
276 : Impl(aOther.first(), aOther.second()) { }
277 explicit Tuple(Pair<A, B>&& aOther) : Impl(Forward<A>(aOther.first()),
278 Forward<B>(aOther.second())) { }
279 explicit Tuple(const std::pair<A, B>& aOther)
280 : Impl(aOther.first, aOther.second) { }
281 explicit Tuple(std::pair<A, B>&& aOther) : Impl(Forward<A>(aOther.first),
282 Forward<B>(aOther.second)) { }
283
284 template <typename AArg, typename BArg>
285 Tuple& operator=(const Tuple<AArg, BArg>& aOther)
286 {
287 static_cast<Impl&>(*this) = aOther;
288 return *this;
289 }
290 template <typename AArg, typename BArg>
291 Tuple& operator=(Tuple<AArg, BArg>&& aOther)
292 {
293 static_cast<Impl&>(*this) = Move(aOther);
294 return *this;
295 }
296 Tuple& operator=(const Tuple& aOther)
297 {
298 static_cast<Impl&>(*this) = aOther;
299 return *this;
300 }
301 Tuple& operator=(Tuple&& aOther)
302 {
303 static_cast<Impl&>(*this) = Move(aOther);
304 return *this;
305 }
306 template <typename AArg, typename BArg>
307 Tuple& operator=(const Pair<AArg, BArg>& aOther)
308 {
309 Impl::Head(*this) = aOther.first();
310 Impl::Tail(*this).Head(*this) = aOther.second();
311 return *this;
312 }
313 template <typename AArg, typename BArg>
314 Tuple& operator=(Pair<AArg, BArg>&& aOther)
315 {
316 Impl::Head(*this) = Forward<AArg>(aOther.first());
317 Impl::Tail(*this).Head(*this) = Forward<BArg>(aOther.second());
318 return *this;
319 }
320 template <typename AArg, typename BArg>
321 Tuple& operator=(const std::pair<AArg, BArg>& aOther)
322 {
323 Impl::Head(*this) = aOther.first;
324 Impl::Tail(*this).Head(*this) = aOther.second;
325 return *this;
326 }
327 template <typename AArg, typename BArg>
328 Tuple& operator=(std::pair<AArg, BArg>&& aOther)
329 {
330 Impl::Head(*this) = Forward<AArg>(aOther.first);
331 Impl::Tail(*this).Head(*this) = Forward<BArg>(aOther.second);
332 return *this;
333 }
334};
335
336/**
337 * Specialization of Tuple for zero arguments.
338 * This is necessary because if the primary template were instantiated with
339 * an empty parameter pack, the 'Tuple(Elements...)' constructors would
340 * become illegal overloads of the default constructor.
341 */
342template <>
343class Tuple<> {};
344
345namespace detail {
346
347/*
348 * Helper functions for implementing Get<N>(tuple).
349 * These functions take a TupleImpl<Index, Elements...>, with Index being
350 * explicitly specified, and Elements being deduced. By passing a Tuple
351 * object as argument, template argument deduction will do its magic and
352 * cast the tuple to the base class which stores the element at Index.
353 */
354
355// Const reference version.
356template<std::size_t Index, typename... Elements>
357auto TupleGetHelper(TupleImpl<Index, Elements...>& aTuple)
358 -> decltype(TupleImpl<Index, Elements...>::Head(aTuple))
359{
360 return TupleImpl<Index, Elements...>::Head(aTuple);
361}
362
363// Non-const reference version.
364template<std::size_t Index, typename... Elements>
365auto TupleGetHelper(const TupleImpl<Index, Elements...>& aTuple)
366 -> decltype(TupleImpl<Index, Elements...>::Head(aTuple))
367{
368 return TupleImpl<Index, Elements...>::Head(aTuple);
369}
370
371} // namespace detail
372
373/**
374 * Index-based access to an element of a tuple.
375 * The syntax is Get<Index>(tuple). The index is zero-based.
376 *
377 * Example:
378 *
379 * Tuple<int, float, char> t;
380 * ...
381 * float f = Get<1>(t);
382 */
383
384// Non-const reference version.
385template<std::size_t Index, typename... Elements>
386auto Get(Tuple<Elements...>& aTuple)
387 -> decltype(detail::TupleGetHelper<Index>(aTuple))
388{
389 return detail::TupleGetHelper<Index>(aTuple);
390}
391
392// Const reference version.
393template<std::size_t Index, typename... Elements>
394auto Get(const Tuple<Elements...>& aTuple)
395 -> decltype(detail::TupleGetHelper<Index>(aTuple))
396{
397 return detail::TupleGetHelper<Index>(aTuple);
398}
399
400// Rvalue reference version.
401template<std::size_t Index, typename... Elements>
402auto Get(Tuple<Elements...>&& aTuple)
403 -> decltype(Move(mozilla::Get<Index>(aTuple)))
404{
405 // We need a 'mozilla::' qualification here to avoid
406 // name lookup only finding the current function.
407 return Move(mozilla::Get<Index>(aTuple));
408}
409
410/**
411 * A convenience function for constructing a tuple out of a sequence of
412 * values without specifying the type of the tuple.
413 * The type of the tuple is deduced from the types of its elements.
414 *
415 * Example:
416 *
417 * auto tuple = MakeTuple(42, 0.5f, 'c'); // has type Tuple<int, float, char>
418 */
419template<typename... Elements>
420inline Tuple<typename Decay<Elements>::Type...>
421MakeTuple(Elements&&... aElements)
422{
423 return Tuple<typename Decay<Elements>::Type...>(Forward<Elements>(aElements)...);
424}
425
426/**
427 * A convenience function for constructing a tuple of references to a
428 * sequence of variables. Since assignments to the elements of the tuple
429 * "go through" to the referenced variables, this can be used to "unpack"
430 * a tuple into individual variables.
431 *
432 * Example:
433 *
434 * int i;
435 * float f;
436 * char c;
437 * Tie(i, f, c) = FunctionThatReturnsATuple();
438 */
439template<typename... Elements>
440inline Tuple<Elements&...>
441Tie(Elements&... aVariables)
442{
443 return Tuple<Elements&...>(aVariables...);
444}
445
446} // namespace mozilla
447
448#endif /* mozilla_Tuple_h */