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