Boost logo

Boost :

Subject: Re: [boost] A design for geometric objects
From: Simonson, Lucanus J (lucanus.j.simonson_at_[hidden])
Date: 2008-11-14 18:43:09


Bruno wrote:
>Indeed it's another right approach I think, don't know either which
>one would be the best one.
>Unless I missed something, you can use the Boost.TypeTraits's is_same
>in conjunction with enable_if instead of your SFINAE_is_same_type.

The thing you are missing is the rather unfortunate circumstance I find myself in where two of my primary groups of users refuse to allow any boost dependency in their core libraries. Apparently, boost has a reputation for making the life of the build environment manager a pain. I'm left out in the cold like an orphan staring in through the frost-covered window on Christmas Eve.

The last thing we need is SFINAE based selection of functions instead of tag dispatching because tag dispatching on object type forces me to implement lots of empty wrapper functions that redirect calls to the right implementation.

template <typename T>
struct is_polygon_concept {};

template <>
struct is_polygon_concept<rectangle_concept> { typedef void type; }
template <>
struct is_polygon_concept<polygon_concept> { typedef void type; }
template <>
struct is_polygon_concept<polygon_with_holes_concept> {
        typedef void type; }
template <>
struct is_polygon_concept<polygon_set_concept> { typedef void type; }

template <typename T, typename T2>
sruct requires { typedef T2 type; }

template <typename T>
typename requires<
        typename is_polygon_concept<
                typename geometry_concept<T>::type
>::type,
        perimeter_return_type>::type
perimeter(const T& polygon) {
        typename polygon_traits<T>::iterator_edges itr =
                polygon_traits<T>::begin_edges(polygon);
        typename polygon_traits<T>::iterator end =
                polytope_traits<T>::end_edges(polygon);
        perimeter_return_type retval = perimeter_return_type(0);
        for(; itr != end; ++itr) {
                perimeter_return_type += length(*itr);
        }
        return retval;
}

Assuming the appropriate specializations for adapting concepts to polygon concept are provided by the library, the perimeter function works on triangle, rectangle, convex polygon, axis parallel polygon, etc. Assuming rectangle is already specialized for axis parallel polygon implementing polygon specialization of rectangle is unnecessary if it is specialized for axis parallel polygon.

It is critically important to take advantage of this extra layer of abstraction because without it the number of functions the library author is required to write in order to handle all the conceptual geometry types the users would want is intractably large. Note that perimeter cannot be called on a circle because is_polygon_concept fails to substitute circle for T.

This provides the semantic of geometric subtyping that Mathias first suggested, but by meta-function and SFINAE rather than inheritance.

We will probably need to create separate mutable traits for geometry types so that they are not accessible through the regular traits.

template <typename T, typename enable = void>
struct ellipse_traits {
        typedef T::coordinate_type coordinate_type;
        coordinate_type width(const T& ellipse);
};

template <typename T, typename enable = void>
struct mutable_ellipse_traits {
        template <typename coord_type>
        void set_width(T& ellipse, coord_type value);
}

mutable_ellipse_traits is not specialized for circle when ellipse traits is, and is_ellipse_concept would need a complementing is_mutable_ellipse_concept to complete this pattern. We might specialize mutable_circle_traits for elipse because we can write circle data to an ellipse.

Regards,
Luke


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk