/* -*- 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 #include #include #include "mozilla/CompactPair.h" #include "mozilla/TemplateLib.h" namespace mozilla { namespace detail { /* * A helper class that allows passing around multiple variadic argument lists * by grouping them. */ template 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> * 'Group' is used to separate types in the two packs (otherwise if we just * wrote 'CheckConvertibility struct CheckConvertibilityImpl; template struct CheckConvertibilityImpl : std::false_type {}; template struct CheckConvertibilityImpl, Group, true> : std::integral_constant< bool, tl::And...>::value> { }; template struct CheckConvertibility; template struct CheckConvertibility, Group> : CheckConvertibilityImpl, Group, sizeof...(SourceTypes) == sizeof...(TargetTypes)> {}; /* * Helper type for Tie(args...) to allow ignoring specific elements * during Tie unpacking. Supports assignment from any type. * * Not for direct usage; instead, use mozilla::Ignore in calls to Tie. */ struct IgnoreImpl { template constexpr const IgnoreImpl& operator=(const T&) const { return *this; } }; /* * 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 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', * where 'I' is specified explicitly and 'E...' is deduced (this is what the * non-member 'Get(t)' function does). * * This implementation strategy is borrowed from libstdc++'s std::tuple * implementation. */ template struct TupleImpl; /* * The base case of the inheritance recursion (and also the implementation * of an empty tuple). */ template struct TupleImpl { bool operator==(const TupleImpl& aOther) const { return true; } template void ForEach(const F& aFunc) const {} }; /* * 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 struct TupleImpl : public TupleImpl { typedef TupleImpl 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 = std::enable_if_t, Group>::value>> explicit TupleImpl(OtherHeadT&& aHead, OtherTailT&&... aTail) : Base(std::forward(aTail)...), mHead(std::forward(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(std::move(Tail(aOther))), mHead(std::forward(Head(aOther))) {} // Assign from a tuple whose elements are convertible to the elements // of this tuple. template > TupleImpl& operator=(const TupleImpl& aOther) { typedef TupleImpl OtherT; Head(*this) = OtherT::Head(aOther); Tail(*this) = OtherT::Tail(aOther); return *this; } template > TupleImpl& operator=(TupleImpl&& aOther) { typedef TupleImpl OtherT; Head(*this) = std::move(OtherT::Head(aOther)); Tail(*this) = std::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) = std::move(Head(aOther)); Tail(*this) = std::move(Tail(aOther)); return *this; } bool operator==(const TupleImpl& aOther) const { return Head(*this) == Head(aOther) && Tail(*this) == Tail(aOther); } template void ForEach(const F& aFunc) const& { aFunc(Head(*this)); Tail(*this).ForEach(aFunc); } template void ForEach(const F& aFunc) & { aFunc(Head(*this)); Tail(*this).ForEach(aFunc); } template void ForEach(const F& aFunc) && { aFunc(std::move(Head(*this))); std::move(Tail(*this)).ForEach(aFunc); } 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 * std::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(tuple)'. */ template 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 , detail::Group>::value>> explicit Tuple(OtherHead&& aHead, OtherTail&&... aTail) : Impl(std::forward(aHead), std::forward(aTail)...) {} Tuple(const Tuple& aOther) : Impl(aOther) {} Tuple(Tuple&& aOther) : Impl(std::move(aOther)) {} template > Tuple& operator=(const Tuple& aOther) { static_cast(*this) = aOther; return *this; } template > Tuple& operator=(Tuple&& aOther) { static_cast(*this) = std::move(aOther); return *this; } Tuple& operator=(const Tuple& aOther) { static_cast(*this) = aOther; return *this; } Tuple& operator=(Tuple&& aOther) { static_cast(*this) = std::move(aOther); return *this; } bool operator==(const Tuple& aOther) const { return static_cast(*this) == static_cast(aOther); } }; /** * Specialization of Tuple for two elements. * This is created to support construction and assignment from a CompactPair or * std::pair. */ template class Tuple : 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 , detail::Group>::value>> explicit Tuple(AArg&& aA, BArg&& aB) : Impl(std::forward(aA), std::forward(aB)) {} Tuple(const Tuple& aOther) : Impl(aOther) {} Tuple(Tuple&& aOther) : Impl(std::move(aOther)) {} explicit Tuple(const CompactPair& aOther) : Impl(aOther.first(), aOther.second()) {} explicit Tuple(CompactPair&& aOther) : Impl(std::forward(aOther.first()), std::forward(aOther.second())) {} explicit Tuple(const std::pair& aOther) : Impl(aOther.first, aOther.second) {} explicit Tuple(std::pair&& aOther) : Impl(std::forward(aOther.first), std::forward(aOther.second)) {} template Tuple& operator=(const Tuple& aOther) { static_cast(*this) = aOther; return *this; } template Tuple& operator=(Tuple&& aOther) { static_cast(*this) = std::move(aOther); return *this; } Tuple& operator=(const Tuple& aOther) { static_cast(*this) = aOther; return *this; } Tuple& operator=(Tuple&& aOther) { static_cast(*this) = std::move(aOther); return *this; } template Tuple& operator=(const CompactPair& aOther) { Impl::Head(*this) = aOther.first(); Impl::Tail(*this).Head(*this) = aOther.second(); return *this; } template Tuple& operator=(CompactPair&& aOther) { Impl::Head(*this) = std::forward(aOther.first()); Impl::Tail(*this).Head(*this) = std::forward(aOther.second()); return *this; } template Tuple& operator=(const std::pair& aOther) { Impl::Head(*this) = aOther.first; Impl::Tail(*this).Head(*this) = aOther.second; return *this; } template Tuple& operator=(std::pair&& aOther) { Impl::Head(*this) = std::forward(aOther.first); Impl::Tail(*this).Head(*this) = std::forward(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(tuple). * These functions take a TupleImpl, 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 auto TupleGetHelper(TupleImpl& aTuple) -> decltype(TupleImpl::Head(aTuple)) { return TupleImpl::Head(aTuple); } // Non-const reference version. template auto TupleGetHelper(const TupleImpl& aTuple) -> decltype(TupleImpl::Head(aTuple)) { return TupleImpl::Head(aTuple); } } // namespace detail /** * Index-based access to an element of a tuple. * The syntax is Get(tuple). The index is zero-based. * * Example: * * Tuple t; * ... * float f = Get<1>(t); */ // Non-const reference version. template auto Get(Tuple& aTuple) -> decltype(detail::TupleGetHelper(aTuple)) { return detail::TupleGetHelper(aTuple); } // Const reference version. template auto Get(const Tuple& aTuple) -> decltype(detail::TupleGetHelper(aTuple)) { return detail::TupleGetHelper(aTuple); } // Rvalue reference version. template auto Get(Tuple&& aTuple) -> decltype(std::move(mozilla::Get(aTuple))) { // We need a 'mozilla::' qualification here to avoid // name lookup only finding the current function. return std::move(mozilla::Get(aTuple)); } /** * Helpers which call a function for each member of the tuple in turn. This will * typically be used with a lambda function with an `auto&` argument: * * Tuple> tuple{a, b, c}; * * ForEach(tuple, [](auto& aElem) { * aElem = nullptr; * }); */ template inline void ForEach(const Tuple<>& aTuple, const F& aFunc) {} template inline void ForEach(Tuple<>& aTuple, const F& aFunc) {} template void ForEach(const Tuple& aTuple, const F& aFunc) { aTuple.ForEach(aFunc); } template void ForEach(Tuple& aTuple, const F& aFunc) { aTuple.ForEach(aFunc); } template void ForEach(Tuple&& aTuple, const F& aFunc) { std::forward>(aTuple).ForEach(aFunc); } /** * 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 */ template inline Tuple...> MakeTuple(Elements&&... aElements) { return Tuple...>(std::forward(aElements)...); } /** * A helper placholder to allow ignoring specific elements during Tie unpacking. * Can be used with any type and any number of elements in a call to Tie. * * Usage of Ignore with Tie is equivalent to using std::ignore with * std::tie. * * Example: * * int i; * float f; * char c; * Tie(i, Ignore, f, c, Ignore) = FunctionThatReturnsATuple(); */ constexpr detail::IgnoreImpl Ignore; /** * 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 inline Tuple Tie(Elements&... aVariables) { return Tuple(aVariables...); } } // namespace mozilla #endif /* mozilla_Tuple_h */