Boost logo

Boost :

From: Beman Dawes (bdawes_at_[hidden])
Date: 2001-12-29 12:50:13


I've started to look at the Loki SmartPtr to see if it can supply the
features we've discussed in the past.

Loki source code is available at
http://cseng.aw.com/book/0,,0201704315,00.html

The features we've discussed are documented in
http://groups.yahoo.com/group/boost/files/smart_pointers/smart%20ptr%20feature%20diagram.htm

One consensus view from past discussions was that there are a large number
of ways to refactor a feature model, and no one way is likely to be
overwhelmingly superior. Thus Loki's factoring of smart pointer features
into four policies isn't an issue.

Three features which Loki doesn't currently provide seem obvious candidates
for additional policy implementations:

* An invasive reference counted ptr.

* Arrays.

* A shared ptr which copes with cyclic references.

Adding an OwnershipPolicy for an invasive reference counted pointer seems
trivial. I wrote the code in a few minutes. No issues surfaced. (I
haven't tested it, but it's so simple it can't possibly fail. Famous last
words:-)

I didn't try to add an OwnershipPolicy which could cope with cycles; I
don't know enough about it.

I tried to add an array StoragePolicy. That seems really difficult,
although only because of issues with the current implementation rather than
because of any conceptual problem with a policy based smart pointer.

Here are the issues I ran into:

* The natural set of access functions for arrays is different from
non-arrays, and interacts with the ConversionPolicy (some details omitted
for brevity):

                           | non-array | array |
   ------------------------+----------------+------------------------+

   No implicit conversion | T& operator*() | T& operator [](size_t) |
                           | T* operator->()| |
   ------------------------+----------------+------------------------+

   Implicit conversion | T& operator*() | operator
T*() |
                           | T* operator->()| |
                           | operator T*() | |
   ------------------------+----------------+------------------------+

Unless I'm missing something, this is very hard to implement given the
current relationship between the policy classes and the framework that
inherits them. The problem is that the framework assumes a certain
interface (op* and op->) is provided by the StoragePolicy. The MC++D book
(p 19) suggests an added ElementAt() interface, but that seems a step
backward to me, and also suffers from the next (error checking)
problem.

* A StoragePolicy or Ownership policy which adds an additional
dereferencing function to the interface has no way to invoke the
OnDereference function of the CheckingPolicy.

* Some Ownership policies, such as for an invasive reference counted
pointer, need to disallow arrays. The appropriate enums to communicate
is_array and disallow_array need to be added to StoragePolicy and
OwnershipPolicy, and a compile time assert needs to be added to framework.

The pattern that emerges from these issues is that there is a lot of
negative fallout from a design decision to enforce policy orthogonality
(p19) by using multiple inheritance in the framework class.

In other words, the inheritance form is thus:

     template <
         typename T,
         template <class> class OwnershipPolicy,
         class ConversionPolicy,
         template <class> class CheckingPolicy,
         template <class> class StoragePolicy >
     class SmartPtr
         : public StoragePolicy<T>
         , public OwnershipPolicy<typename StoragePolicy<T>::PointerType>
         , public CheckingPolicy<typename StoragePolicy<T>::StoredType>
         , public ConversionPolicy
     { ...

That means that the SmartPtr framework class itself must handle all
interactions between policies since they don't know about each other. That
limits the policies to interactions foreseen when the framework was
developed, and requires the framework to know a lot about the interfaces
provided by the policies. It means the policies can't have private
understanding between themselves, among other things.

Czarnecki and Eisenecker in Generative Programming
(http://www.generative-programming.org/) deal with the issue by using a
nested inheritance hierarchy (see page 612 for example). So the form might
be something like this:

     template <
         typename T,
         template <class> class OwnershipPolicy,
         template <class> class ConversionPolicy,
         template <class> class CheckingPolicy,
         template <class> class StoragePolicy >
     class SmartPtr
         : public ConversionPolicy <
             typename OwnershipPolicy <
               typename CheckingPolicy <
                 typename StoragePolicy<T> > > >
     { ...

C&E use several variations, including self-referral via typedefs, but the
key point is that the framework not longer has to mediate all interactions
between the policies. Doing this with the Loki SmartPtr would seem to
reduce the complexity of the framework and allow more flexibility in the
interface functions which can be delivered by the policies. Downside would
be reduced othogonality between policies, although since policy
requirements have to be carefully specified anyhow, I'm not sure that is
much of a practical disadvantage.

Comments? Am I missing some obvious resolutions for the issues noted
above?

--Beman


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