
Hi Lucanus,
Fernando wrote:
Nitpicking here, but, why is set() forwarding to set_() instead of doing this itself?
It has to do with maintainability. You see, I can easily change the design pattern because it is abstracted away from its usage. Only private functions of the PointConcept call the PointConceptMap functions, and I group those private functions together at the end of the class definition. Public functions only call the private functions or eachother and don't need to be changed if the design pattern or PointConceptMap implementation changes.
But private functions do need to be changed in this case. How is it better to change the *implementation* of private vs public functions? Is not that the private functions are out of line in a library. I would understand it if the private functions did not match the interface of the public ones... but doing it the way you did it is IMO over-engineering. It's a good idea to boil down to private method(s) to implement a public one, but not if ALL you do is forward all the work to a SINGLEe INLINE private function that takes exactly the same parameters.
OK, you showed us how the pattern works. So, as a user I need to implement PointConceptMap<> and PointConcept<>, is that right? That seems like a lot of unnecesary work to me just to call a library. So, let me start with a concrete review now that I see some actual stuff. Why do I need to implement both PointConceptMap and PointConcept??
You don't, that is the point of the pattern. I implement PointConcept once for all points and the user only needs to implement the PointConceptMap for their type. My real PointConcept class has at least a hundred member functions plus stream operators.
Ha OK. Yes, this is an important detail. So PointConcept is provided by the library and the user just exposes the map.
The PointConceptMap, in constrast, has only three functions. The idea is that the PointConcept gives a gratuitous, rich, and highly productive API, factoring out everything a person might do with a point or pair of points and providing those things as member functions.
Right. We all agree that the library needs to provide a rich high level API on top of just a few fundamental operations provided by the user.
The PointConcept is the library, as far as points go.
Couldn't foo() be implemented like this instead? template <class T> void foo(T& t) { PointConceptMap<T>::PointSet(t ,HORIZONTAL ,PointConceptMap<T>::PointGet(t,VERTICAL) }
Of course, but then I would need to reimplement foo if I changed the interface.
If what changed **exactly**? Let's see: What foo() does is to "set the horizontal coordinate of "t" to a value equal to its vertical coordinate" This requires the point type to support set(point,orienation) get(point,orientation) this operations are defined by the point concept. The only different between using the PointConceptMap directly instead of PointConcept, for this example, is in the syntax of those operations. So, considering this specific example: Could I change the adapter functions (the map) such that they don't provide Get or Set? NO. If I did that then MyPoint just couldn't be interfaced with foo a all. But I think you just picked the wrong example to show your point. A point I agree with: it is a good idea to require the user to provide just fundamental operations and let the framework compose the more advanced ones on top of that. But again, you can do just the same with the more conventional BGL-style approach to generic programming. You don't need monolithic classes, unusual casts and duplication (there are 3 sets of get/set functions in your example!) to do it.
template <class T> void foo(T& t) { PointSet(t,HORIZONTAL,PointGet(t,VERTICAL); }
Yes, that is pretty much the starting point for generic programming. I started there and added classes, abstractions and indirection
You are not adding "more" or "better" abstractions just because you added classes As for indirection, I'm not sure what that means,
You can easily argue that I didn't *need* to do anything different from the simple, well know generic programming techniques.
Right, I can easily argue that. And I'm doing it since it seems to me that you have over-engineered the adaptation design. Let's see... Considering *only* that simplistic example (getting and setting coordinates of a point), would you agree that free templates functions are all you need and there isno justification do so anything more complex than that? Assuming, yes... When does something more complex starts being neccesary? A user can supply only fundamental operations (like those in your PointConceptMap) via free functions. And the library can certainly build a richer point-related functionality on top of that, also using free template functions and lightweigth traits. Consider that one upside of free fucntions is that they are the opposite of monolthic. A user I can override any level of the adaptation layer if she wants to, because function matching is resolved at the point of call, so is easy to override any function in your API to better much my legacy type, if I need to.
There are a hundred different ways to skin a cat.
But there are lots of people using way "A" and you come in with *your* way "B" (or should that be "L" ;) I'm OK with innovation, but you do need to show us why B is better. Or at the very least, why isn't it worse. Best Fernando