boostorg / boostorg/geometry

Compilation error when adapting geometry model with point iterator returning by value

Open
#902 6 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
C++
Stars
517
Forks
232
PR merge metrics
No merged PRs in 30d

Description

## Problem
I am adapting my geometry model to work with boost::geometry(as described in the [tutorial](https://www.boost.org/doc/libs/1_76_0/libs/geometry/doc/html/geometry/examples/example__adapting_a_legacy_geometry_object_model.html)).

However, my point iterator needs to return a value rather than a reference. In my case, there is no specific class to represent a point and I need to create points on the fly from the binary representation(see example below).

When adapting the polygon concept I receive the following complication issue:
```
[build] /opt/homebrew/include/boost/geometry/iterators/concatenate_iterator.hpp:105:16: warning: returning reference to local temporary object [-Wreturn-stack-address]
[build] return *m_it1;
[build] ^~~~~~
[build] /opt/homebrew/include/boost/iterator/iterator_facade.hpp:550:20: note: in instantiation of member function 'boost::geometry::concatenate_iterator, std::__1::__wrap_iter>, boost::geometry::flatten_iterator, PointIterator, std::__1::__wrap_iter>, const boost::geometry::model::d2::point_xy, boost::geometry::dispatch::points_begin, boost::geometry::dispatch::points_end>, const boost::geometry::model::d2::point_xy, const boost::geometry::model::d2::point_xy &>::dereference' requested here
[build] return f.dereference();
[build] ^
[build] /opt/homebrew/include/boost/iterator/iterator_facade.hpp:656:42: note: in instantiation of function template specialization 'boost::iterators::iterator_core_access::dereference, std::__1::__wrap_iter>, boost::geometry::flatten_iterator, PointIterator, std::__1::__wrap_iter>, const boost::geometry::model::d2::point_xy, boost::geometry::dispatch::points_begin, boost::geometry::dispatch::points_end>, const boost::geometry::model::d2::point_xy>>' requested here
[build] return iterator_core_access::dereference(this->derived());
[build] ^
[build] /opt/homebrew/include/boost/iterator/iterator_adaptor.hpp:294:18: note: in instantiation of member function 'boost::iterators::detail::iterator_facade_base, std::__1::__wrap_iter>, boost::geometry::flatten_iterator, PointIterator, std::__1::__wrap_iter>, const boost::geometry::model::d2::point_xy, boost::geometry::dispatch::points_begin, boost::geometry::dispatch::points_end>, const boost::geometry::model::d2::point_xy>, const boost::geometry::model::d2::point_xy, boost::iterators::bidirectional_traversal_tag, const boost::geometry::model::d2::point_xy &, long, false, false>::operator*' requested here
[build] { return *m_iterator; }
[build] ^
[build] /opt/homebrew/include/boost/iterator/iterator_facade.hpp:550:20: note: in instantiation of member function 'boost::iterators::iterator_adaptor, boost::geometry::concatenate_iterator, std::__1::__wrap_iter>, boost::geometry::flatten_iterator, PointIterator, std::__1::__wrap_iter>, const boost::geometry::model::d2::point_xy, boost::geometry::dispatch::points_begin, boost::geometry::dispatch::points_end>, const boost::geometry::model::d2::point_xy>, boost::use_default, boost::use_default, boost::use_default, boost::use_default>::dereference' requested here
[build] return f.dereference();
[build] ^
[build] /opt/homebrew/include/boost/iterator/iterator_facade.hpp:656:42: note: in instantiation of function template specialization 'boost::iterators::iterator_core_access::dereference>' requested here
[build] return iterator_core_access::dereference(this->derived());
[build] ^
[build] /opt/homebrew/include/boost/geometry/algorithms/detail/distance/linear_to_linear.hpp:68:26: note: (skipping 2 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
[build] >::apply(*points_begin(linear2), linear1, strategy);
[build] ^
[build] /opt/homebrew/include/boost/geometry/algorithms/detail/distance/interface.hpp:128:16: note: in instantiation of member function 'boost::geometry::detail::distance::linear_to_areal>::apply' requested here
[build] >::apply(geometry1, geometry2, strategy_type());
[build] ^
[build] /opt/homebrew/include/boost/geometry/algorithms/detail/distance/interface.hpp:151:16: note: in instantiation of function template specialization 'boost::geometry::resolve_strategy::distance::apply' requested here
[build] >::apply(geometry1, geometry2, strategy);
[build] ^
[build] /opt/homebrew/include/boost/geometry/algorithms/detail/distance/interface.hpp:378:19: note: in instantiation of function template specialization 'boost::geometry::resolve_variant::distance::apply' requested here
[build] >::apply(geometry1, geometry2, strategy);
[build] ^
[build] /opt/homebrew/include/boost/geometry/algorithms/detail/distance/interface.hpp:403:22: note: in instantiation of function template specialization 'boost::geometry::distance' requested here
[build] return geometry::distance(geometry1, geometry2, default_strategy());
```

## Reproducing
To reproduce a problem I wrote a simple program that creates polygons and linestrings from separate vectors for X and Y:

Click here to see a simple program to reproduce the issue



```cpp
#include
#include
#include

using namespace std;
namespace bg = boost::geometry;

// Iterator creating points from two separate iterators for Xs and Ys
template
class PointIterator
: public boost::iterator_facade, T,
boost::random_access_traversal_tag, T> {
public:
friend class boost::iterator_core_access;

PointIterator() {}
PointIterator(I ix, I iy) : _ix(ix), _iy(iy) {}

// I WANT RETURN BY VALUE HERE!!!
T dereference() const { return T{*_ix, *_iy}; }

void increment() {
++_ix;
++_iy;
}
void decrement() {
--_ix;
--_iy;
}
void advance(size_t n) {
_ix += n;
_iy += n;
}
typename PointIterator::difference_type
distance_to(const PointIterator& other) const {
assert(distance(this->_ix, other._ix) == distance(this->_iy, other._iy));
return other._ix - this->_ix;
}
bool equal(const PointIterator& other) const {
return this->_ix == other._ix && this->_iy == other._iy;
}
private:
I _ix, _iy;
};

// Implementing my "geometry model" below
using MyPoint = bg::model::d2::point_xy;

struct MyLineString {
using const_iterator = PointIterator::const_iterator>;

const_iterator begin() const {
return const_iterator(_x.cbegin(), _y.cbegin());
}
const_iterator end() const { return const_iterator(_x.cend(), _y.cend()); }

vector _x, _y;
};

struct MyRing {
using const_iterator = PointIterator::const_iterator>;

const_iterator begin() const {
return const_iterator(_x.cbegin(), _y.cbegin());
}
const_iterator end() const { return const_iterator(_x.cend(), _y.cend()); }

vector _x, _y;
};

struct MyPolygon {
using const_iterator = vector::const_iterator;

MyRing exterior;
vector interior; // We will not use it here
};

// Implementing boost::geometry traits for my types
namespace boost::geometry::traits {

template <> struct tag { using type = linestring_tag; };
template <> struct tag { using type = ring_tag; };
template <> struct tag { using type = polygon_tag; };

template <> struct ring_const_type { using type = const MyRing&; };
template <> struct ring_mutable_type { using type = const MyRing&; };
template <> struct interior_const_type {
using type = const boost::iterator_range;
};
template <> struct interior_mutable_type {
using type = boost::iterator_range;
};

template <> struct exterior_ring {
static MyRing const& get(MyPolygon const& p) { return p.exterior; }
};

template <> struct interior_rings {
static const auto get(MyPolygon const& p) {
return boost::iterator_range(p.interior.cbegin(),
p.interior.cend());
}
};

} // namespace boost::geometry::traits

// Implementing range traits
namespace boost {
template <> struct range_iterator {
using type = MyLineString::const_iterator;
};
template <> struct range_iterator {
using type = MyRing::const_iterator;
};
} // namespace boost

// Compute distance between linestring and polygon
int main() {
MyPolygon polygon{MyRing{
{1, 1, 2, 2, 1}, // x coordinates
{1, 2, 2, 1, 1} // y coordinates
},
{}};
MyLineString linestring{
{-1, 1}, // x coordinates
{1, -1} // y coordinates
};

cout << "Distance = " << bg::distance(linestring, polygon) << endl;
}
```

## Possible cause

I seems that issue can be in how the [geometry::detail::point_iterator](https://github.com/boostorg/geometry/blob/665305defa72f4de8777666c632bdd0436666343/include/boost/geometry/iterators/detail/point_iterator/iterator_type.hpp#L63) is defined. It is a combination of `concatenate_iterator` and `flatten_iterator` which both assume reference by default instead of deriving the reference type from the incoming point iterators:
```cpp
template
class iterator_type
{
private:
typedef typename inner_range_type::type inner_range;

public:
typedef concatenate_iterator
<
typename boost::range_iterator::type,
flatten_iterator
<
typename boost::range_iterator
<
typename geometry::interior_type::type
>::type,
typename iterator_type::type,
typename value_type::type, // Value
dispatch::points_begin,
dispatch::points_end
//, Reference = Value& !!!
>,
typename value_type::type // Value
//, Reference = Value& !!!
> type;
};
```

Indeed, the test program above works when I add the following specialization:
```cpp
namespace boost::geometry::detail::point_iterator {

template <> class iterator_type {
private:
typedef typename inner_range_type::type inner_range;

public:
using type = concatenate_iterator<
typename boost::range_iterator::type,
flatten_iterator<
typename boost::range_iterator<
typename geometry::interior_type::type>::type,
typename iterator_type::type,
typename value_type::type,
dispatch::points_begin,
dispatch::points_end,
typename value_type::type>, // Provide reference type
typename value_type::type,
typename value_type::type>; // Provide reference type
};

} // namespace boost::geometry::detail::point_iterator
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.