Search code examples
c++boosteigenboost-geometryr-tree

boost r-tree error: The first type of std::pair has to be an Indexable. How do I fix this?


I'm using boost::geometry::index::rtree with the point type given by Eigen::Vector. To do this, I'm declaring

namespace boost
{
    namespace geometry
    {
        namespace traits
        {
            template<typename T, std::size_t D> struct tag<Eigen::Vector<T, D>> { using type = point_tag; };
            template<typename T, std::size_t D> struct dimension<Eigen::Vector<T, D>> : boost::mpl::int_<D> {};
            template<typename T, std::size_t D> struct coordinate_type<Eigen::Vector<T, D>> { using type = T; };
            template<typename T, std::size_t D> struct coordinate_system<Eigen::Vector<T, D>> { using type = boost::geometry::cs::cartesian; };

            template<typename T, std::size_t D, std::size_t Index>
            struct access<Eigen::Vector<T, D>, Index>
            {
                static_assert(Index < D, "out of range");

                using Point = Eigen::Vector<T, D>;
                using CoordinateType = typename coordinate_type<Point>::type;
                static inline CoordinateType get(Point const& p) { return p[Index]; }
                static inline void set(Point& p, CoordinateType const& value) { p[Index] = value; }
            };
        } // namespace traits
    } // namespace geometry
} // namespace boost

Then I declare

template<class PointType>
using rtree = boost::geometry::index::rtree<PointType, boost::geometry::index::rstar<16>>;

and rtree<std::pair<Eigen::Vector<double, 2>, std::size_t>> foo;. While this perfectly compiles using MSVC, I'm receiving the error

error: static assertion failed: The first type of std::pair has to be an Indexable. 24 | static_assert(boost::geometry::detail::static_assert_check<(CHECK), VA_ARGS>::value, MESSAGE)

when I compile it under Linux/GCC. What's going wrong here and how do I fix it?


Solution

  • Somehow, GCC doesn't match the partial template specialization unless you make std::size_t D template parameter int.

    This probably has to do with an internal interpretation of narrowing restrictions, but I feel it might have been a bug (in some version(s) of GCC?). Clang 15 accepts it just like you report MSVC did.

    Changing that fixes it on my GCC versions as well:

    Live On Compiler Explorer

    #include <Eigen/StdVector>
    #include <boost/geometry.hpp>
    #include <boost/geometry/geometries/adapted/std_array.hpp>
    #include <boost/geometry/geometries/register/point.hpp>
    #include <boost/geometry/index/rtree.hpp>
    namespace bgi = boost::geometry::index;
    
    namespace boost::geometry::traits {
        template <typename T, int D> struct tag<Eigen::Vector<T, D>> {
            using type = point_tag;
        };
        template <typename T, int D> struct dimension<Eigen::Vector<T, D>> : boost::mpl::int_<D> {};
        template <typename T, int D> struct coordinate_type<Eigen::Vector<T, D>> {
            using type = T;
        };
        template <typename T, int D> struct coordinate_system<Eigen::Vector<T, D>> {
            using type = boost::geometry::cs::cartesian;
        };
    
        template <typename T, int D, std::size_t Index> struct access<Eigen::Vector<T, D>, Index> {
            static_assert(Index < D, "out of range");
    
            using Point          = Eigen::Vector<T, D>;
            using CoordinateType = typename coordinate_type<Point>::type;
            static inline CoordinateType get(Point const& p) { return p[Index]; }
            static inline void           set(Point& p, CoordinateType const& value) { p[Index] = value; }
        };
    } // namespace boost::geometry::traits
    
    int main() {//
        using Point = Eigen::Vector<double, 2>;
        bgi::rtree<std::pair<Point, std::size_t>, bgi::rstar<16>> foo;
    }