Search code examples
c++boostsfinaeboost-geometry

Add sfinae to a constructor allowing only the boost::geometry point types


boost::geometry provides for planar Cartesian coordinates two classes to define a point, using a given floating point type (double, float, ...):

(assume namespace bg = boost::geometry; in the following)

With FPT being the desired floating point type (float, double, ...):

bg::model::point<FPT, 2, bg::cs::cartesian> pt1;
bg::model::d2::point_xy<FPT> pt2;

(unrelated note: the second type is being inherited from the first one).

(ref1, ref2)

Now say I want a constructor taking a boost point as single argument for some class. How do I write this, so that it will be selected only for these two types (because there are other single argument constructors in the class)?

For example, say I have this, where the constructor for boost points needs to be "sfinaed":

template<typename FPT>
struct Point
{
    FPT x,y;
    template<typename BPT> // Boost Point Type
    Point( const BPT& in )
    { /* code here */ }
    template<typename T> // other type
    Point( const T& in )
    { /* code here */ }
};

I read here that the boost point classes must comply to a concept that states that:

there must be a specialization of traits::tag, defining point_tag as type

So I tried this (live here) but there must be something I am missing, because it fails to build, with the classical

template argument deduction/substitution failed

template<typename FPT> // Floating Point Type
struct Point
{
    FPT x,y;

    template<
        typename BPT,   // Boost Point Type
        typename std::enable_if<
            std::is_same<
                bg::traits::tag<BPT>,
                bg::point_tag
            >::value,
            BPT           
        >::type* = nullptr
    >
    Point( const BPT& pt ):
        x{ bg::get<0>(pt) }, // maybe add some static cast here
        y{ bg::get<1>(pt) }
    {}
};

int main()
{
    bg::model::point<double, 2, bg::cs::cartesian> pt1;
    bg::model::d2::point_xy<double> pt2;
    Point<float>  p1(pt1);
    Point<double> p2(pt2);
}

Those errors are still particularly difficult for me to track down... Any idea?


Solution

  • Well, constructors don't have return types didn't you know? ;)

    Here you go:

    template<typename FPT> // Floating Point Type
    struct Point
    {
        FPT x,y;
    
        template<class BPT>
        Point( const BPT& pt, typename std::enable_if<
            std::is_same<bg::traits::tag<BPT>, bg::point_tag>::value           
        >::type* = nullptr)
        :
            x{ boost::geometry::get<0>(pt) }, // maybe add some static cast here
            y{ boost::geometry::get<1>(pt) }
        {}
    
        template<class Other>
        Point(const Other&) : x{0}, y{0} {}
    };
    

    Note: If C++20 is an option, concepts would be the ideal solution rather than SFINAE.