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
24namespace folly {
25namespace test {
26
27TEST(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
42class 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
58bool operator==(const MovableInt& a, const MovableInt& b) {
59 return a.value() == b.value();
60}
61
62TEST(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).
79template <class U, class T>
80U tupleTo(const T& input);
81
82template <class U, class T>
83struct TupleTo;
84
85// Base case: empty typle -> empty tuple
86template <>
87struct 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.
95template <class U, class... Us, class T>
96struct 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
104template <class U, class T>
105U tupleTo(const T& input) {
106 return TupleTo<U, T>::convert(input);
107}
108
109template <class S>
110struct 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.
115template <std::size_t... Ns>
116struct 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
124template <
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>
130U 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
143TEST(TupleOps, TupleTo) {
144 CHECK_TUPLE_TO(tupleTo);
145}
146
147TEST(TupleOps, TupleTo2) {
148 CHECK_TUPLE_TO(tupleTo2);
149}
150
151#undef CHECK_TUPLE_TO
152
153} // namespace test
154} // namespace folly
155