Re: [boost] Deterimining interest in: Optional Polymorphic Value object with Small Object optimization

[sorry if resend--the first posting attempt (via gmail) seems to have failed] On Oct 20, 2007 1:15 PM, Corrado Zoccolo <czoccolo@gmail.com> wrote:
Hi Simon
On 10/20/07, Simon Francis <thejello@gmail.com> wrote:
http://opensource.adobe.com/classadobe_1_1poly.html
I already knew adobe poly, when I started writing optional_poly. The aim
is similar, but the approach is completely different. With Adobe poly, you have to design the interfaces, and write "concept maps" to specifically to work with it.
Example, from Adobe poly test:
struct poly_measurable_interface : adobe::poly_copyable_interface { virtual double size() const = 0;
virtual ~poly_measurable_interface() { } };
/*************************************************************************** **********************/
// This class template implements the abstract measurable interface in terms of a type T that // Models the MeasurableConcept
template <typename T> struct poly_measurable_instance : adobe::optimized_storage_type<T, poly_measurable_interface>::type { ADOBE_CLASS_REQUIRE(T, , MeasurableConcept);
poly_measurable_instance(const T& x) : adobe::optimized_storage_type<T, poly_measurable_interface>::type(x) {}
poly_measurable_instance(poly_measurable_instance& x, adobe::move_ctor m) : adobe::optimized_storage_type<T, poly_measurable_interface>::type(x, m) {}
double size() const { return MeasurableConcept<T>::size(this->get()); } };
/*************************************************************************** **********************/
// Another boilerplate class, measurable serves as a template parameter to the poly<> machinery // tying together the interface and implementation above.
struct measurable : adobe::poly_base<poly_measurable_interface, poly_measurable_instance> { // No delegating constructors (yet), so we call base constructor manually template <typename T> explicit measurable(const T& s) : adobe::poly_base<poly_measurable_interface, poly_measurable_instance>(s) { }
measurable(measurable& x, adobe::move_ctor m) : adobe::poly_base<poly_measurable_interface, poly_measurable_instance>(x, m) {}
// No forwarding in C++, so we do it manually double size() const { return interface_ref().size(); } };
typedef adobe::poly<measurable> poly_measurable;
Thanks to all this boilerplate code, you can use the usual dot notation to access the members of the class.
My approach is different. You don't have to change the interfaces to use optional_poly (so it is non intrusive with existing hierarchies), and you don't need all this boilerplate code, but you have to use the -> operator to access members of the class.
[snip] -------------- There have been a few posts on the boost lists recently mentioning the adobe::poly library. Since the poly documentation is still somewhat incomplete, except for a few research-orineted papers, I thought I'd give a bit more detail here, to help clarify the differences between poly<> and possibly similar libraries. You correctly note that the adobe::poly library requires more machinery to be provided by certain stakeholers. It does not, however, intrude into existing concrete classes. To better understand where the adobe::poly library lives in the design continuum, I'll consider a possible manner in which a piece of code may over its lifetime. I'll start with a (toy example of a) class that automatically sizes and positions widgets from a particular GUI library inside of a dialog. In this scenario, users of the layout engine first "append" widgets to the layout collection. Once all the widgets have been appended, the user can ask the engine to "solve" the layout. In order to solve the layout, the engine will call a measure function on each widget to obtain its desired extents and other layout constraints. When the layout is solved, the engine informs the widgets of their final location by invoking the place(Widget, extents_t) member function: // // Code evolution: stage 1-- //layout engine operating on aspecific concrete type class Widget {/*...*/}; // API-specific widget class class extents_t {/*...*/}; // encapsulates screen object size and location struct layout_engine { void append(Widget placeable) { placeables_m.push_back(placeable); } void solve() { extents_m.resize(placeables_m.size()); for(int i = 0; i != placeables_m.size(); ++i) measure(placeables_m[i], extents_m[i]); // ommitted: code to solve layout constraints // and update place_data_m (ommitted) for(int i = 0; i != placeables_m.size(); ++i) place(placeables_m[i], place_data_m[i]); } vector<extents_t> extents_m; vector<Widget> placeables_m; }; Version #1 of the engine above is designed to work on a single specific Widget type. Later in life, this code is to be ported to other platforms. One approach to generalization is to templatize the layout engine on the widget type. The code for the layout engine doesn't need to change. // // Code evolution: stage 2 --lifting the engine // (at compile time) using templates // template <class T> struct layout_engine { void append(T placeable) { placeables_m.push_back(placeable); } void solve() { extents_m.resize(placeables_m.size()); for(int i = 0; i != placeables_m.size(); ++i) measure(placeables_m[i], extents_m[i]); // ommitted: code to solve layout constraints // and update place_data_m (ommitted) for(int i = 0; i != placeables_m.size(); ++i) place(placeables_m[i], place_data_m[i]); } vector<extents_t> extents_m; vector<T> placeables_m; }; Version #2 of the code (above) assumes that measure and place operations with the right signatures are implemented for the T widget type. To improve usability of our class template, we use the boost::concept_check library to improve error messages in case the required measure and place operations are not present (not shown here). In the test code for adobe::poly that you cited above, you can see some of this machinery (ADOBE_CLASS_REQUIRE). We go even further simulating the C++ '0x concept_map feature through the use of operations qualified by MeasurableConcept. But this is not really poly<>-specific so I won't say more about it now. For the next stage of our code's evolution, we wish be able to use the layout engine in contexts where the widget type is not known until runtime. Since we don't wish to intrude on our concrete widget types to make them inherit from a base class, the usual object-oriented abstract base class approach doesn't appeal. Such an approach might not even be possible, given that we may not control the source code for the widget classes. One principle applied in the design of the poly<> library was that the ability for an object to behave polymorphically is not one that should be introduced/paid for at design-time. Instead, we support a pay-as-you-go plan at the point of polymorphic use. The adobe::poly library is a toolkit designed to facilitate the task of providing non-intrusive value-based wrappers that allow concrete classes to be used in a runtime polymorphic manner, without change of interface. We could retain the previous version of the layout_engine unchanged, instantiating it on the poly_placeable runtime polymorphic wrapper when we want runtime polymorphic behavior, and we could instantiate the layout engine "library" on a concrete type in applications where the widget type is known at compile time. For the runtime-polymorphic wrapper case this would simply look like: // Code evolution: stage 3 -- instantiating the layout_engine class on // the runtime polymorphic wrapper type poly_placeable layout_engine<poly_placeable> e; As an alternate use case (e.g. for dynamic libraries), we sometimes emphasize the runtime polymorphic usage by promoting/de-templatizing the layout_engine class template into a plain (non-templated) class: struct layout_engine { void append(poly_placeable placeable); void solve(); vector<extents_t> extents_m; vector<poly_placeable> placeables_m; }; The current version of the poly library assumes two different kinds of clients. The first kind of client (the wrapper author) is the provider of a poly "wrapper", such as the poly_measurable wrapper above. This client uses the poly library to reduce the burden of producing a particular non-intrusive polymorphic value type. Such clients must provide: an abstract base class specifying the required/available operations, (this is the role of poly_measurable_interface in the poly_test code), a class template that implementing the abstract interface in terms of concrete types that provide offer the appropriate signature (this is the role of poly_measurable_instance in the poly_test example), and a small glue class that holds the previous two artifacts together (measureable in the adobe_test example). Once those three artifacts have been written, the poly<> wrapper can be instantiated: typedef adobe::poly<measurable> poly_measurable; // in the poly_test example Poly offers a number of other features (small object optimization, move semantics using the NRVO-based adobe::move library, etc.) but I won't discuss those further. At this point the responsibilities of the first kind of poly client, the wrapper author, are complete. The second kind of client is the end user of classes such as poly_placeable. In the evolving code example above, we can look briefly at how the code that calls the layout engine evolves. The stage 1 calling sequence might look like: void place(widget, extents_t); //#0 exents_t measure(widget, extents_t); //#0 //... layout_engine e; // #1 void layout_dialog() { Widget widget1, widget2; //append some widgets: e.append(widget1); //#2 e.append(widgetn); //#3 //... e.solve(); } At stage 2, we might change #0 and #1: template <typename Widget> inline void place(poly_placeable p, extents_t ext) { p.place(ext); } //#0 template <typename Widget> inline exents_t measure(poly_placeable p , extents_t ext) { return p.measure(ext); } //#0 layout_engine<Widget> e; //#1' In keeping with our emphasis upon non-intrusiveness, the layout engine in this example makes use of free-function syntax when invoking place and measure. In the version 1 calling code, we provided overloads for the concrete widget type. In the version 2 calling code we provide function templates instead. The primary function templates above provide default implementations that delegate to member functions. For widget types that do not provide the necessary member functions we must specialize place<> and measure. At stage 3, we might also need to change #1 once more to layout_engine<poly_placeable> e; //#1'' In the conceptC++ version of poly, or in a version in which poly_constructors were non-explicit, no further changes would be required. In the current C++ '03 version, poly<> has an explicit constructor, so we also change lines #2 and #3 to: e.append(poly_placeable(widget1)); //#2'' e.append(poly_placeable(widgetn)); //#3'' I haven't touched on other adobe::poly features such as the assignability across different poly<> types, dynamic dispatch and dynamic_cast that poly can provide, but I've probably already said more than anyone wanted to hear about it in this thread. More info can be found at the following locations: http://opensource.adobe.com/ For the adobe source library (including poly) http://www.emarcus.org/papers/MPOOL2007-marcus.pdf http://www.emarcus.org/papers/MPOOL2007-slides-marcus.pdf # Runtime Polymorphic Generic Programming--Mixing Objects and Concepts in ConceptC++ Mat Marcus, Adobe Systems, Inc., Jaakko Järvi, Texas A&M University, Sean Parent, Adobe Systems, Inc. From: The 6th International Workshop on Multiparadigm Programming with Object-Oriented Languages at the ECOOP 2007. and a little bit here: http://www.speakeasy.org/~mmarcus/papers/gpce2007f-authors-version.pdf # Library Composition and Adaptation using C++ Concepts Jaakko Järvi, Texas A&M University, Mat Marcus, Adobe Systems, Inc., Jacob N. Smith, Texas A&M University. From: Proceedings of the 6th international conference on Generative programming and component engineering, 2007. Copyright ACM, 2007. This is the author's version of the work. It is posted here by permission of ACM for your personal use. Not for redistribution. The definitive version was published in the Proceedings of the 6th International Conference on Generative Programming and Component Engineering (Salzburg, Austria, October 01 - 03, 2007). GPCE '07 http://doi.acm.org/10.1145/1289971.1289984 - Mat
participants (1)
-
Mat Marcus