Boost logo

Boost :

Subject: Re: [boost] Preview 3 of the Geometry Library
From: Simonson, Lucanus J (lucanus.j.simonson_at_[hidden])
Date: 2008-10-21 13:51:55


Luke wrote:
>> Also, your get function returns a reference to coordinate. It should
>> return by value. Many legacy point types may have a public API that
>> prohibits direct access to their members because that is what good
>> little C++ programmers were taught to do in school. Those point classes
>> could not conform to your concept.

Bruno wrote:
>If you are talking about using the getter for read access, this way of
>writing traits specialization is not an obligation and the user can
>always return by value in his own one.

>Except that wouldn't work with library code that used it as an lvalue.
>If you are talking about using the getter for write access (that is:
>get<0>(p) = value) it's more problematics. We've discussed about that
>with Barend and it appears that as you say, a legacy point without
>direct access to its coordinates will never be adaptable to such
>traits. I hadn't thought about that before.

>The problem is that this approach (value = get<0>(p) for read access
>and get<0>(p) = value for write access) has been chosen after having
>been advocated by some people on this list. The argument was that it
>is a very common approach in Boost, for Tuple and Fusion. And this
>argument was quite convincing.

>Must I deduce that the Boost.Fusion approach is not totally generic
>and that a structure with unreferencable private members couldn't be
>adapted to it?

>And what would finally be the best way to handle point write access if
>the Boost.Fusion one is wrong?

I don't think it's a matter of right and wrong. It's a matter of goals and objectives. My goal is to work with all geometry types and not impose unnecessary restrictions on the way geometry types need to be implemented. Fusion is a way to work with plain old data. Because point objects have no invariant to enforce, they should be plain old data (though often aren't defined that way), but that is not true of geometry objects in general. 1D intervals are almost as simple, but do have an invariant, and therefore should not provide direct access to their data members.

I was just looking a Brandon's library, and it seems he has no write access to point, but does have a construct function that accepts the coordinates and returns a point by value. This could be used to overwrite a point type that provides an assignment operator, but would be cumbersome to use to write to a single coordinate of a point.

My traits looks like the following:

template <typename T>
struct point_traits {
  typedef typename T::coordinate_type coordinate_type;

  static inline coordinate_type get(const T& point, orientation_2d orient) {
    return point.get(orient);
  }
  static inline void set(T& point, orientation_2d orient, coordinate_type value) {
    point.set(orient, value);
  }
  static inline T construct(coordinate_type x_value, coordinate_type y_value) {
    return T(x_value, y_value);
  }
};

This is specifically the Cartesian 2D point traits, the 3D point traits are a different structure. It needs to be specialized for point types that don't conform to my expected default behavior. Like Brandon, I provide a construct function that returns a point by value. I also provide the set function that takes a point by reference, an orientation class encapsulated enum and the coordinate value. I prefer runtime indexing, as I've mentioned before.

There is a certain degree of redundancy in the way I define the traits, since construct and set are both ways of getting data into an object. I think that Brandon may have the right idea with a getter function and a construct function. It is rare to write to only part of an unknown object type.

I conclude that the point accessor should return by value or const reference, and that the traits should also provide a construct function to construct a point from coordinate values. A given point class should additionally provide an assignment operator and copy constructor to conform to the point concept. If we are able to get away without requiring default constructor that would be a good thing. For an ND point, the construct function should take an iterator pair over coordinate values and the accessor function must be runtime indexing.

In looking more at Brandon's code, I have to repeat my observation that he has done an impressive job of defining a generic geometry type system and using boost libraries to do it. I think that if we make his accessor dimension agnostic (either runtime or compile time indexing) we have a winner.

Regards,
Luke


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