Skip to content

Commit b280689

Browse files
Merge pull request #3586 from diffblue/tas/range_move
Add support for containers of move only typed values to range.
2 parents 8065b10 + bbc2016 commit b280689

File tree

2 files changed

+164
-18
lines changed

2 files changed

+164
-18
lines changed

src/util/range.h

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class map_iteratort
4848
PRECONDITION(underlying != underlying_end);
4949
++underlying;
5050
if(underlying != underlying_end)
51-
current = util_make_unique<outputt>((*f)(*underlying));
51+
current = std::make_shared<outputt>((*f)(*underlying));
5252
return *this;
5353
}
5454

@@ -60,6 +60,16 @@ class map_iteratort
6060
return tmp;
6161
}
6262

63+
value_type &operator*()
64+
{
65+
return *current.get();
66+
}
67+
68+
value_type *operator->()
69+
{
70+
return &(*current.get());
71+
}
72+
6373
const value_type &operator*() const
6474
{
6575
return *current.get();
@@ -80,16 +90,7 @@ class map_iteratort
8090
f(std::move(f))
8191
{
8292
if(this->underlying != this->underlying_end)
83-
current = util_make_unique<outputt>((*this->f)(*this->underlying));
84-
}
85-
86-
map_iteratort(const map_iteratort &other)
87-
: underlying(other.underlying),
88-
underlying_end(other.underlying_end),
89-
f(other.f)
90-
{
91-
if(other.current != nullptr)
92-
current = util_make_unique<outputt>(*other.current.get());
93+
current = std::make_shared<outputt>((*this->f)(*this->underlying));
9394
}
9495

9596
private:
@@ -101,7 +102,7 @@ class map_iteratort
101102

102103
/// Stores the result of \c f at the current position of the iterator.
103104
/// Equals nullptr if the iterator reached \c underlying_end.
104-
std::unique_ptr<outputt> current = nullptr;
105+
std::shared_ptr<outputt> current = nullptr;
105106
};
106107

107108
/// Iterator which only stops at elements for which some given function \c f is
@@ -142,6 +143,16 @@ class filter_iteratort
142143
return tmp;
143144
}
144145

146+
value_type &operator*()
147+
{
148+
return *underlying;
149+
}
150+
151+
value_type *operator->()
152+
{
153+
return &(*underlying);
154+
}
155+
145156
const value_type &operator*() const
146157
{
147158
return *underlying;
@@ -234,6 +245,20 @@ struct concat_iteratort
234245
return tmp;
235246
}
236247

248+
value_type &operator*()
249+
{
250+
if(first_begin == first_end)
251+
return *second_begin;
252+
return *first_begin;
253+
}
254+
255+
value_type *operator->()
256+
{
257+
if(first_begin == first_end)
258+
return &(*second_begin);
259+
return &(*first_begin);
260+
}
261+
237262
const value_type &operator*() const
238263
{
239264
if(first_begin == first_end)
@@ -302,7 +327,11 @@ struct ranget final
302327
}
303328

304329
/// The type of elements contained in the resulting range is deduced from the
305-
/// return type of \p `f`.
330+
/// return type of `f`.
331+
/// Please note that the parameter to `f` must be a const reference. This is
332+
/// a limitation of the current implementation. This means that you can't move
333+
/// a value through `f`. `f` may take a move-only typed parameter by const
334+
/// reference. 'f' may also construct and return a move-only typed value.
306335
template <typename functiont>
307336
auto map(functiont &&f) -> ranget<map_iteratort<
308337
iteratort,
@@ -357,12 +386,11 @@ ranget<iteratort> make_range(iteratort begin, iteratort end)
357386
return ranget<iteratort>(begin, end);
358387
}
359388

360-
template <
361-
typename containert,
362-
typename iteratort = typename containert::const_iterator>
363-
ranget<iteratort> make_range(const containert &container)
389+
template <typename containert>
390+
auto make_range(containert &container) -> ranget<decltype(container.begin())>
364391
{
365-
return ranget<iteratort>(container.begin(), container.end());
392+
return ranget<decltype(container.begin())>(
393+
container.begin(), container.end());
366394
}
367395

368396
#endif // CPROVER_UTIL_RANGE_H

unit/util/range.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,121 @@ SCENARIO("range tests", "[core][util][range]")
6565
}
6666
}
6767
}
68+
69+
class move_onlyt
70+
{
71+
public:
72+
move_onlyt(move_onlyt &&) = default;
73+
move_onlyt &operator=(move_onlyt &&) = default;
74+
move_onlyt(const move_onlyt &) = delete;
75+
move_onlyt &operator=(const move_onlyt &) = delete;
76+
77+
explicit move_onlyt(int value) : value{value} {};
78+
int value = 0;
79+
};
80+
81+
bool is_odd(const move_onlyt &move_only)
82+
{
83+
return move_only.value % 2 != 0;
84+
}
85+
86+
const auto add = [](int left) {
87+
return [=](const move_onlyt &right) { return left + right.value; };
88+
};
89+
90+
SCENARIO(
91+
"Range tests, with collections of move only typed values.",
92+
"[core][util][range]")
93+
{
94+
GIVEN("A vector of move only typed values.")
95+
{
96+
std::vector<move_onlyt> input;
97+
for(int i = 1; i <= 10; ++i)
98+
input.emplace_back(i);
99+
THEN("Values from a range of made from the vector can be moved.")
100+
{
101+
auto input_range = make_range(input);
102+
move_onlyt destination{std::move(*input_range.begin())};
103+
REQUIRE(destination.value == 1);
104+
}
105+
THEN("A range of made from the vector can be filtered.")
106+
{
107+
auto odds_filter = make_range(input).filter(is_odd);
108+
std::size_t total = 0;
109+
for(const auto &move_only : odds_filter)
110+
total += 1;
111+
REQUIRE(total == 5);
112+
auto iterator = odds_filter.begin();
113+
REQUIRE((iterator++)->value == 1);
114+
REQUIRE((iterator++)->value == 3);
115+
REQUIRE((iterator++)->value == 5);
116+
REQUIRE((iterator++)->value == 7);
117+
REQUIRE((iterator++)->value == 9);
118+
}
119+
THEN("Values from a filtered range made from the vector can be moved.")
120+
{
121+
std::vector<move_onlyt> odds;
122+
for(move_onlyt &odd : make_range(input).filter(is_odd))
123+
odds.emplace_back(std::move(odd));
124+
125+
REQUIRE(odds.size() == 5);
126+
REQUIRE(odds[0].value == 1);
127+
REQUIRE(odds[1].value == 3);
128+
REQUIRE(odds[2].value == 5);
129+
REQUIRE(odds[3].value == 7);
130+
REQUIRE(odds[4].value == 9);
131+
}
132+
THEN("Map can be applied to a range of move only typed values.")
133+
{
134+
std::vector<int> results;
135+
for(int result : make_range(input).map(add(1)))
136+
results.push_back(result);
137+
138+
const std::vector<int> expected_results{2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
139+
REQUIRE(results == expected_results);
140+
}
141+
}
142+
GIVEN("Two vectors containing move only types values.")
143+
{
144+
std::vector<move_onlyt> input1;
145+
for(int i = 1; i <= 3; ++i)
146+
input1.emplace_back(i);
147+
std::vector<move_onlyt> input2;
148+
for(int i = 7; i <= 9; ++i)
149+
input2.emplace_back(i);
150+
151+
THEN("Values from concatenated ranges made from the vector can be moved.")
152+
{
153+
std::vector<move_onlyt> both_inputs;
154+
for(move_onlyt &input : make_range(input1).concat(make_range(input2)))
155+
both_inputs.emplace_back(std::move(input));
156+
157+
REQUIRE(both_inputs.size() == 6);
158+
REQUIRE(both_inputs[0].value == 1);
159+
REQUIRE(both_inputs[1].value == 2);
160+
REQUIRE(both_inputs[2].value == 3);
161+
REQUIRE(both_inputs[3].value == 7);
162+
REQUIRE(both_inputs[4].value == 8);
163+
REQUIRE(both_inputs[5].value == 9);
164+
}
165+
}
166+
GIVEN("A const vector of ints.")
167+
{
168+
const std::vector<int> input{1, 2, 3, 4, 5};
169+
170+
THEN("The vector can be mapped into a range of move-only types")
171+
{
172+
std::vector<move_onlyt> results;
173+
const auto make_move_only = [](int i) { return move_onlyt{i}; };
174+
for(auto &incremented : make_range(input).map(make_move_only))
175+
results.emplace_back(std::move(incremented));
176+
177+
REQUIRE(results.size() == 5);
178+
REQUIRE(results[0].value == 1);
179+
REQUIRE(results[1].value == 2);
180+
REQUIRE(results[2].value == 3);
181+
REQUIRE(results[3].value == 4);
182+
REQUIRE(results[4].value == 5);
183+
}
184+
}
185+
}

0 commit comments

Comments
 (0)