586a6549117420e0b661062fe6bfe824e8984af2
[folly.git] / folly / experimental / test / TupleOpsTest.cpp
1 /*
2  * Copyright 2017 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 <folly/portability/GTest.h>
21
22 #include <glog/logging.h>
23
24 namespace folly { namespace test {
25
26 TEST(TupleOps, Copiable) {
27   auto t = std::make_tuple(10, std::string("hello"), 30);
28
29   EXPECT_EQ(10, std::get<0>(t));
30   auto t1 = tupleRange<1>(t);
31   EXPECT_EQ("hello", std::get<0>(t1));
32   EXPECT_EQ(2, std::tuple_size<decltype(t1)>::value);
33   auto t2 = tupleRange<1, 1>(t);
34   EXPECT_EQ(1, std::tuple_size<decltype(t2)>::value);
35   EXPECT_EQ("hello", std::get<0>(t2));
36   EXPECT_EQ(30, std::get<0>(tupleRange<1>(tupleRange<1>(t))));
37
38   EXPECT_TRUE(t == tuplePrepend(std::get<0>(t), tupleRange<1>(t)));
39 }
40
41 class MovableInt {
42  public:
43   explicit MovableInt(int value) : value_(value) { }
44   int value() const { return value_; }
45
46   MovableInt(MovableInt&&) = default;
47   MovableInt& operator=(MovableInt&&) = default;
48   MovableInt(const MovableInt&) = delete;
49   MovableInt& operator=(const MovableInt&) = delete;
50
51  private:
52   int value_;
53 };
54
55 bool operator==(const MovableInt& a, const MovableInt& b) {
56   return a.value() == b.value();
57 }
58
59 TEST(TupleOps, Movable) {
60   auto t1 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
61   auto t2 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
62   auto t3 = std::make_tuple(MovableInt(10), std::string("hello"), 30);
63
64   auto t1car = std::get<0>(std::move(t1));
65   auto t2cdr = tupleRange<1>(std::move(t2));
66
67   EXPECT_TRUE(t3 == tuplePrepend(std::move(t1car), std::move(t2cdr)));
68 }
69
70 // Given a tuple of As, convert to a tuple of Bs (of the same size)
71 // by calling folly::to on matching types.
72 //
73 // There are two example implementation: tupleTo (using tail recursion), which
74 // may create a lot of intermediate tuples, and tupleTo2, using
75 // TemplateTupleRange directly (below).
76 template <class U, class T>
77 U tupleTo(const T& input);
78
79 template <class U, class T> struct TupleTo;
80
81 // Base case: empty typle -> empty tuple
82 template <>
83 struct TupleTo<std::tuple<>, std::tuple<>> {
84   static std::tuple<> convert(const std::tuple<>& /* input */) {
85     return std::make_tuple();
86   }
87 };
88
89 // Induction case: split off head element and convert it, then call tupleTo on
90 // the tail.
91 template <class U, class... Us, class T>
92 struct TupleTo<
93   std::tuple<U, Us...>,
94   T> {
95   static std::tuple<U, Us...> convert(const T& input) {
96     return tuplePrepend(
97         folly::to<U>(std::get<0>(input)),
98         tupleTo<std::tuple<Us...>>(tupleRange<1>(input)));
99   }
100 };
101
102 template <class U, class T>
103 U tupleTo(const T& input) {
104   return TupleTo<U, T>::convert(input);
105 }
106
107 template <class S> struct TupleTo2;
108
109 // Destructure all indexes into Ns... and use parameter pack expansion
110 // to repeat the conversion for each individual element, then wrap
111 // all results with make_tuple.
112 template <std::size_t... Ns>
113 struct TupleTo2<TemplateSeq<std::size_t, Ns...>> {
114   template <class U, class T>
115   static U convert(const T& input) {
116     return std::make_tuple(
117         folly::to<typename std::tuple_element<Ns, U>::type>(
118             std::get<Ns>(input))...);
119   }
120 };
121
122 template <
123     class U,
124     class T,
125     class Seq = typename TemplateTupleRange<U>::type,
126     class Enable = typename std::enable_if<
127         (std::tuple_size<U>::value == std::tuple_size<T>::value)>::type>
128 U tupleTo2(const T& input) {
129   return TupleTo2<Seq>::template convert<U>(input);
130 }
131
132 #define CHECK_TUPLE_TO(converter) \
133   do { \
134     auto src = std::make_tuple(42, "50", 10); \
135     auto dest = converter<std::tuple<std::string, int, int>>(src); \
136     EXPECT_EQ("42", std::get<0>(dest)); \
137     EXPECT_EQ(50, std::get<1>(dest)); \
138     EXPECT_EQ(10, std::get<2>(dest)); \
139   } while (false)
140
141 TEST(TupleOps, TupleTo) {
142   CHECK_TUPLE_TO(tupleTo);
143 }
144
145 TEST(TupleOps, TupleTo2) {
146   CHECK_TUPLE_TO(tupleTo2);
147 }
148
149 #undef CHECK_TUPLE_TO
150
151 }}  // namespaces