Skip to content

Commit 5730633

Browse files
Merge pull request #3833 from thomasspriggs/tas/range_constness
Fix a selection of `const` related issues with `ranget`.
2 parents 5caa59f + a89ca18 commit 5730633

File tree

2 files changed

+104
-43
lines changed

2 files changed

+104
-43
lines changed

src/util/range.h

Lines changed: 13 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ class filter_iteratort
113113
public:
114114
using difference_type = typename iteratort::difference_type;
115115
using value_type = typename iteratort::value_type;
116-
using pointer = const value_type *;
117-
using reference = const value_type &;
116+
using pointer = typename iteratort::pointer;
117+
using reference = typename iteratort::reference;
118118
using iterator_category = std::forward_iterator_tag;
119119

120120
bool operator==(const filter_iteratort &other) const
@@ -143,22 +143,12 @@ class filter_iteratort
143143
return tmp;
144144
}
145145

146-
value_type &operator*()
146+
reference operator*() const
147147
{
148148
return *underlying;
149149
}
150150

151-
value_type *operator->()
152-
{
153-
return &(*underlying);
154-
}
155-
156-
const value_type &operator*() const
157-
{
158-
return *underlying;
159-
}
160-
161-
const value_type *operator->() const
151+
pointer operator->() const
162152
{
163153
return &(*underlying);
164154
}
@@ -208,8 +198,8 @@ struct concat_iteratort
208198
public:
209199
using difference_type = typename first_iteratort::difference_type;
210200
using value_type = typename first_iteratort::value_type;
211-
using pointer = const value_type *;
212-
using reference = const value_type &;
201+
using pointer = typename first_iteratort::pointer;
202+
using reference = typename first_iteratort::reference;
213203
using iterator_category = std::forward_iterator_tag;
214204

215205
static_assert(
@@ -245,28 +235,14 @@ struct concat_iteratort
245235
return tmp;
246236
}
247237

248-
value_type &operator*()
238+
reference operator*() const
249239
{
250240
if(first_begin == first_end)
251241
return *second_begin;
252242
return *first_begin;
253243
}
254244

255-
value_type *operator->()
256-
{
257-
if(first_begin == first_end)
258-
return &(*second_begin);
259-
return &(*first_begin);
260-
}
261-
262-
const value_type &operator*() const
263-
{
264-
if(first_begin == first_end)
265-
return *second_begin;
266-
return *first_begin;
267-
}
268-
269-
const value_type *operator->() const
245+
pointer operator->() const
270246
{
271247
if(first_begin == first_end)
272248
return &(*second_begin);
@@ -311,14 +287,14 @@ template <typename iteratort>
311287
struct ranget final
312288
{
313289
public:
314-
using value_typet = typename iteratort::value_type;
290+
using value_type = typename iteratort::value_type;
315291

316292
ranget(iteratort begin, iteratort end) : begin_value(begin), end_value(end)
317293
{
318294
}
319295

320296
ranget<filter_iteratort<iteratort>>
321-
filter(std::function<bool(const value_typet &)> f)
297+
filter(std::function<bool(const value_type &)> f)
322298
{
323299
auto shared_f = std::make_shared<decltype(f)>(std::move(f));
324300
filter_iteratort<iteratort> filter_begin(shared_f, begin(), end());
@@ -335,9 +311,9 @@ struct ranget final
335311
template <typename functiont>
336312
auto map(functiont &&f) -> ranget<map_iteratort<
337313
iteratort,
338-
typename std::result_of<functiont(value_typet)>::type>>
314+
typename std::result_of<functiont(value_type)>::type>>
339315
{
340-
using outputt = typename std::result_of<functiont(value_typet)>::type;
316+
using outputt = typename std::result_of<functiont(value_type)>::type;
341317
auto shared_f = std::make_shared<
342318
std::function<outputt(const typename iteratort::value_type &)>>(
343319
std::forward<functiont>(f));
@@ -365,7 +341,7 @@ struct ranget final
365341
return begin_value == end_value;
366342
}
367343

368-
iteratort begin()
344+
iteratort begin() const
369345
{
370346
return begin_value;
371347
}

unit/util/range.cpp

Lines changed: 91 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ Author: Romain Brenguier, [email protected]
1111
#include <testing-utils/catch.hpp>
1212
#include <util/range.h>
1313

14+
/// Trivial example template function requiring a container to have a
15+
/// `value_type`.
16+
template <typename containert>
17+
typename containert::value_type front(containert container)
18+
{
19+
return *container.begin();
20+
}
21+
1422
SCENARIO("range tests", "[core][util][range]")
1523
{
1624
GIVEN("A vector with three strings")
@@ -21,15 +29,15 @@ SCENARIO("range tests", "[core][util][range]")
2129
list.emplace_back("acdef");
2230
THEN("Use range-for to compute the total length")
2331
{
24-
auto range = make_range(list);
32+
const auto range = make_range(list);
2533
std::size_t total_length = 0;
2634
for(const auto &s : range)
2735
total_length += s.length();
2836
REQUIRE(total_length == 12);
2937
}
3038
THEN("Use map to compute individual lengths")
3139
{
32-
auto length_range =
40+
const auto length_range =
3341
make_range(list).map([](const std::string &s) { return s.length(); });
3442
auto it = length_range.begin();
3543
REQUIRE(*it == 3);
@@ -42,16 +50,27 @@ SCENARIO("range tests", "[core][util][range]")
4250
}
4351
THEN("Filter using lengths")
4452
{
45-
auto filtered_range = make_range(list).filter(
53+
const auto filtered_range = make_range(list).filter(
4654
[&](const std::string &s) { return s.length() == 4; });
4755
auto it = filtered_range.begin();
4856
REQUIRE(*it == "cdef");
4957
++it;
5058
REQUIRE(it == filtered_range.end());
5159
}
60+
THEN(
61+
"A const instance of a `filter_iteratort` can mutate the input "
62+
"collection.")
63+
{
64+
const auto it =
65+
make_range(list)
66+
.filter([&](const std::string &s) { return s.length() == 3; })
67+
.begin();
68+
*it += "x";
69+
REQUIRE(*list.begin() == "abcx");
70+
}
5271
THEN("Filter, map and use range-for on the same list")
5372
{
54-
auto range =
73+
const auto range =
5574
make_range(list)
5675
.filter([&](const std::string &s) -> bool { return s[0] == 'a'; })
5776
.map([&](const std::string &s) { return s.length(); });
@@ -64,6 +83,72 @@ SCENARIO("range tests", "[core][util][range]")
6483
REQUIRE(total == 8);
6584
}
6685
}
86+
GIVEN("A const vector of ints")
87+
{
88+
const std::vector<int> input{1, 2, 3, 4};
89+
THEN("Filter the vector using range.")
90+
{
91+
const auto odds_range =
92+
make_range(input).filter([](const int number) { return number % 2; });
93+
const std::vector<int> odds{odds_range.begin(), odds_range.end()};
94+
const std::vector<int> expected_odds{1, 3};
95+
REQUIRE(odds == expected_odds);
96+
}
97+
THEN(
98+
"The unit testing template function requiring `value_type` works with "
99+
"`std::vector`.")
100+
{
101+
REQUIRE(front(input) == 1);
102+
}
103+
THEN(
104+
"A range can be used with a template function expecting a container "
105+
"which has a `value_type`.")
106+
{
107+
REQUIRE(front(make_range(input)) == 1);
108+
}
109+
THEN("Map over the vector using range.")
110+
{
111+
const auto plus_one_range =
112+
make_range(input).map([](const int number) { return number + 1; });
113+
const std::vector<int> plus_one_collection{plus_one_range.begin(),
114+
plus_one_range.end()};
115+
const std::vector<int> expected_output{2, 3, 4, 5};
116+
REQUIRE(plus_one_collection == expected_output);
117+
};
118+
}
119+
GIVEN("Two const vectors of ints")
120+
{
121+
const std::vector<int> input1{1, 2};
122+
const std::vector<int> input2{3, 4};
123+
THEN("Concat the vectors using range.")
124+
{
125+
const auto range = make_range(input1).concat(make_range(input2));
126+
const std::vector<int> output{range.begin(), range.end()};
127+
const std::vector<int> expected{1, 2, 3, 4};
128+
REQUIRE(output == expected);
129+
};
130+
}
131+
GIVEN("Two non-const vectors of ints.")
132+
{
133+
std::vector<int> input1{1, 2};
134+
std::vector<int> input2{3, 4};
135+
THEN(
136+
"Const instances of `concat_iteratort` should enable the input "
137+
"collections to be mutated.")
138+
{
139+
const auto concat_range = make_range(input1).concat(make_range(input2));
140+
int x = 5;
141+
for(auto it = concat_range.begin(); it != concat_range.end(); ++it, ++x)
142+
{
143+
const auto const_it = it;
144+
*const_it = x;
145+
}
146+
std::vector<int> expected_result1{5, 6};
147+
std::vector<int> expected_result2{7, 8};
148+
REQUIRE(input1 == expected_result1);
149+
REQUIRE(input2 == expected_result2);
150+
}
151+
}
67152
}
68153

69154
class move_onlyt
@@ -98,13 +183,13 @@ SCENARIO(
98183
input.emplace_back(i);
99184
THEN("Values from a range of made from the vector can be moved.")
100185
{
101-
auto input_range = make_range(input);
186+
const auto input_range = make_range(input);
102187
move_onlyt destination{std::move(*input_range.begin())};
103188
REQUIRE(destination.value == 1);
104189
}
105190
THEN("A range of made from the vector can be filtered.")
106191
{
107-
auto odds_filter = make_range(input).filter(is_odd);
192+
const auto odds_filter = make_range(input).filter(is_odd);
108193
const std::size_t total =
109194
std::distance(odds_filter.begin(), odds_filter.end());
110195
REQUIRE(total == 5);

0 commit comments

Comments
 (0)