Commit 63e28343 authored by Kenton Varda's avatar Kenton Varda

Extend Tuples to be able to contain references.

You need to use a different constructor function, `refTuple()`, to opt into references. Otherwise, it's too easy to construct a tuple of references by mistake.
parent e92382a4
...@@ -90,4 +90,23 @@ TEST(Tuple, Tuple) { ...@@ -90,4 +90,23 @@ TEST(Tuple, Tuple) {
EXPECT_EQ(0, (indexOfType<int, int>())); EXPECT_EQ(0, (indexOfType<int, int>()));
} }
TEST(Tuple, RefTuple) {
uint i = 123;
StringPtr s = "foo";
Tuple<uint&, StringPtr&, uint, StringPtr> t = refTuple(i, s, 321, "bar");
EXPECT_EQ(get<0>(t), 123);
EXPECT_EQ(get<1>(t), "foo");
EXPECT_EQ(get<2>(t), 321);
EXPECT_EQ(get<3>(t), "bar");
i = 456;
s = "baz";
EXPECT_EQ(get<0>(t), 456);
EXPECT_EQ(get<1>(t), "baz");
EXPECT_EQ(get<2>(t), 321);
EXPECT_EQ(get<3>(t), "bar");
}
} // namespace kj } // namespace kj
...@@ -100,12 +100,10 @@ struct TupleElement { ...@@ -100,12 +100,10 @@ struct TupleElement {
template <uint index, typename T> template <uint index, typename T>
struct TupleElement<index, T&> { struct TupleElement<index, T&> {
// If tuples contained references, one of the following would have to be true: // A tuple containing references can be constucted using refTuple().
// - `auto x = tuple(y, z)` would cause x to be a tuple of references to y and z, which is
// probably not what you expected. T& value;
// - `Tuple<Foo&, Bar&> x = tuple(a, b)` would not work, because `tuple()` returned constexpr inline TupleElement(T& value): value(value) {}
// Tuple<Foo, Bar>.
static_assert(sizeof(T*) == 0, "Sorry, tuples cannot contain references.");
}; };
template <uint index, typename... T> template <uint index, typename... T>
...@@ -136,7 +134,7 @@ struct TupleImpl<Indexes<indexes...>, Types...> ...@@ -136,7 +134,7 @@ struct TupleImpl<Indexes<indexes...>, Types...>
template <typename... U> template <typename... U>
constexpr inline TupleImpl(Tuple<U...>&& other) constexpr inline TupleImpl(Tuple<U...>&& other)
: TupleElement<indexes, Types>(kj::mv(getImpl<indexes>(other)))... {} : TupleElement<indexes, Types>(kj::fwd<U>(getImpl<indexes>(other)))... {}
template <typename... U> template <typename... U>
constexpr inline TupleImpl(Tuple<U...>& other) constexpr inline TupleImpl(Tuple<U...>& other)
: TupleElement<indexes, Types>(getImpl<indexes>(other))... {} : TupleElement<indexes, Types>(getImpl<indexes>(other))... {}
...@@ -146,6 +144,7 @@ struct TupleImpl<Indexes<indexes...>, Types...> ...@@ -146,6 +144,7 @@ struct TupleImpl<Indexes<indexes...>, Types...>
}; };
struct MakeTupleFunc; struct MakeTupleFunc;
struct MakeRefTupleFunc;
template <typename... T> template <typename... T>
class Tuple { class Tuple {
...@@ -172,6 +171,7 @@ private: ...@@ -172,6 +171,7 @@ private:
template <size_t index, typename... U> template <size_t index, typename... U>
friend inline const TypeByIndex<index, U...>& getImpl(const Tuple<U...>& tuple); friend inline const TypeByIndex<index, U...>& getImpl(const Tuple<U...>& tuple);
friend struct MakeTupleFunc; friend struct MakeTupleFunc;
friend struct MakeRefTupleFunc;
}; };
template <> template <>
...@@ -313,6 +313,17 @@ struct MakeTupleFunc { ...@@ -313,6 +313,17 @@ struct MakeTupleFunc {
} }
}; };
struct MakeRefTupleFunc {
template <typename... Params>
Tuple<Params...> operator()(Params&&... params) {
return Tuple<Params...>(kj::fwd<Params>(params)...);
}
template <typename Param>
Param operator()(Param&& param) {
return kj::fwd<Param>(param);
}
};
} // namespace _ (private) } // namespace _ (private)
template <typename... T> struct Tuple_ { typedef _::Tuple<T...> Type; }; template <typename... T> struct Tuple_ { typedef _::Tuple<T...> Type; };
...@@ -335,6 +346,14 @@ inline auto tuple(Params&&... params) ...@@ -335,6 +346,14 @@ inline auto tuple(Params&&... params)
return _::expandAndApply(_::MakeTupleFunc(), kj::fwd<Params>(params)...); return _::expandAndApply(_::MakeTupleFunc(), kj::fwd<Params>(params)...);
} }
template <typename... Params>
inline auto refTuple(Params&&... params)
-> decltype(_::expandAndApply(_::MakeRefTupleFunc(), kj::fwd<Params>(params)...)) {
// Like tuple(), but if the params include lvalue references, they will be captured as
// references. rvalue references will still be captured as whole values (moved).
return _::expandAndApply(_::MakeRefTupleFunc(), kj::fwd<Params>(params)...);
}
template <size_t index, typename Tuple> template <size_t index, typename Tuple>
inline auto get(Tuple&& tuple) -> decltype(_::getImpl<index>(kj::fwd<Tuple>(tuple))) { inline auto get(Tuple&& tuple) -> decltype(_::getImpl<index>(kj::fwd<Tuple>(tuple))) {
// Unpack and return the tuple element at the given index. The index is specified as a template // Unpack and return the tuple element at the given index. The index is specified as a template
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment