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

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

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

  template<>
  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_with_size<N>
  smart_ptr_traits_helpers(T*,
    type<typename ct_if<my_trait<T>::value,
                        T,
                        do_not_unify>::type>);

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

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

  #define BOOST_SMART_PTR_POLICY COM_policy
  # include <boost/smart_ptr/register_policy.hpp>
  #undef BOOST_SMART_PTR_POLICY

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

  template<>
  struct smart_ptr_policy<BOOST_SMART_PTR_POLICY_ID>
  {
    typedef BOOST_SMART_PTR_POLICY type;
  };

  #if BOOST_SMART_PTR_POLICY == 1
  # undef BOOST_SMART_PTR_POLICY
  # define BOOST_SMART_PTR_POLICY 2
  #elif BOOST_SMART_PTR_POLICY == 2
  # undef BOOST_SMART_PTR_POLICY
  # define BOOST_SMART_PTR_POLICY 3
  #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.

Agreed.

        Doug


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