summaryrefslogtreecommitdiffstats
path: root/mfbt/Tuple.h
diff options
context:
space:
mode:
Diffstat (limited to 'mfbt/Tuple.h')
-rw-r--r--mfbt/Tuple.h461
1 files changed, 461 insertions, 0 deletions
diff --git a/mfbt/Tuple.h b/mfbt/Tuple.h
new file mode 100644
index 000000000..a7f9bee6c
--- /dev/null
+++ b/mfbt/Tuple.h
@@ -0,0 +1,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 */