Boost logo

Boost :

From: Peter Dimov (pdimov_at_[hidden])
Date: 2002-01-04 08:20:22


From: "Beman Dawes" <bdawes_at_[hidden]>
> I've started to look at the Loki SmartPtr to see if it can supply the
> features we've discussed in the past.
[...]

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

[...]

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

This kind of hierarchy has its benefits. On MSVC 7b2, this E:

struct A {};
struct B {};
struct C {};
struct D { void * p; };
struct E: A, B, C, D {};

has a size of 8, whereas this E:

struct A {};
struct B: A {};
struct C: B {};
struct D: C { void * p; };
struct E: D {};

has a size of 4.

But it's not a general solution to the policy communication problem. I don't
see how StoragePolicy<T> can access ConversionPolicy<> in the above example.

An alternative is to use the Coplien/Barton-Nackman idiom and pass the
SmartPtr to each policy:

#include <assert.h>

template<class T, class P> class default_storage
{
public:

 explicit default_storage(T * p = 0): p_(p) {}

 T * get() const
 {
  return p_;
 }

private:

 T * p_;
};

template<class T, class P> class assert_check
{
public:

 static void check(T const * p)
 {
  assert(p != 0);
 }
};

template<class T, class P> class default_access
{
public:

 T * operator->() const
 {
  P const * this_ = static_cast<P const *>(this); // access parent

  T * p = this_->get();

  this_->check(p);

  return p;
 }

 T & operator*() const
 {
  return *this->operator->();
 }
};

template<

 class T,
 template<class, class> class StoragePolicy,
 template<class, class> class CheckingPolicy,
 template<class, class> class AccessPolicy
>
class smart_ptr:

 public StoragePolicy<T, smart_ptr<T, StoragePolicy, CheckingPolicy,
AccessPolicy> >,
 public CheckingPolicy<T, smart_ptr<T, StoragePolicy, CheckingPolicy,
AccessPolicy> >,
 public AccessPolicy<T, smart_ptr<T, StoragePolicy, CheckingPolicy,
AccessPolicy> >

{
};

typedef smart_ptr<int, default_storage, assert_check, default_access> S;

int main()
{
    S s;
    *s;
}

As default_access<>::operator-> demonstrates, this allows policies to access
the "parent" SmartPtr class; and since SmartPtr is derived from all
policies, a policy can then upcast to another policy, if a private
communication is necessary.

The other fundamental problem - that some policy combinations are
nonsensical (array_access with intrusive_storage) - has three major
solutions:

1. Do nothing.
2. Compile-time assertions in the parent class.
3. A generator class that makes it impossible to get at the nonsensical
combinations.

BTW the code above did compile under MSVC 7b2(!).

--
Peter Dimov
Multi Media Ltd.

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