|
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