24df7b21de7853109205fe28091d9aa0f0888ce1
[folly.git] / folly / experimental / test / TupleOpsTest.cpp
1 /*
2  * Copyright 2016 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/experimental/TupleOps.h>
18
19 #include <folly/Conv.h>
20 #include <glog/logging.h>
21 #include <gtest/gtest.h>
22
23 namespace folly { namespace test {
24
25 TEST(TupleOps, Copiable) {
26   auto t = std::make_tuple(10, std::string("hello"), 30);
27
28   EXPECT_EQ(10, std::get<0>(t));
29   auto t1 = tupleRange<1>(t);
30   EXPECT_EQ("hello", std::get<0>(t1));
31   EXPECT_EQ(2, std::tuple_size<decltype(t1)>::value);
32   auto t2 = tupleRange<1, 1>(t);
33   EXPECT_EQ(1, std::tuple_size<decltype(t2)>::value);
34   EXPECT_EQ("hello", std::get<0>(t2));
35   EXPECT_EQ(30, std::get<0>(tupleRange<1>(tupleRange<1>(t))));
36
37   EXPECT_TRUE(t == tuplePrepend(std::get<0>(t), tupleRange<1>(t)));
38 }
39
40 class MovableInt {
41  public:
42   explicit MovableInt(int value) : value_(value) { }
43   int value() const { return value_; }
44
45   MovableInt(MovableInt&&) = default;
46   MovableInt& operator=(MovableInt&&) = default;
47   MovableInt(const MovableInt&) = delete;
48   MovableInt& operator=(const MovableInt&) = delete;
49
50  private:
51   int value_;
52 };
53
54 bool operator==(const MovableInt& a, const MovableInt& b) {
55   return a.value() == b.value();
56 }
57
58 TEST(TupleOps, Movable) {
59   auto t1 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
60   auto t2 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
61   auto t3 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
62
63   auto t1car = std::get<0>(std::move(t1));
64   auto t2cdr = tupleRange<1>(std::move(t2));
65
66   EXPECT_TRUE(t3 == tuplePrepend(std::move(t1car), std::move(t2cdr)));
67 }
68
69 // Given a tuple of As, convert to a tuple of Bs (of the same size)
70 // by calling folly::to on matching types.
71 //
72 // There are two example implementation: tupleTo (using tail recursion), which
73 // may create a lot of intermediate tuples, and tupleTo2, using
74 // TemplateTupleRange directly (below).
75 template <class U, class T>
76 U tupleTo(const T& input);
77
78 template <class U, class T> struct TupleTo;
79
80 // Base case: empty typle -> empty tuple
81 template <>
82 struct TupleTo<std::tuple<>, std::tuple<>> {
83   static std::tuple<> convert(const std::tuple<>& /* input */) {
84     return std::make_tuple();
85   }
86 };
87
88 // Induction case: split off head element and convert it, then call tupleTo on
89 // the tail.
90 template <class U, class... Us, class T>
91 struct TupleTo<
92   std::tuple<U, Us...>,
93   T> {
94   static std::tuple<U, Us...> convert(const T& input) {
95     return tuplePrepend(
96         folly::to<U>(std::get<0>(input)),
97         tupleTo<std::tuple<Us...>>(tupleRange<1>(input)));
98   }
99 };
100
101 template <class U, class T>
102 U tupleTo(const T& input) {
103   return TupleTo<U, T>::convert(input);
104 }
105
106 template <class S> struct TupleTo2;
107
108 // Destructure all indexes into Ns... and use parameter pack expansion
109 // to repeat the conversion for each individual element, then wrap
110 // all results with make_tuple.
111 template <std::size_t... Ns>
112 struct TupleTo2<TemplateSeq<std::size_t, Ns...>> {
113   template <class U, class T>
114   static U convert(const T& input) {
115     return std::make_tuple(
116         folly::to<typename std::tuple_element<Ns, U>::type>(
117             std::get<Ns>(input))...);
118   }
119 };
120
121 template <class U, class T,
122           class Seq = typename TemplateTupleRange<U>::type,
123           class Enable = typename std::enable_if<
124             (std::tuple_size<U>::value == std::tuple_size<T>::value)>::type>
125 U tupleTo2(const T& input) {
126   return TupleTo2<Seq>::template convert<U>(input);
127 }
128
129 #define CHECK_TUPLE_TO(converter) \
130   do { \
131     auto src = std::make_tuple(42, "50", 10); \
132     auto dest = converter<std::tuple<std::string, int, int>>(src); \
133     EXPECT_EQ("42", std::get<0>(dest)); \
134     EXPECT_EQ(50, std::get<1>(dest)); \
135     EXPECT_EQ(10, std::get<2>(dest)); \
136   } while (false)
137
138 TEST(TupleOps, TupleTo) {
139   CHECK_TUPLE_TO(tupleTo);
140 }
141
142 TEST(TupleOps, TupleTo2) {
143   CHECK_TUPLE_TO(tupleTo2);
144 }
145
146 #undef CHECK_TUPLE_TO
147
148 }}  // namespaces