Opened 9 years ago
Closed 8 years ago
#9812 closed Bugs (fixed)
BinaryOperation boost::transform only stops when it reaches the end of rng1, even if rng2 is shorter
Reported by: | Owned by: | Neil Groves | |
---|---|---|---|
Milestone: | Boost 1.56.0 | Component: | range |
Version: | Boost 1.54.0 | Severity: | Problem |
Keywords: | Cc: |
Description
Here's a complete program that reproduces the problem:
#include <boost/range/algorithm/transform.hpp> #include <boost/range/irange.hpp>
namespace std {
ostream& operator<<(ostream& stream, const pair<int, int>& p) {
return stream << "(" << p.first << ", " << p.second << ")";
}
}
int main() {
boost::transform(
boost::irange(0, 100), boost::irange(0, 10), std::ostream_iterator<std::pair<int,int>>(std::cout, "\n"), [](const int left, const int right) { return std::make_pair(left, right); });
return EXIT_SUCCESS;
}
I compile on Fedora 20 using g++ 4.8.2. Here's the output I get:
g++ transform.cpp -std=c++11 && ./a.out (0, 0) (1, 1) (2, 2) (3, 3) (4, 4) (5, 5) (6, 6) (7, 7) (8, 8) (9, 9) a.out: /usr/include/boost/range/algorithm/transform.hpp:63: OutputIterator boost::range_detail::transform_impl(SinglePassTraversalReadableIterator1, SinglePassTraversalReadableIterator1, SinglePassTraversalReadableIterator2, SinglePassTraversalReadableIterator2, OutputIterator, BinaryFunction) [with SinglePassTraversalReadableIterator1 = boost::range_detail::integer_iterator<int>; SinglePassTraversalReadableIterator2 = boost::range_detail::integer_iterator<int>; OutputIterator = std::ostream_iterator<std::pair<int, int> >; BinaryFunction = main()::lambda0]: Assertion `first2 != last2' failed. Abort
Here's the code I believe to be broken in boost/range/algorithm/transform.hpp:
template< class SinglePassTraversalReadableIterator1,
class SinglePassTraversalReadableIterator2, class OutputIterator, class BinaryFunction >
inline OutputIterator transform_impl(SinglePassTraversalReadableIterator1 first1,
SinglePassTraversalReadableIterator1 last1, SinglePassTraversalReadableIterator2 first2, SinglePassTraversalReadableIterator2 last2, OutputIterator out, BinaryFunction fn)
{
for (; first1 != last1; ++first1, ++first2) {
BOOST_ASSERT( first2 != last2 ); *out = fn(*first1, *first2); ++out;
} return out;
}
Here's my proposed fix:
template< class SinglePassTraversalReadableIterator1,
class SinglePassTraversalReadableIterator2, class OutputIterator, class BinaryFunction >
inline OutputIterator transform_impl(SinglePassTraversalReadableIterator1 first1,
SinglePassTraversalReadableIterator1 last1, SinglePassTraversalReadableIterator2 first2, SinglePassTraversalReadableIterator2 last2, OutputIterator out, BinaryFunction fn)
{
for (; first1 != last1 && first2 != last2; ++first1, ++first2) {
*out = fn(*first1, *first2); ++out;
} return out;
}
I added a check that first2 != last2 and removed the ASSERT.
Here's the relevant documentation: BinaryOperation version:
transform assigns the value z to each element [out, out + min(distance(rng1), distance(rng2))), z = fun(x,y) where x is the corresponding value in rng1 and y is the corresponding value in rng2. This version of transform stops upon reaching either the end of rng1, or the end of rng2. Hence there isn't a requirement for distance(rng1) == distance(rng2) since there is a safe guaranteed behaviour, unlike with the iterator counterpart in the standard library.
Please note that the original implementation doesn't meet the specification described above, whereas the new implementation does. BinaryOperation boost::for_each has a similar specification, but it implements it correctly.
Boost Rocks! Keep up the stellar work!
Don't hesitate to contact me if any of the above needs further explanation.
Thanks, David
Change History (3)
comment:1 by , 8 years ago
Milestone: | To Be Determined → Boost 1.57.0 |
---|---|
Status: | new → assigned |
comment:2 by , 8 years ago
Milestone: | Boost 1.57.0 → Boost 1.56.0 |
---|
Release window for 1.56 has been extended so I can put this into 1.56.
comment:3 by , 8 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Merged to master and ready for the next release.
This is fixed on the "develop" branch.