Boost logo

Boost :

From: Douglas Gregor (gregod_at_[hidden])
Date: 2002-04-19 07:51:57

On Thursday 18 April 2002 11:56 pm, you wrote:
> >My general feeling is that there needs to be something like
> 'boost::smart_ptr<T>' which is appropriately configured for the
> specific
> needs of type 'T' (eg. intrusive vs. external counters are probably
> type specific), to application wide preferences (eg. multi-threading
> support is likely to be configured application wide - at least for
> most
> cases), and otherwise useful defaults.<
> The problem here is that you don't want to specialize smart_ptr<T> for
> all types that, for example, use COM's intrusive reference counting.
> There are many such types in the application and you don't really want
> to configure smart_ptr separately for each.

But suppose that you didn't need to configure smart_ptr individually for each
type. Would you then agree with the premise, that the type of reference
counting is almost always dependent on 'T' for 'smart_ptr<T>'?

If I have a type T with intrusive reference counting, I would expect
shared_ptr<T> to use that intrusive reference counting. If T is an open FILE,
I expect shared_ptr<T> to have an external reference count and know how to
fclose() the file when the count drops to zero. Having to specify the policy
along with the type _seems_ like it could be error-prone. If I'm supposed to
use 'smart_ptr<MyCOMObject, COMRefCountPolicy>' and I accidentally type
'smart_ptr<MyCOMObject>', bad things will happen.

But nobody wants to specialize a smart_ptr_traits for every single class.
However, reference counting is a property that is always inherited by derived
classes. We could massage the type traits notion a little so that we use
overloading instead of partial specialization: then base classes like
IUnknown can be taken into account so that anything derived from IUnknown
uses the COM reference counting mechanism.

We could have something like this:

  template<typename T>
  type_with_size<1> smart_ptr_traits_helper(void*, type<T>);

  template<typename T>
  type_with_size<2> smart_ptr_traits_helper(IUnknown*, type<T>);

  type_with_size<3> smart_ptr_traits_helper(FILE*, type<FILE>);

Then the return value sizes perform the mapping over to a reference counting

  struct smart_ptr_policy<1> { typedef external_ref_count_policy type; };

  struct smart_ptr_policy<2> { typedef COM_policy type; };

  struct smart_ptr_policy<3> { typedef FILE_policy type; };

So for smart_ptr<T>, the policy generator used is:

  smart_ptr_policy<sizeof(smart_ptr_traits_helper((T*)0, type<T>()))>::type

smart_ptr_traits_helper has two parameters so that one can easily check for a
base class (via the base pointer conversion), an exact type (type<T>), or an
arbitrary predicate:
  template<typename T>
    type<typename ct_if<my_trait<T>::value,

I don't have a wonderful way to register new reference counting policies, but
I do have an ugly hack:

First, let BOOST_SMART_PTR_NEXT_POLICY_ID be a macro that will be
initialized to 1 by the smart_ptr header. Then we can add a new policy like

  template<typename T>
  smart_ptr_traits_helper(IUnknown*, type<T>);

  # include <boost/smart_ptr/register_policy.hpp>

boost/smart_ptr/register_policy.hpp will add the appropriate specialization,
and then increment BOOST_SMART_PTR_NEXT_POLICY_ID:

  struct smart_ptr_policy<BOOST_SMART_PTR_POLICY_ID>
    typedef BOOST_SMART_PTR_POLICY type;

  #elif ...

I told you it was ugly :). Even so, it makes it possible to drop the explicit
reference counting policy parameter, and so long as your #includes are
consistent, smart_ptr<T> will always have the right policy.

> The threading policy is not application-wide; there are places when
> you want, and places when you don't want, to share smart pointers
> between threads.



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