Boost logo

Boost :

From: Fernando Cacciola (fcacciola_at_[hidden])
Date: 2002-04-19 13:28:00


"Gennadiy Rozental" <rogeeff_at_[hidden]> wrote in message
news:a9pjah$oog$1_at_main.gmane.org...
>
> "Fernando Cacciola" <fcacciola_at_[hidden]> wrote in message
> news:a9pfv7$cb7$1_at_main.gmane.org...
> > Just my 2 cents...
> >
>
> > Scenario 1: I want to write a class which does something with a given
> > source object. This class needs to keep the object alive, but it is
quite
> > possible that the caller disposes the object even before this class
> > completes its task. This scenario calls for a common pattern:
> >
> > class Processor
> > {
> > Processor ( smart_ptr<A> const& obj ) : m_obj(obj) {}
> > ...
> > smart_ptr<A> m_obj ;
> > } ;
> >
> >
> > The important thing in this example is that all I really know of is A,
and
> > that I need it to be alive as long as Proccesor needs it.
> > If smart_ptr<> is intrusively-policy-based, I -the user of A- must make
> > decisions about which of those policies would fit here. Some of those
> > decisions might be hard to take on this side of the system, but even if
> can
> > take them, there is still this problem:
> > Suppose I do:
> >
> > Processor ( smart_ptr<A,Policy1,Policy2> const& obj ) : m_obj(obj) {}
> >
> > what happens with the caller?
> >
> > Likely, there is a smart_ptr<> holding A objects on the caller side
> already.
> > If the caller has a different (in terms of policies) smart_ptr, he could
> > probably -given the appropriate machinery- do a conversion, but this is
> > unlikely to scale well, so, most likely, Processor will eventually end
up
> > being parametrized:
> >
> > template<class APtr>
> > class Processor
> > {
> > Processor ( APtr const& obj ) : m_obj(obj) {}
> > ...
> > APtr m_obj ;
> > } ;
> >
> > These, however, intoduces a much higher engineering complexity to the
> > system, because we have changed a non-template class to a template class
> > only because the smart_ptr is overparametrized (from the pov of the user
> of
> > A who just wants to keep it alive).
>
> No. I disagree. Real problem here you want to reuse you Processor
facility,
> but do not know storage type. There as usual two choises runtime
> polimorphizm - which I hardly believe you can accept, or compile-time one
> with we usually express in form of template parameters. And FWIW it does
not
> "intoduces a much higher engineering complexity".
>
But it does. Remember that Processor IS NOT A TEMPLATE class, and turing a
non-template class into a template class *is* a very complex enginnering
change. For the simplest of the reasons, if you change the implemention of a
template class you need to recompile the world because exporting templates
is not currently available.

> >
> > Scenario 2:
> >
> > It is quite common to specialize certain things -such as predicates- on
a
> > smart_ptr<>. This is in order to allow certain expressions to be applied
> > indistinctely to bare and smart pointers.
> > For example, the boost shared-ptr declares:
> >
> > // get_pointer() enables boost::mem_fn to recognize shared_ptr
> > template<typename T> inline T * get_pointer(shared_ptr<T> const & p) ;
> >
> > This is used -as the comment says- inside mem_fn which allows a very
> > powerful usage of mem_fn.
> >
> > If shared_ptr were intrusively policy-based, this wouldn't be feasible
> > becuase mem_fn couldn't know about the smart pointer policies.
>
> Why? Can't you write another predicate like this:
>
> template<typename StoragePolicy,...> inline StoragePolicy::pointer_type
> get_pointer(smart_ptr<....> const & p) ;
>
mem_fn doesn't know about the different policies introduced in the complete
type it sees. So this would only work as long as there is one get_pointer
specialization for each of the possible policy combinations.

Now, figure the whole picture:
You set up your own set of policies to customize your own CoolPrt<>... nice,
just a straight-forward assembly of strategies.
.. but then you start to *discover* -as the compiler-errors start to appear-
that you need to specialize get_pointer() for your CoolPtr(), and you also
need to specialize swap(), shared_dynamic_cast(), shared_static_cast(),
my_smart_pointer_fancy_tool(), etc, etc, etc....

> >
> >
> > So....
> >
> > Just as is the case with optional<>, a smart pointer is a library fature
> so
> > general that the ability to express a smart pointer as a
single-parameter
> > template class: smart_ptr<T> is so powerful that directly competes with
> the
> > power of the flexibility given by a policy-based class.
> >
> >
> > Intrusive vs external policies:
> >
> > Since the real problem is not the usage of policies as compile-time
> > strategic-drivers for the smart ptr, but instrumenting the approach
using
> > intrusive policies, it appears to me that the obvious solution is to use
> > external policies, as optional<> does.
> >
> > An external policy is just like any other policy template class, except
> that
> > is is identified by a fixed template-id.
> > A good example of it is std::iterator_traits<>. This is an external
policy
> > class, with a fixed template-id, so you don't need to pass it as an
> > argument.
> The thing is that for given iterator type most probably you using always.
> the same iterator_traits, while this is not the case for smart_ptr
policies
> (with rare exclusions)
>
> >
> > Of course, external policies are a lot less flexible than their
intrusive
> > counterparts.
> >
> The whole point of policy-based design is it's flexibility.
>
Sure, but the idiom of expressing a smart ptr as a single-parameter template
class is in my opinion -and in the opinion of Dietmar too- a fundamental
part of the flexibility of a smart ptr.

> > Dietmar already shown -and I totally agree- that most of the smart ptr
> > policies are usually type dependent, so they perfectly call for external
> > policies. (BTW, the sketch made by Doug seems very reasonable).
>
> I disagree here. What is type dependent? Specifically?
>
The count strategy (intrusive (either COM based or else) or external)
The deallocation strategy is most of the times type-dependent.

These, along with the threading policy, are IMO the three most practical
strategic choices for a *general* smart pointer.

You can still use a Loki style SmartPtr for uncommon needs. We are talking
here about a general smart ptr.

And again... we are not against policy-based designs.. we are just trying to
figure out different ways to specify the policies; in search of one that
doesn't clutter the interface that a user would like to use.

--
Fernando Cacciola
Sierra s.r.l.
fcacciola_at_[hidden]
www.gosierra.com
>
> > --
> > Fernando Cacciola
> > Sierra s.r.l.
> > fcacciola_at_[hidden]
> > www.gosierra.com
> >
>
> Gennadiy.
>
>
>
>
>
> _______________________________________________
> Unsubscribe & other changes:
http://lists.boost.org/mailman/listinfo.cgi/boost
>

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