Boost logo

Boost :

From: Joel de Guzman (joel_at_[hidden])
Date: 2007-10-08 19:11:16


Simonson, Lucanus J wrote:
> Marco wrote:
>> If I can ask, why do you preferred subclassing instead of composition?
>> Could 'b' be a member of 'a' instead?
>> Perhaps to access protected members of class b otherwise inaccessible?
>> In another part of your thread you say inerithance is public just
>> because of a limitation in MS compiler, so your intention would be to
>> use private inerithance, so you don't think at 'a' as is 'b' but in is
>> implemented in terms of 'b', is this correct?
>
> This is the kind of question I wanted asked. Gyuszi suggested
> composition early on. I don't really have a good rationale for choosing
> inheritance over composition. At the time, private inheritance was
> working fine in gcc 4.2.0 and I was able to make the design pattern
> using inheritance from the template parameter do everything I wanted. I
> didn't need to access protected members of b. You are correct that I
> could have made the design pattern work equally well, perhaps better,
> using composition instead of inheritance. All that I need is for a to
> be able to safely cast to b and visa-versa. I specifically made this
> type of code change easy to make by abstracting away the mechanism for
> accessing the data, but what benefit would I get from changing the
> design pattern to use composition instead of inheritance? It just isn't
> clear to me what it is about inheritance that is objectionable in this
> case. Perhaps this is a learning opportunity for me.

Erm, I do not want to be an antagonist here but, you don't need
both inheritance and composition to model concepts. They can
be useful tools, sure, but they should not be required for
modeling concepts.

Take STL, for instance, you have a few concepts for containers and
sequences (Forward Container, Random Access Container, Associative
Container, to name a few). std::vector, std::list, std::set, etc.
are all models of these concepts. Surely, you don't need to do
anything (neither inherit nor compose some concept classes) to
write these classes or for the third party to write his own.

Same is true with points. A model of a point is just a model of
a point. It need not inherit from anything nor be composed to
get the desired model of the Point concept. If an object
satisfies the Point model, then all algorithms that work on
points work AS-IS, regardless where the point object came from;
out of the box from the library, supplied by third party,
user supplied, etc. The key here is: AS-IS.

Take GIL, for instance. It has this point concept:
http://opensource.adobe.com/gil/html/gildesignguide.html#PointSectionDG

Now, as long as I model this concept(*** see below) correctly, I
should be able to use my home grown point in my library as a GIL
point without doing anything.

(*** GIL's point concept is intrusive though in that it requires
some member functions. A better strategy would be to use customization
points: free functions and metafunctions; whereby allowing legacy
classes to be adapted non-intrusively.)

In Fusion, a library for working with heterogeneous collections of
data (http://tinyurl.com/7ra5g), we use concepts too to model
containers and views. I've shown in a previous post that I can
adopt any struct to make it usable AS-IS in fusion. Concept wise,
a struct is a tuple. Out of the box, boost::tuple, boost::array,
boost::pair are all adapted. So, I can write:

     std::pair<int, short> p(1, 2);
     fusion::for_each(p, cout << _1);

and:

     boost::tuple<int, short> t(1, 2);
     fusion::for_each(t, cout << _1);

and:

     boost::array<int, 2> a(1, 2);
     fusion::for_each(a, cout << _1);

without having to wrap p, t and a in some adaptor classes or
concept map classes. All adaptation is done non-intrusively.
You use p, t and a AS-IS without modification, without
wrapping. Customization points take care of the mapping.

I could take a legacy point, POINT, and adapt it to fusion.
Then, I can write:

     Legacy::POINT lp(1 ,2);
     fusion::for_each(lp, cout << _1);

or even bi-directional:

     void PrintPoint(const Legacy::POINT&); // supplied by Legacy

     fusion::for_each(lp, bind(&PrintPoint, _1));

Again, no fuss with inheritance and composition. It just works.

------------------

Ok, nuff said. I hope I made myself clear.

Regards,

-- 
Joel de Guzman
http://www.boost-consulting.com
http://spirit.sf.net

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk