summaryrefslogtreecommitdiff
path: root/mfbt/Tuple.h
blob: a7f9bee6cdd58d36a7c80ba2c0555a86d30a5735 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/* A variadic tuple class. */

#ifndef mozilla_Tuple_h
#define mozilla_Tuple_h

#include "mozilla/Move.h"
#include "mozilla/Pair.h"
#include "mozilla/TemplateLib.h"
#include "mozilla/TypeTraits.h"

#include <stddef.h>
#include <utility>

namespace mozilla {

namespace detail {

/*
 * A helper class that allows passing around multiple variadic argument lists
 * by grouping them.
 */
template<typename... Ts>
struct Group;

/*
 * CheckConvertibility checks whether each type in a source pack of types
 * is convertible to the corresponding type in a target pack of types.
 *
 * It is intended to be invoked like this:
 *   CheckConvertibility<Group<SourceTypes...>, Group<TargetTypes...>>
 * 'Group' is used to separate types in the two packs (otherwise if we just
 * wrote 'CheckConvertibility<SourceTypes..., TargetTypes...', it couldn't
 * know where the first pack ends and the second begins).
 *
 * Note that we need to check explicitly that the two packs are of the same
 * size, because attempting to simultaneously expand two parameter packs
 * is an error (and it would be a hard error, because it wouldn't be in the
 * immediate context of the caller).
 */

template<typename Source, typename Target, bool SameSize>
struct CheckConvertibilityImpl;

template<typename Source, typename Target>
struct CheckConvertibilityImpl<Source, Target, false>
  : FalseType {};

template<typename... SourceTypes, typename... TargetTypes>
struct CheckConvertibilityImpl<Group<SourceTypes...>, Group<TargetTypes...>, true>
  : IntegralConstant<bool, tl::And<IsConvertible<SourceTypes, TargetTypes>::value...>::value> { };

template<typename Source, typename Target>
struct CheckConvertibility;

template<typename... SourceTypes, typename... TargetTypes>
struct CheckConvertibility<Group<SourceTypes...>, Group<TargetTypes...>>
  : CheckConvertibilityImpl<Group<SourceTypes...>, Group<TargetTypes...>,
        sizeof...(SourceTypes) == sizeof...(TargetTypes)> { };

/*
 * TupleImpl is a helper class used to implement mozilla::Tuple.
 * It represents one node in a recursive inheritance hierarchy.
 * 'Index' is the 0-based index of the tuple element stored in this node;
 * 'Elements...' are the types of the elements stored in this node and its
 * base classes.
 *
 * Example:
 *   Tuple<int, float, char> inherits from
 *   TupleImpl<0, int, float, char>, which stores the 'int' and inherits from
 *   TupleImpl<1, float, char>, which stores the 'float' and inherits from
 *   TupleImpl<2, char>, which stores the 'char' and inherits from
 *   TupleImpl<3>, which stores nothing and terminates the recursion.
 *
 * The purpose of the 'Index' parameter is to allow efficient index-based
 * access to a tuple element: given a tuple, and an index 'I' that we wish to
 * access, we can cast the tuple to the base which stores the I'th element
 * by performing template argument deduction against 'TupleImpl<I, E...>',
 * where 'I' is specified explicitly and 'E...' is deduced (this is what the
 * non-member 'Get<N>(t)' function does).
 *
 * This implementation strategy is borrowed from libstdc++'s std::tuple
 * implementation.
 */
template<std::size_t Index, typename... Elements>
struct TupleImpl;

/*
 * The base case of the inheritance recursion (and also the implementation
 * of an empty tuple).
 */
template<std::size_t Index>
struct TupleImpl<Index> {
  bool operator==(const TupleImpl<Index>& aOther) const
  {
    return true;
  }
};

/*
 * One node of the recursive inheritance hierarchy. It stores the element at
 * index 'Index' of a tuple, of type 'HeadT', and inherits from the nodes
 * that store the remaining elements, of types 'TailT...'.
 */
template<std::size_t Index, typename HeadT, typename... TailT>
struct TupleImpl<Index, HeadT, TailT...>
  : public TupleImpl<Index + 1, TailT...>
{
  typedef TupleImpl<Index + 1, TailT...> Base;

  // Accessors for the head and the tail.
  // These are static, because the intended usage is for the caller to,
  // given a tuple, obtain the type B of the base class which stores the
  // element of interest, and then call B::Head(tuple) to access it.
  // (Tail() is mostly for internal use, but is exposed for consistency.)
  static HeadT& Head(TupleImpl& aTuple) { return aTuple.mHead; }
  static const HeadT& Head(const TupleImpl& aTuple) { return aTuple.mHead; }
  static Base& Tail(TupleImpl& aTuple) { return aTuple; }
  static const Base& Tail(const TupleImpl& aTuple) { return aTuple; }

  TupleImpl() : Base(), mHead() { }

  // Construct from const references to the elements.
  explicit TupleImpl(const HeadT& aHead, const TailT&... aTail)
    : Base(aTail...), mHead(aHead) { }

  // Construct from objects that are convertible to the elements.
  // This constructor is enabled only when the argument types are actually
  // convertible to the element types, otherwise it could become a better
  // match for certain invocations than the copy constructor.
  template <typename OtherHeadT, typename... OtherTailT,
            typename = typename EnableIf<
                CheckConvertibility<
                    Group<OtherHeadT, OtherTailT...>,
                    Group<HeadT, TailT...>>::value>::Type>
  explicit TupleImpl(OtherHeadT&& aHead, OtherTailT&&... aTail)
    : Base(Forward<OtherTailT>(aTail)...), mHead(Forward<OtherHeadT>(aHead)) { }

  // Copy and move constructors.
  // We'd like to use '= default' to implement these, but MSVC 2013's support
  // for '= default' is incomplete and this doesn't work.
  TupleImpl(const TupleImpl& aOther)
    : Base(Tail(aOther))
    , mHead(Head(aOther)) {}
  TupleImpl(TupleImpl&& aOther)
    : Base(Move(Tail(aOther)))
    , mHead(Forward<HeadT>(Head(aOther))) {}

  // Assign from a tuple whose elements are convertible to the elements
  // of this tuple.
  template <typename... OtherElements,
            typename = typename EnableIf<
                sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
  TupleImpl& operator=(const TupleImpl<Index, OtherElements...>& aOther)
  {
    typedef TupleImpl<Index, OtherElements...> OtherT;
    Head(*this) = OtherT::Head(aOther);
    Tail(*this) = OtherT::Tail(aOther);
    return *this;
  }
  template <typename... OtherElements,
            typename = typename EnableIf<
                sizeof...(OtherElements) == sizeof...(TailT) + 1>::Type>
  TupleImpl& operator=(TupleImpl<Index, OtherElements...>&& aOther)
  {
    typedef TupleImpl<Index, OtherElements...> OtherT;
    Head(*this) = Move(OtherT::Head(aOther));
    Tail(*this) = Move(OtherT::Tail(aOther));
    return *this;
  }

  // Copy and move assignment operators.
  TupleImpl& operator=(const TupleImpl& aOther)
  {
    Head(*this) = Head(aOther);
    Tail(*this) = Tail(aOther);
    return *this;
  }
  TupleImpl& operator=(TupleImpl&& aOther)
  {
    Head(*this) = Move(Head(aOther));
    Tail(*this) = Move(Tail(aOther));
    return *this;
  }
  bool operator==(const TupleImpl& aOther) const
  {
    return Head(*this) == Head(aOther) && Tail(*this) == Tail(aOther);
  }
private:
  HeadT mHead;  // The element stored at this index in the tuple.
};

} // namespace detail

/**
 * Tuple is a class that stores zero or more objects, whose types are specified
 * as template parameters. It can be thought of as a generalization of Pair,
 * (which can be thought of as a 2-tuple).
 *
 * Tuple allows index-based access to its elements (with the index having to be
 * known at compile time) via the non-member function 'Get<N>(tuple)'.
 */
template<typename... Elements>
class Tuple : public detail::TupleImpl<0, Elements...>
{
  typedef detail::TupleImpl<0, Elements...> Impl;
public:
  // The constructors and assignment operators here are simple wrappers
  // around those in TupleImpl.

  Tuple() : Impl() { }
  explicit Tuple(const Elements&... aElements) : Impl(aElements...) { }
  // Here, we can't just use 'typename... OtherElements' because MSVC will give
  // a warning "C4520: multiple default constructors specified" (even if no one
  // actually instantiates the constructor with an empty parameter pack -
  // that's probably a bug) and we compile with warnings-as-errors.
  template <typename OtherHead, typename... OtherTail,
            typename = typename EnableIf<
                detail::CheckConvertibility<
                    detail::Group<OtherHead, OtherTail...>,
                    detail::Group<Elements...>>::value>::Type>
  explicit Tuple(OtherHead&& aHead, OtherTail&&... aTail)
    : Impl(Forward<OtherHead>(aHead), Forward<OtherTail>(aTail)...) { }
  Tuple(const Tuple& aOther) : Impl(aOther) { }
  Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }

  template <typename... OtherElements,
            typename = typename EnableIf<
                sizeof...(OtherElements) == sizeof...(Elements)>::Type>
  Tuple& operator=(const Tuple<OtherElements...>& aOther)
  {
    static_cast<Impl&>(*this) = aOther;
    return *this;
  }
  template <typename... OtherElements,
            typename = typename EnableIf<
                sizeof...(OtherElements) == sizeof...(Elements)>::Type>
  Tuple& operator=(Tuple<OtherElements...>&& aOther)
  {
    static_cast<Impl&>(*this) = Move(aOther);
    return *this;
  }
  Tuple& operator=(const Tuple& aOther)
  {
    static_cast<Impl&>(*this) = aOther;
    return *this;
  }
  Tuple& operator=(Tuple&& aOther)
  {
    static_cast<Impl&>(*this) = Move(aOther);
    return *this;
  }
  bool operator==(const Tuple& aOther) const
  {
    return static_cast<const Impl&>(*this) == static_cast<const Impl&>(aOther);
  }
};

/**
 * Specialization of Tuple for two elements.
 * This is created to support construction and assignment from a Pair or std::pair.
 */
template <typename A, typename B>
class Tuple<A, B> : public detail::TupleImpl<0, A, B>
{
  typedef detail::TupleImpl<0, A, B> Impl;

public:
  // The constructors and assignment operators here are simple wrappers
  // around those in TupleImpl.

  Tuple() : Impl() { }
  explicit Tuple(const A& aA, const B& aB) : Impl(aA, aB) { }
  template <typename AArg, typename BArg,
            typename = typename EnableIf<
                detail::CheckConvertibility<
                    detail::Group<AArg, BArg>,
                    detail::Group<A, B>>::value>::Type>
  explicit Tuple(AArg&& aA, BArg&& aB)
    : Impl(Forward<AArg>(aA), Forward<BArg>(aB)) { }
  Tuple(const Tuple& aOther) : Impl(aOther) { }
  Tuple(Tuple&& aOther) : Impl(Move(aOther)) { }
  explicit Tuple(const Pair<A, B>& aOther)
    : Impl(aOther.first(), aOther.second()) { }
  explicit Tuple(Pair<A, B>&& aOther) : Impl(Forward<A>(aOther.first()),
                                    Forward<B>(aOther.second())) { }
  explicit Tuple(const std::pair<A, B>& aOther)
    : Impl(aOther.first, aOther.second) { }
  explicit Tuple(std::pair<A, B>&& aOther) : Impl(Forward<A>(aOther.first),
                                    Forward<B>(aOther.second)) { }

  template <typename AArg, typename BArg>
  Tuple& operator=(const Tuple<AArg, BArg>& aOther)
  {
    static_cast<Impl&>(*this) = aOther;
    return *this;
  }
  template <typename AArg, typename BArg>
  Tuple& operator=(Tuple<AArg, BArg>&& aOther)
  {
    static_cast<Impl&>(*this) = Move(aOther);
    return *this;
  }
  Tuple& operator=(const Tuple& aOther)
  {
    static_cast<Impl&>(*this) = aOther;
    return *this;
  }
  Tuple& operator=(Tuple&& aOther)
  {
    static_cast<Impl&>(*this) = Move(aOther);
    return *this;
  }
  template <typename AArg, typename BArg>
  Tuple& operator=(const Pair<AArg, BArg>& aOther)
  {
    Impl::Head(*this) = aOther.first();
    Impl::Tail(*this).Head(*this) = aOther.second();
    return *this;
  }
  template <typename AArg, typename BArg>
  Tuple& operator=(Pair<AArg, BArg>&& aOther)
  {
    Impl::Head(*this) = Forward<AArg>(aOther.first());
    Impl::Tail(*this).Head(*this) = Forward<BArg>(aOther.second());
    return *this;
  }
  template <typename AArg, typename BArg>
  Tuple& operator=(const std::pair<AArg, BArg>& aOther)
  {
    Impl::Head(*this) = aOther.first;
    Impl::Tail(*this).Head(*this) = aOther.second;
    return *this;
  }
  template <typename AArg, typename BArg>
  Tuple& operator=(std::pair<AArg, BArg>&& aOther)
  {
    Impl::Head(*this) = Forward<AArg>(aOther.first);
    Impl::Tail(*this).Head(*this) = Forward<BArg>(aOther.second);
    return *this;
  }
};

/**
 * Specialization of Tuple for zero arguments.
 * This is necessary because if the primary template were instantiated with
 * an empty parameter pack, the 'Tuple(Elements...)' constructors would
 * become illegal overloads of the default constructor.
 */
template <>
class Tuple<> {};

namespace detail {

/*
 * Helper functions for implementing Get<N>(tuple).
 * These functions take a TupleImpl<Index, Elements...>, with Index being
 * explicitly specified, and Elements being deduced. By passing a Tuple
 * object as argument, template argument deduction will do its magic and
 * cast the tuple to the base class which stores the element at Index.
 */

// Const reference version.
template<std::size_t Index, typename... Elements>
auto TupleGetHelper(TupleImpl<Index, Elements...>& aTuple)
    -> decltype(TupleImpl<Index, Elements...>::Head(aTuple))
{
  return TupleImpl<Index, Elements...>::Head(aTuple);
}

// Non-const reference version.
template<std::size_t Index, typename... Elements>
auto TupleGetHelper(const TupleImpl<Index, Elements...>& aTuple)
    -> decltype(TupleImpl<Index, Elements...>::Head(aTuple))
{
  return TupleImpl<Index, Elements...>::Head(aTuple);
}

} // namespace detail

/**
 * Index-based access to an element of a tuple.
 * The syntax is Get<Index>(tuple). The index is zero-based.
 *
 * Example:
 *
 * Tuple<int, float, char> t;
 * ...
 * float f = Get<1>(t);
 */

// Non-const reference version.
template<std::size_t Index, typename... Elements>
auto Get(Tuple<Elements...>& aTuple)
    -> decltype(detail::TupleGetHelper<Index>(aTuple))
{
  return detail::TupleGetHelper<Index>(aTuple);
}

// Const reference version.
template<std::size_t Index, typename... Elements>
auto Get(const Tuple<Elements...>& aTuple)
    -> decltype(detail::TupleGetHelper<Index>(aTuple))
{
  return detail::TupleGetHelper<Index>(aTuple);
}

// Rvalue reference version.
template<std::size_t Index, typename... Elements>
auto Get(Tuple<Elements...>&& aTuple)
    -> decltype(Move(mozilla::Get<Index>(aTuple)))
{
  // We need a 'mozilla::' qualification here to avoid
  // name lookup only finding the current function.
  return Move(mozilla::Get<Index>(aTuple));
}

/**
 * A convenience function for constructing a tuple out of a sequence of
 * values without specifying the type of the tuple.
 * The type of the tuple is deduced from the types of its elements.
 *
 * Example:
 *
 * auto tuple = MakeTuple(42, 0.5f, 'c');  // has type Tuple<int, float, char>
 */
template<typename... Elements>
inline Tuple<typename Decay<Elements>::Type...>
MakeTuple(Elements&&... aElements)
{
  return Tuple<typename Decay<Elements>::Type...>(Forward<Elements>(aElements)...);
}

/**
 * A convenience function for constructing a tuple of references to a
 * sequence of variables. Since assignments to the elements of the tuple
 * "go through" to the referenced variables, this can be used to "unpack"
 * a tuple into individual variables.
 *
 * Example:
 *
 * int i;
 * float f;
 * char c;
 * Tie(i, f, c) = FunctionThatReturnsATuple();
 */
template<typename... Elements>
inline Tuple<Elements&...>
Tie(Elements&... aVariables)
{
  return Tuple<Elements&...>(aVariables...);
}

} // namespace mozilla

#endif /* mozilla_Tuple_h */