Boost logo

Boost :

From: Dan Nuffer (dnuffer_at_[hidden])
Date: 2001-12-29 14:18:35


Beman Dawes wrote:

>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
>

Recently I faced the same sort of problem when making the multi_pass
iterator of Spirit into a policy-based class. multi_pass is similar in
a lot of way to Loki's SmartPtr. I ran into the problem where one
policy needed to acess member functions of another policy. I solved the
problem by changing this:

ValueT dereference()
{
  if (queuePosition == queuedElements->end())
  {
    return get_input(); // here's the problem! This policy can't access get_input b/c it's part of the InputPolicy
  }
  else
  {
    return *queuePosition;
  }
}

to this:

        template <typename MultiPassT>
        static ValueT dereference(MultiPassT const& mp)
        {
            if (mp.queuePosition == mp.queuedElements->end())
            {
                return mp.get_input();
            }
            else
            {
                return *mp.queuePosition;
            }
        }

So, I basically switched the functions that needed to acess other
policies to static template member functions that were passed a
reference to the main class.
The disadvantage of this approach is that I had to make some parts of
the policies (such as get_input() ) public that I would have rather kept
private, but I couldn't think of a good solution.

You can see the multi_pass code here:
<http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/spirit/spirit/boost/multi_pass.hpp?rev=1.31&content-type=text/vnd.viewcvs-markup>

--Dan Nuffer


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