
Phil wrote:
You should be able to express this something like "Type T models the Point Concept if there exists a specialisation point_interface<T> having members ....". Can you write a definition like that?
As it stands right now: Type T models the Point concept if it is default constructable, copy constructable and there exists a specialization of PointInterface<T> having the members: static Unit pointGet(const T& t, Orientation2D orient); static void pointSet(T& t, Orientatino2D orient, Unit value); static T pointConstruct(Unit x, Unit y); Except for capitalization, I would keep this portion of the library the same (I like runtime resolution of getting x or y value and depend on compiler constant propagation and optimization to provide fast access if orient is known at compile time.) Orientation2D is a class-encapsulated enum to enforce compile time checking that index values are legal. Phil wrote:
I think it would be an interesting thought-experiment to consider how the standard library would look if re-written in this style, e.g. not using std::pair in maps. There are three groups of people affected: - Users with legacy code, who benefit. - Users without legacy code, who have a (perhaps small) extra step on their learning curve. - People writing libraries, who have more work to do.
Users without legacy code have the option of either using the geometry types provided by the library or defining their own that match the expectations of the default (non-specialized version) of the interface. This should offset the added complexity of the extra layer of abstraction to a large degree. I have found new users starting from scratch to be generally happier using GTL than the users with legacy code. I always consider doing work as a library author to safe work for the library user to be a win. I don't see how I create work for people writing other libraries, since they have the option us conforming to the default interface. Bruno wrote:
The point concept of Barend's library doesn't require any x() or y() member functions. The fact that the library proposes 2 predefined point classes, point_xy and point_ll, can be a bit confusing but don't be fooled: they do *not* represent the point concept, they are only 2 classes that satisfy it. The point concept uses accessors of this kind : template <int I> value() const;
That looks much better to me. I didn't follow Barend's thread closely enough, I was unsubscribed at the time. Bruno wrote:
In the second preview of the library, there was also runtime indexing accessors, but they should disappear to only have compile-time accessors as shown above (I'm currently working on this with Barend).
There is no need to have both run time and compile time accessors, and you can do things with run time accessors that you cannot do with compile time accessors. The reverse is not true. I think you made the wrong decision and should consider making the accessors runtime only instead. The way my accessors work is that I pass a class-encapsulated enum value, which, if the data type is particularly well formed, can be used to index directly into an array of member data. This turns out to be just as fast (when optimized) as direct access to a data member when the index is known at compile time and faster if the index is a runtime variable. Consider the difference between: int get(array<int, 2> point, int index) { return point[index]; } and int get(const tuple<int, int>& point, int index) { if(index==0) return get<0>(point); else return get<1>(point); } If you want to be passing the index around as data you don't want it to be a compile time parameter. I want to pass it around as data. I want my point to be a class-encapsulated array. Bruno wrote:
Also, the point concept as currently defined might be replaced by a point_traits class usable the same way as what you propose with your point_interface<T>. It gives something like : point_traits<T>::value<0>(p) to have the first coordinate of a point p. The point concept would then follow the exact definition just given by Phil. Not sure yet this will be done, but it brings a lot of advantages (non-intrusiveness, possibility of using native arrays as points, ...).
I have thought about changing the name to traits. For more complex types, such as polygons, there are typedefs in the interface for iterators, and sometimes entire classes defined to adapt a polygon data type to iterator range semantics. The new pattern might look like: template <class T> class point_traits { public: typedef T::coordinate_type coordinate_type; static inline coordinate_type get(const T& t, Orientation2D orient) { return T.get(orient); } static inline void set(T& t, Orientation2D orient, coordinate_type value) { t.set(orient, value); } static inline T construct(coordinate_type x, coordinate_type y) { return T(x, y); } }; which can be partially specialized to allow legacy types to work with the library. Hartmut wrote:
Joel explicitly alluded to that in the first discussion and I think there is no other way forward. ... And - anything not conformant to >the Standard shouldn't even be considered as a Boost library, IMHO
Yes, Joel did suggest compile time accessors, and tuples in the first discussion. A point can be a tuple; it doesn't have to be. Heterogeneous coordinate types for different axis' coordinate values is reasonable, and provided that they all provide conversion to and from the library's coordinate type they work fine. I have been trying to figure out how to make templating the coordinate data type (everywhere) work, and it provides some advantages, but adds a lot of complexity. It would be even worse if I tried to have separate x_coordinate_type and y_coordinate_type. I'm fine with changing the library (even big changes) to make it conformant to the Standard. I said so when I first posted my code to the vault. Some of my internal install base is even willing to port their code to a new "boost-ified" version, if it comes to that. I think doing so would constitute and improvement. Luke