
I've been working on an idea for using some compile time concept type introspection and tag dispatching to allow generically named free functions (such as contains()) to compile and work in user code when called with any sensible combination of geometry types. This should make user code much more concise than with my current design; all the concept related boilerplate can be dispensed with. //should compile bool result1 = contains(rectangle1, rectangle2); //should compile bool result2 = contains(rectangle1, point); //should compile bool result3 = contains(interval1, interval2); //should compile bool result4 = contains(interval1, coordinate_value); //should not compile because it is non-sensical bool result5 = contains(rectangle1, interval1); //should not compile because it is non-sensical bool result6 = contains(coordinate_value, point1); The idea is that a geometry_traits struct be specialized for each geometry data type such that it provides a typedef for the related geometry concept. That typedef can be inspected at compile time to provide type introspection and passed as a tag value for tag dispatching. See example code pasted below. The one problem with this scheme is that it doesn't allow a user type to model more than one geometry type. That means that if someone models point with std::pair<int, int> it makes it impossible for someone else to model interval with std::pair<int, int> anywhere where the declaration of the one for point is visible. I consider that to be a good thing because it enforces type safety on user types, since you lose the protection of the compiler from inadvertently using point data as interval data if you model std::pair<int, int> as both. However, people eager to use tuple for all of their applicable geometry data types should take note of this limitation. Do you guys prefer this scheme to what I showed in example3.cpp? Thanks, Luke //some point related code omitted struct rectangle_concept { template <typename rectangle_type, typename rectangle_type2> static bool contains_dispatch(const rectangle_type& rectangle, const rectangle_type2 rectangle_contained, rectangle_concept tag) { std::cout << "rectangle contains rectangle\n"; } template <typename rectangle_type, typename point_type> static bool contains_dispatch(const rectangle_type& rectangle, const point_type point_contained, point_concept tag) { std::cout << "rectangle contains point\n"; } template <typename rectangle_type, typename geometry_type> static bool contains(const rectangle_type& rectangle, const geometry_type& contained_geometry_object); }; struct no_type {}; template <typename T> struct geometry_traits { typedef no_type geometry_concept; }; template <> struct geometry_traits<point_data<int> > { typedef point_concept geometry_concept; }; template <> struct geometry_traits<rectangle_data<int> > { typedef rectangle_concept geometry_concept; }; template <typename rectangle_type, typename geometry_type> bool rectangle_concept::contains(const rectangle_type& rectangle, const geometry_type& contained_geometry_object) { //disambiguate function to call through tag dispatching return rectangle_concept::contains_dispatch(rectangle, contained_geometry_object, typename geometry_traits<geometry_type>::geometry_concept()); } //function that should copile for any two geometry types that may have a containment relationship template <typename geometry_type_1, typename geometry_type_2> bool contains(const geometry_type_1& geometry_object, const geometry_type_2& contained_geometry_object) { //select function to call through introspection typename geometry_traits<geometry_type_1>::geometry_concept concept_instantiation; return concept_instantiation.contains(geometry_object, contained_geometry_object); } void foo() { rectangle_data<int> rect1, rect2; point_data<int> point; rectangle_concept::contains(rect1, point); rectangle_concept::contains(rect1, rect2); contains(rect1, point); contains(rect1, rect2); } output from calling foo(): rectangle contains point rectangle contains rectangle rectangle contains point rectangle contains rectangle