
I thought I'd update those who are interested in on my progress redesigning the generic interfaces of my geometry type system. After implementing compile time accessors in the point_concept class and noticing that specializing them required explicit specialization of all possible values of the template parameter I realized that this won't work for more general interfaces. Specifically, polygons require iterator semantics at their interfaces. That means that the arguments to the set() interface of a polygon_concept would need to be templated for the iterator type, since that function should take an iterator range. Since functions cannot be partially specialized I moved the accessors back into the point_traits<T> stuct, where it is the struct that is specialized, and the functions can keep their template parameter lists when the user specialized the traits for their type. This however led to a erroneous attempt to wrap the point_traits accessor with a templated function (such as a wrapper in the point_concept struct). In the example below, I show a free function wrapper. Directly instantiating and calling the point_traits<T>::get<int>(T) function compiles, but calling the free function wrapper does not compile and gives an error about operator<. //demo point type struct point_data { int coords_[2]; int get(int orient) const { return coords_[orient]; } typedef int coordinate_type; }; template <typename T> struct point_traits { typedef typename T::coordinate_type coordinate_type; template<int orient> static inline coordinate_type get(const T& point) { return point.get(orient); } }; template <int orient, typename T> typename point_traits<T>::coordinate_type get(const T& point) { return point_traits<T>::get<orient>(point); } int main() { point_data pt; //this line compiles int value0 = point_traits<point_data>::get<0>(pt); //this line does not compile int value = get<0>(pt); return 0; } Note, if the free function directly uses the API of the data type it compiles, but as I explained initially, I need the indirection of going through the point_traits struct to allow specialization without getting burned by the language disallowing partial specialization of functions. This leaves me in something of a pickle. Because it was what I wanted to do anyway in the first place, my solution is to take the orient parameter out of the template list and make it runtime. The compiler readily accepts that syntax. After rewriting the example code that way, and getting it compiling and working satisfactorily, I was very pleased to notice that all of the code that looked like: if(orient == HORIOZNTAL) return get<HORIZONTAL>(point); return get<VERTICAL>(point); was gone, and access through any path of indirection would result in no if statements needing to be executed. I did implement a compile time accessor in the point concept (it calls the runtime one in the point_traits struct) but it doesn't make a lot of sense to have it, since: template <typename T> typename point_traits<T>::coordinate_type point_concept::x(const T& point); is more concise. Since this isn't a tuple or some n-dimensional hyper-point there is no need for meta-programming its accessors. So, I also went ahead and implemented the pattern for polygon data types. I translated some of the algorithms that operate on a polygon to use the new interface to get a taste for what was in store for me. In the guts of the polygon_type related code is an algorithm (implemented as an iterator) for converting the "compact" representation of a manhattan polygon with one integer per edge to a sequence of points at its vertices. The increment operator looks thusly: inline iterator_points& operator++() { ++iter_; if(iter_ == iter_end_) { if(point_concept::get<HORIZONTAL>(pt) != firstX_) { --iter_; point_concept::set<HORIZONTAL>(pt, firstX_); } } else { point_concept::set(pt_, orient_, *iter_); orient_.turn90(); } return *this; } Note: that most of the time it updates a point pt_, which is a member of the iterator_points class based on the *runtime* value of orient_, and then changes the runtime value each time it is called. When executed in a loop this results in no branch instructions other than the ones checking for loop termination (and the special case for last vertex). If I used compile time accessors as the interface to the data type there would be an extra branch in that loop for no good reason when using my own point type (the common case.) Thoughts? Thanks, Luke

I have re-written the GTL polygon type to use the traits/concepts generic interface, similar to what we've been discussing vis-a-vi point types. The code compiles on gcc4.2.0 at least. Please see the attached file (1KLOC) which defines the polygon_traits, polygon_concept and provides some generic behaviors for the polygon_concept. (getting and setting polygon contents through iterator range semantics, construction from a rectangle, getting number of vertices and winding direction, bounding box/area/perimeter computation, point containment predicate algorithm, point projection, and translation algorithm.) While point makes a good conversation piece, because it's interfaces are trivial, it is the polygon types that are most important to provide a good generic interface. Once the new polygon interface is settled, I can port the heavier algorithms in the library to the new interfaces. Please have a look and let me know what you think. I'm happy with the interfaces as it stands now, and plan to continue to re-write the rest of the library in this style unless feedback from the community suggests a better approach. Fernando in particular should have some insight into the polygon interfaces. Thanks, Luke

I found a way to work around the compiler error for wrapping compile time accessors defined in traits classes. //demo point type struct point_data { int coords_[2]; int get(int orient) const { return coords_[orient]; } typedef int coordinate_type; }; template <typename T> struct point_traits { point_traits() {} //needs a default constructor now typedef typename T::coordinate_type coordinate_type; template<int orient> static inline coordinate_type get(const T& point) { return point.get(orient); } }; //would not compile //template <int orient, typename T> //typename point_traits<T>::coordinate_type get(const T& point) { // return point_traits<T>::get<orient>(point); //} //will compile when written this way template <int orient, typename T> typename point_traits<T>::coordinate_type get(const T& point) { point_traits<T> traits_instance; return traits_instance.get<orient>(point); } int main() { point_data pt; //this line compiles int value0 = point_traits<point_data>::get<0>(pt); //this line now compiles int value = get<0>(pt); return 0; } But it still bugs me that I don't know why the compiler thought that point_traits<T>::get was a function pointer and tried to apply the operator< with orient, but does the right thing when I instantiate the traits struct and use the . operator to access the same function. The . operator could also produce a function pointer. Thanks, Luke

On Tue, May 6, 2008 at 1:42 PM, Simonson, Lucanus J <lucanus.j.simonson@intel.com> wrote:
But it still bugs me that I don't know why the compiler thought that point_traits<T>::get was a function pointer and tried to apply the operator< with orient, but does the right thing when I instantiate the traits struct and use the . operator to access the same function. The . operator could also produce a function pointer.
Try: point_traits<T>::template get<0>(pt); --Michael Fawcett

AMDG Simonson, Lucanus J wrote:
//would not compile //template <int orient, typename T> //typename point_traits<T>::coordinate_type get(const T& point) { // return point_traits<T>::get<orient>(point); //}
//will compile when written this way template <int orient, typename T> typename point_traits<T>::coordinate_type get(const T& point) { point_traits<T> traits_instance; return traits_instance.get<orient>(point); }
int main() { point_data pt; //this line compiles int value0 = point_traits<point_data>::get<0>(pt); //this line now compiles int value = get<0>(pt); return 0; }
But it still bugs me that I don't know why the compiler thought that point_traits<T>::get was a function pointer and tried to apply the operator< with orient, but does the right thing when I instantiate the traits struct and use the . operator to access the same function. The . operator could also produce a function pointer.
You need to use point_traits<T>::template get<0> inside a template. The compiler should not accept the "." either, BTW. In Christ, Steven Watanabe
participants (3)
-
Michael Fawcett
-
Simonson, Lucanus J
-
Steven Watanabe