c++boostc++17customizationboost-geometry

Boost Geometry compilation error in version 1.80.0 using custom point type


I'm using Boost 1.80.0.
I would like to use boost geometries with a custom point type.
To do that I register my custom point into boost::geometry.

I am then lock when I want to use boost::geometry::within() function.
I got a compilation error that I do not understand well.

See some errors:

boost/1.80/include/boost/geometry/algorithms/detail/relate/boundary_checker.hpp:99:38: error: no matching function for call to ‘std::vector<boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>, std::allocator<boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian> > >::push_back(const point&)’

/usr/include/c++/10/bits/predefined_ops.h:194:23: error: no match for call to ‘(boost::geometry::less<boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>, -1, boost::geometry::cartesian_tag>) (boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian>&, const point&)’

But, if I compile with a previous Boost version (for example Boost 1.79.0) it compiles fine.

Edit : It appears to be a boost::geometry regression, and not a misuse from my part.

As I am not familiar with boost::geometry and Boost does not mention any change for boost::geometry from version 1.79.0 to 1.80.0, I assume I am missing something with my comprehension of using boost type with custom points.

Code sample:


#include <iostream>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/register/point.hpp>

struct point {
    double x, y;
    point(double x = 0, double y = 0) : x(x), y(y) {}
};

BOOST_GEOMETRY_REGISTER_POINT_2D(point, double, boost::geometry::cs::cartesian, x, y);

using line = boost::geometry::model::linestring<point>;
using multiline = boost::geometry::model::multi_linestring<line>;
using polygon = boost::geometry::model::polygon<point>;

int main(int argc, char** argv) {
  multiline inMultiline;
  polygon inPoly;
  bool isWithin = boost::geometry::within(inMultiline, inPoly); // error

  return EXIT_SUCCESS;
}

Note 1 : within() "Supported Geometries" table state that it is correct to use it with multi_linestring and polygon.

Note 2 : Remplacing my custom point by using point = boost::geometry::model::point<double, 2, boost::geometry::cs::cartesian> works fine in this sample.

Edit: As It seems that come from a bug from boost:geometry I edited the title for newcomers.


Solution

  • So I dove into the code/message and noticed that inside the algorithm an equal_range call is used comparing between a helper geometry (built with helper_geometry<>) and yours. It ends up calling a comparator with incompatible point types:

    using mutable_point_type = bg::helper_geometry<MyPoint, double>::type;
    bg::less<mutable_point_type, -1, bg::cartesian_tag> cmp;
    
    mutable_point_type a;
    MyPoint b;
    cmp(a, b);
    

    This gives the same error message, because the less<> implementation doesn't accept generic point types. It looks as though that's an implementation issue, because there exists a bg::less<void, 2, void> implementation that does correctly apply the default comparison-strategy for heterogenous point types:

    template <int Dimension>
    struct less<void, Dimension, void>
    {
        typedef bool result_type;
    
        template <typename Point1, typename Point2>
        inline bool operator()(Point1 const& left, Point2 const& right) const
        {
            typedef typename strategy::compare::services::default_strategy
                <
                    strategy::compare::less,
                    Point1, Point2,
                    Dimension
                >::type strategy_type;
    
            return strategy_type::apply(left, right);
        }
    };
    

    However, pathching the relevant line in boost/geometry/algorithms/detail/relate/boundary_checker.hpp:160 to use it:

    @@ -157,7 +157,7 @@ public:
         template <typename Point>
         bool is_endpoint_boundary(Point const& pt) const
         {
    -        using less_type = geometry::less<mutable_point_type, -1, typename Strategy::cs_tag>;
    +        using less_type = geometry::less<void, -1, void>;
     
             auto const multi_count = boost::size(m_geometry);
    

    reveals more places where boundary_checker assumes identical point types: line 99 attempts to push a MyPoint into vector<> of "helper" points (mutable_point_type):

            boundary_points.push_back(front_pt);
    

    Consequently I could get your code to compile only by patching the implementation (L:148) to use your point type as the helper point type as well.

    @@ -145,7 +145,7 @@ template <typename Geometry, typename Strategy>
     class boundary_checker<Geometry, Strategy, multi_linestring_tag>
     {
         using point_type = typename point_type<Geometry>::type;
    -    using mutable_point_type = typename helper_geometry<point_type>::type;
    +    using mutable_point_type = point_type;
    

    This only works for cases where the point type is mutable, of course, so it won't be a good general-purpose fix.

    I'd file an issue about this with the library devs: https://github.com/boostorg/geometry/issues?q=is%3Aissue+boundary_checker - it looks like it wasn't previously reported there.