--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FOLLY_TUPLEOPS_H_
+#define FOLLY_TUPLEOPS_H_
+
+#include <limits>
+#include <tuple>
+#include <type_traits>
+
+// tupleRange<start, n>(tuple): select n elements starting at index start
+// in the given tuple
+// tupleRange<start>(tuple): select all elements starting at index start
+// until the end of the given tuple
+// tuplePrepend(x, tuple): return a tuple obtained by prepending x to the
+// given tuple.
+//
+// In Lisp lingo, std::get<0> is car, tupleRange<1> is cdr, and tuplePrepend
+// is cons.
+
+namespace folly {
+
+// TemplateSeq<T, ...> is a type parametrized by sizeof...(Xs) values of type
+// T. Used to destructure the values into a template parameter pack;
+// see the example in TupleSelect, below.
+template <class T, T... xs>
+struct TemplateSeq {
+ template <T x>
+ using Prepend = TemplateSeq<T, x, xs...>;
+};
+
+// TemplateRange<T, start, n>::type is
+// TemplateSeq<T, start+1, start+2, ..., start+n-1>
+template <class T, T start, T n, class Enable=void> struct TemplateRange;
+
+template <class T, T start, T n>
+struct TemplateRange<
+ T, start, n,
+ typename std::enable_if<(n > 0)>::type> {
+ using type =
+ typename TemplateRange<T, start+1, n-1>::type::template Prepend<start>;
+};
+
+template <class T, T start, T n>
+struct TemplateRange<
+ T, start, n,
+ typename std::enable_if<(n <= 0)>::type> {
+ using type = TemplateSeq<T>;
+};
+
+// Similar to TemplateRange, given a tuple T,
+// TemplateTupleRange<T, start, n>::type is
+// TemplateSeq<size_t, start, start+1, ..., start+k-1>
+// where k = min(tuple_size<T>::value - start, n)
+// (that is, it's a TemplateSeq of at most n elements, but won't extend
+// past the end of the given tuple)
+template <class T,
+ std::size_t start = 0,
+ std::size_t n = std::numeric_limits<std::size_t>::max(),
+ std::size_t size =
+ std::tuple_size<typename std::remove_reference<T>::type>::value,
+ class Enable = typename std::enable_if<(start <= size)>::type>
+struct TemplateTupleRange {
+ using type = typename TemplateRange<
+ std::size_t,
+ start,
+ (n <= size - start ? n : size - start)>::type;
+};
+
+namespace detail {
+
+// Helper class to select a subset of a tuple
+template <class S> struct TupleSelect;
+template <std::size_t... Ns>
+struct TupleSelect<TemplateSeq<std::size_t, Ns...>> {
+ template <class T>
+ static auto select(T&& v)
+ -> decltype(std::make_tuple(std::get<Ns>(std::forward<T>(v))...)) {
+ return std::make_tuple(std::get<Ns>(std::forward<T>(v))...);
+ }
+};
+
+} // namespace detail
+
+// Return a tuple consisting of the elements at a range of indices.
+//
+// Use as tupleRange<start, n>(t) to return a tuple of (at most) n
+// elements starting at index start in tuple t.
+// If only start is specified (tupleRange<start>(t)), returns all elements
+// starting at index start until the end of the tuple t.
+// Won't compile if start > size of t.
+// Will return fewer elements (size - start) if start + n > size of t.
+template <
+ std::size_t start = 0,
+ std::size_t n = std::numeric_limits<std::size_t>::max(),
+ class T,
+ class Seq = typename TemplateTupleRange<T, start, n>::type>
+auto tupleRange(T&& v)
+-> decltype(detail::TupleSelect<Seq>::select(std::forward<T>(v))) {
+ return detail::TupleSelect<Seq>::select(std::forward<T>(v));
+}
+
+// Return a tuple obtained by prepending car to the tuple cdr.
+template <class T, class U>
+auto tuplePrepend(T&& car, U&& cdr)
+-> decltype(std::tuple_cat(std::make_tuple(std::forward<T>(car)),
+ std::forward<U>(cdr))) {
+ return std::tuple_cat(std::make_tuple(std::forward<T>(car)),
+ std::forward<U>(cdr));
+}
+
+} // namespaces
+
+#endif /* FOLLY_TUPLEOPS_H_ */
--- /dev/null
+/*
+ * Copyright 2015 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/experimental/TupleOps.h>
+
+#include <folly/Conv.h>
+#include <glog/logging.h>
+#include <gtest/gtest.h>
+
+namespace folly { namespace test {
+
+TEST(TupleOps, Copiable) {
+ auto t = std::make_tuple(10, std::string("hello"), 30);
+
+ EXPECT_EQ(10, std::get<0>(t));
+ auto t1 = tupleRange<1>(t);
+ EXPECT_EQ("hello", std::get<0>(t1));
+ EXPECT_EQ(2, std::tuple_size<decltype(t1)>::value);
+ auto t2 = tupleRange<1, 1>(t);
+ EXPECT_EQ(1, std::tuple_size<decltype(t2)>::value);
+ EXPECT_EQ("hello", std::get<0>(t2));
+ EXPECT_EQ(30, std::get<0>(tupleRange<1>(tupleRange<1>(t))));
+
+ EXPECT_TRUE(t == tuplePrepend(std::get<0>(t), tupleRange<1>(t)));
+}
+
+class MovableInt {
+ public:
+ explicit MovableInt(int value) : value_(value) { }
+ int value() const { return value_; }
+
+ MovableInt(MovableInt&&) = default;
+ MovableInt& operator=(MovableInt&&) = default;
+ MovableInt(const MovableInt&) = delete;
+ MovableInt& operator=(const MovableInt&) = delete;
+
+ private:
+ int value_;
+};
+
+bool operator==(const MovableInt& a, const MovableInt& b) {
+ return a.value() == b.value();
+}
+
+TEST(TupleOps, Movable) {
+ auto t1 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
+ auto t2 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
+ auto t3 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
+
+ auto t1car = std::get<0>(std::move(t1));
+ auto t2cdr = tupleRange<1>(std::move(t2));
+
+ EXPECT_TRUE(t3 == tuplePrepend(std::move(t1car), std::move(t2cdr)));
+}
+
+// Given a tuple of As, convert to a tuple of Bs (of the same size)
+// by calling folly::to on matching types.
+//
+// There are two example implementation: tupleTo (using tail recursion), which
+// may create a lot of intermediate tuples, and tupleTo2, using
+// TemplateTupleRange directly (below).
+template <class U, class T>
+U tupleTo(const T& input);
+
+template <class U, class T> struct TupleTo;
+
+// Base case: empty typle -> empty tuple
+template <>
+struct TupleTo<std::tuple<>, std::tuple<>> {
+ static std::tuple<> convert(const std::tuple<>& input) {
+ return std::make_tuple();
+ }
+};
+
+// Induction case: split off head element and convert it, then call tupleTo on
+// the tail.
+template <class U, class... Us, class T>
+struct TupleTo<
+ std::tuple<U, Us...>,
+ T> {
+ static std::tuple<U, Us...> convert(const T& input) {
+ return tuplePrepend(
+ folly::to<U>(std::get<0>(input)),
+ tupleTo<std::tuple<Us...>>(tupleRange<1>(input)));
+ }
+};
+
+template <class U, class T>
+U tupleTo(const T& input) {
+ return TupleTo<U, T>::convert(input);
+}
+
+template <class S> struct TupleTo2;
+
+// Destructure all indexes into Ns... and use parameter pack expansion
+// to repeat the conversion for each individual element, then wrap
+// all results with make_tuple.
+template <std::size_t... Ns>
+struct TupleTo2<TemplateSeq<std::size_t, Ns...>> {
+ template <class U, class T>
+ static U convert(const T& input) {
+ return std::make_tuple(
+ folly::to<typename std::tuple_element<Ns, U>::type>(
+ std::get<Ns>(input))...);
+ }
+};
+
+template <class U, class T,
+ class Seq = typename TemplateTupleRange<U>::type,
+ class Enable = typename std::enable_if<
+ (std::tuple_size<U>::value == std::tuple_size<T>::value)>::type>
+U tupleTo2(const T& input) {
+ return TupleTo2<Seq>::template convert<U>(input);
+}
+
+#define CHECK_TUPLE_TO(converter) \
+ do { \
+ auto src = std::make_tuple(42, "50", 10); \
+ auto dest = converter<std::tuple<std::string, int, int>>(src); \
+ EXPECT_EQ("42", std::get<0>(dest)); \
+ EXPECT_EQ(50, std::get<1>(dest)); \
+ EXPECT_EQ(10, std::get<2>(dest)); \
+ } while (false)
+
+TEST(TupleOps, TupleTo) {
+ CHECK_TUPLE_TO(tupleTo);
+}
+
+TEST(TupleOps, TupleTo2) {
+ CHECK_TUPLE_TO(tupleTo2);
+}
+
+#undef CHECK_TUPLE_TO
+
+}} // namespaces