1 // Range v3 library
2 //
3 //  Copyright Eric Niebler 2014-present
4 //
5 //  Use, modification and distribution is subject to the
6 //  Boost Software License, Version 1.0. (See accompanying
7 //  file LICENSE_1_0.txt or copy at
8 //  http://www.boost.org/LICENSE_1_0.txt)
9 //
10 // Project home: https://github.com/ericniebler/range-v3
11 
12 #include <string>
13 #include <vector>
14 #include <iterator>
15 #include <functional>
16 #include <range/v3/core.hpp>
17 #include <range/v3/algorithm/move.hpp>
18 #include <range/v3/functional/overload.hpp>
19 #include <range/v3/iterator/insert_iterators.hpp>
20 #include <range/v3/utility/copy.hpp>
21 #include <range/v3/view/transform.hpp>
22 #include <range/v3/view/counted.hpp>
23 #include <range/v3/view/reverse.hpp>
24 #include <range/v3/view/span.hpp>
25 #include <range/v3/view/zip.hpp>
26 #include "../simple_test.hpp"
27 #include "../test_utils.hpp"
28 
29 struct is_odd
30 {
operator ()is_odd31     bool operator()(int i) const
32     {
33         return (i % 2) == 1;
34     }
35 };
36 
37 // https://github.com/ericniebler/range-v3/issues/996
bug_996()38 void bug_996()
39 {
40     std::vector<int> buff(12, -1);
41     ::ranges::span<int> sp(buff.data(), 12);
42 
43     auto x = ::ranges::views::transform(sp, [](int a) { return a > 3 ? a : 42; });
44     auto y = ::ranges::views::transform(x, sp, [](int a, int b) { return a + b; });
45     auto rng = ::ranges::views::transform(y, [](int a) { return a + 1; });
46     (void)rng;
47 }
48 
main()49 int main()
50 {
51     using namespace ranges;
52 
53     int rgi[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
54     std::pair<int, int> rgp[] = {{1,1}, {2,2}, {3,3}, {4,4}, {5,5}, {6,6}, {7,7}, {8,8}, {9,9}, {10,10}};
55 
56     {
57         auto rng = rgi | views::transform(is_odd());
58         has_type<int &>(*begin(rgi));
59         has_type<bool>(*begin(rng));
60         CPP_assert(view_<decltype(rng)>);
61         CPP_assert(sized_range<decltype(rng)>);
62         CPP_assert(random_access_range<decltype(rng)>);
63         ::check_equal(rng, {true, false, true, false, true, false, true, false, true, false});
64     }
65 
66     {
67         auto rng2 = rgp | views::transform(&std::pair<int,int>::first);
68         has_type<int &>(*begin(rng2));
69         CPP_assert(same_as<range_value_t<decltype(rng2)>, int>);
70         CPP_assert(same_as<decltype(iter_move(begin(rng2))), int &&>);
71         CPP_assert(view_<decltype(rng2)>);
72         CPP_assert(common_range<decltype(rng2)>);
73         CPP_assert(sized_range<decltype(rng2)>);
74         CPP_assert(random_access_range<decltype(rng2)>);
75         ::check_equal(rng2, {1,2,3,4,5,6,7,8,9,10});
76         ::check_equal(rng2 | views::reverse, {10,9,8,7,6,5,4,3,2,1});
77         CHECK(&*begin(rng2) == &rgp[0].first);
78         CHECK(rng2.size() == 10u);
79     }
80 
81     {
82 
83         auto rng3 = views::counted(rgp, 10) | views::transform(&std::pair<int,int>::first);
84         has_type<int &>(*begin(rng3));
85         CPP_assert(view_<decltype(rng3)>);
86         CPP_assert(common_range<decltype(rng3)>);
87         CPP_assert(sized_range<decltype(rng3)>);
88         CPP_assert(random_access_range<decltype(rng3)>);
89         ::check_equal(rng3, {1,2,3,4,5,6,7,8,9,10});
90         CHECK(&*begin(rng3) == &rgp[0].first);
91         CHECK(rng3.size() == 10u);
92     }
93 
94     {
95         auto rng4 = views::counted(ForwardIterator<std::pair<int, int>*>{rgp}, 10)
96                         | views::transform(&std::pair<int,int>::first);
97         has_type<int &>(*begin(rng4));
98         CPP_assert(view_<decltype(rng4)>);
99         CPP_assert(!common_range<decltype(rng4)>);
100         CPP_assert(sized_range<decltype(rng4)>);
101         CPP_assert(forward_range<decltype(rng4)>);
102         CPP_assert(!bidirectional_range<decltype(rng4)>);
103         ::check_equal(rng4, {1,2,3,4,5,6,7,8,9,10});
104         CHECK(&*begin(rng4) == &rgp[0].first);
105         CHECK(rng4.size() == 10u);
106 
107         counted_iterator<ForwardIterator<std::pair<int, int>*>> i = begin(rng4).base();
108         (void)i;
109     }
110 
111     // Test transform with a mutable lambda
112     {
113         int cnt = 100;
114         auto mutable_rng = views::transform(rgi, [cnt](int) mutable { return cnt++;});
115         ::check_equal(mutable_rng, {100,101,102,103,104,105,106,107,108,109});
116         CHECK(cnt == 100);
117         CPP_assert(view_<decltype(mutable_rng)>);
118         CPP_assert(!view_<decltype(mutable_rng) const>);
119     }
120 
121     // Test iter_transform by transforming a zip view to select one element.
122     {
123         auto v0 = to<std::vector<MoveOnlyString>>({"a","b","c"});
124         auto v1 = to<std::vector<MoveOnlyString>>({"x","y","z"});
125 
126         auto rng1 = views::zip(v0, v1);
127         CPP_assert(random_access_range<decltype(rng1)>);
128 
129         std::vector<MoveOnlyString> res;
130         using R1 = decltype(rng1);
131         using I1 = iterator_t<R1>;
132         // Needlessly verbose -- a simple transform would do the same, but this
133         // is an interesting test.
134         auto proj = overload(
135             [](I1 i1) -> MoveOnlyString& {return (*i1).first;},
136             [](copy_tag, I1) -> MoveOnlyString {return {};},
137             [](move_tag, I1 i1) -> MoveOnlyString&& {return std::move((*i1).first);}
138         );
139         auto rng2 = rng1 | views::iter_transform(proj);
140         move(rng2, ranges::back_inserter(res));
141         ::check_equal(res, {"a","b","c"});
142         ::check_equal(v0, {"","",""});
143         ::check_equal(v1, {"x","y","z"});
144         using R2 = decltype(rng2);
145         CPP_assert(same_as<range_value_t<R2>, MoveOnlyString>);
146         CPP_assert(same_as<range_reference_t<R2>, MoveOnlyString &>);
147         CPP_assert(same_as<range_rvalue_reference_t<R2>, MoveOnlyString &&>);
148     }
149 
150     // two range transform
151     {
152         auto v0 = to<std::vector<std::string>>({"a","b","c"});
153         auto v1 = to<std::vector<std::string>>({"x","y","z"});
154 
155         auto rng = views::transform(v0, v1, [](std::string& s0, std::string& s1){return std::tie(s0, s1);});
156         using R = decltype(rng);
157         CPP_assert(same_as<range_value_t<R>, std::tuple<std::string&, std::string&>>);
158         CPP_assert(same_as<range_reference_t<R>, std::tuple<std::string&, std::string&>>);
159         CPP_assert(same_as<range_rvalue_reference_t<R>, std::tuple<std::string&, std::string&>>);
160 
161         using T = std::tuple<std::string, std::string>;
162         ::check_equal(rng, {T{"a","x"}, T{"b","y"}, T{"c","z"}});
163     }
164 
165     // two range indirect transform
166     {
167         auto v0 = to<std::vector<std::string>>({"a","b","c"});
168         auto v1 = to<std::vector<std::string>>({"x","y","z"});
169         using I = std::vector<std::string>::iterator;
170 
171         auto fun = overload(
172             [](I i, I j)           { return std::tie(*i, *j); },
173             [](copy_tag, I, I)     { return std::tuple<std::string, std::string>{}; },
174             [](move_tag, I i, I j) { return common_tuple<std::string&&, std::string&&>{
175                 std::move(*i), std::move(*j)}; } );
176 
177         auto rng = views::iter_transform(v0, v1, fun);
178         using R = decltype(rng);
179         CPP_assert(same_as<range_value_t<R>, std::tuple<std::string, std::string>>);
180         CPP_assert(same_as<range_reference_t<R>, std::tuple<std::string&, std::string&>>);
181         CPP_assert(same_as<range_rvalue_reference_t<R>, common_tuple<std::string&&, std::string&&>>);
182 
183         using T = std::tuple<std::string, std::string>;
184         ::check_equal(rng, {T{"a","x"}, T{"b","y"}, T{"c","z"}});
185     }
186 
187     {
188         auto rng = debug_input_view<int const>{rgi} | views::transform(is_odd{});
189         ::check_equal(rng, {true, false, true, false, true, false, true, false, true, false});
190     }
191 
192     {
193         auto v0 = to<std::vector<std::string>>({"a","b","c"});
194         auto v1 = to<std::vector<std::string>>({"x","y","z"});
195 
196         auto r0 = debug_input_view<std::string>{v0.data(), distance(v0)};
197         auto r1 = debug_input_view<std::string>{v1.data(), distance(v1)};
198         auto rng = views::transform(std::move(r0), std::move(r1),
199             [](std::string &s0, std::string &s1){ return std::tie(s0, s1); });
200         using R = decltype(rng);
201         CPP_assert(same_as<range_value_t<R>, std::tuple<std::string &, std::string &>>);
202         CPP_assert(same_as<range_reference_t<R>, std::tuple<std::string &, std::string &>>);
203         CPP_assert(same_as<range_rvalue_reference_t<R>, std::tuple<std::string &, std::string &>>);
204 
205         using T = std::tuple<std::string, std::string>;
206         ::check_equal(rng, {T{"a","x"}, T{"b","y"}, T{"c","z"}});
207     }
208 
209     {
210 #if RANGES_CXX_DEDUCTION_GUIDES >= RANGES_CXX_DEDUCTION_GUIDES_17
211 #if defined(__clang__) && __clang_major__ < 6
212         // Workaround https://bugs.llvm.org/show_bug.cgi?id=33314
213         RANGES_DIAGNOSTIC_PUSH
214         RANGES_DIAGNOSTIC_IGNORE_UNDEFINED_FUNC_TEMPLATE
215 #endif
216         std::vector<int> vi = {1, 2, 3};
217         ranges::transform_view times_ten{vi, [](int i) { return i * 10; }};
218         ::check_equal(times_ten, {10, 20, 30});
219 #if defined(__clang__) && __clang_major__ < 6
220         RANGES_DIAGNOSTIC_POP
221 #endif // clang bug workaround
222 #endif // use deduction guides
223     }
224 
225     return test_result();
226 }
227