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
> 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
> ( deal with the issue by using a
> nested inheritance hierarchy (see page 612 for example). So the form
> 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

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

 T * get() const
  return p_;


 T * p_;

template<class T, class P> class assert_check

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

template<class T, class P> class default_access

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

  T * p = this_->get();


  return p;

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


 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;

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

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

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

Peter Dimov
Multi Media Ltd.

Boost list run by bdawes at, gregod at, cpdaniel at, john at