c++: smart auto_ptr

Boris Kolpackov boris at kolpackov.net
Fri Apr 2 15:27:06 CST 2004


Good day,


Here is the problem. We want to write template class auto_ptr
that supports both invasive and non-invasive reference counting.
Here is an example:

  struct counter {};

  struct foo : counter {};

  struct bar {};

  auto_ptr<foo> foo_p; // uses invasive counter
  auto_ptr<bar> bar_p; // uses non-invasive counter


Of course we don't want to limit ourselves to only one predefined
type of invasive counter. In other words this should work too:


  struct my_counter {};

  struct baz : my_counter {};

  auto_ptr<baz> baz_p; // uses invasive my_counter


So how do we go about implementing this? The first idea may be to
specialize auto_ptr for different invasive counters and leave non-
specialized implementation to handle the non-invasive case:


  template <typename x>
  struct auto_ptr
  {
    auto_ptr ()
    {
      cerr << "non-invasive" << endl;
    }
  };


  template <>
  struct auto_ptr<counter>
  {
    auto_ptr ()
    {
      cerr << "counter" << endl;
    }
  };


  template <>
  struct auto_ptr<my_counter>
  {
    auto_ptr ()
    {
      cerr << "my_counter" << endl;
    }
  };

Looks good, right? The only problem with this approach is
that it doesn't work. Consider, for example, this:


  auto_ptr<bar> bar_p;


Which specialization will be used? The truth is that the
primary non-specialized version will be used. Why? Because
parameterization inhibits conversion. In our case c++ prefers
instantiation of the primary template with x = bar to implicit
conversion of bar to counter and instantiation of specialization
with x = counter. And this makes sense.

So does it mean we cannot do what we want? The answer is
we can but we will need far more trickier meta-programming
techniques and a c++ compiler that supports typeof keyword
(e.g. GNU g++ or Intel c++).

Here is how we can do this. The main task is to write a
type function that for each argument X returns either


 * a type from predefined (but open for extensions) set of
   types if x is implicitly-convertible to one of them

 * x otherwise


Such type function will allow us to distinguish between
all invasive cases and the non-invasive case. Suppose we
have such a function:

  template <typename x>
  struct type
  {
    typedef ... r;
  };

How can we use it to achieve what we want? Here is how. First
of all we will need a helper class that will encapsulate counter
management. We will call it a counter management unit or cmu:

  template <typename x>
  struct cmu;

Now we will use cmu in auto_ptr implementation and that's
where we take advantage of the type function: we instantiate
cmu with the invasive counter type, not x (well, it will be
x if it is the non-invasive case).

  template <typename x>
  struct auto_ptr
  {
    cmu<typename type<x>::r> cmu_;
  };

Do you see how everything falls into places? Now we can
specialize cmu for whatever counter type we want:

  template <>
  struct cmu<counter>
  {
    cmu ()
    {
      cerr << "counter" << endl;
    }
  };

The trickiest part is the type function. I guess it is easier
to show the code since it speaks for itself.


  template <typename x>
  x test (...);

  template <typename>
  counter test (counter*);

  template <typename x>
  struct type
  {
    typedef typeof (test<x> (reinterpret_cast<x*> (0))) r;
  };

The technique is explained in details in both "C++ Templates"
and "Modern C++ Design".

In order to extend our design with a new counter type we need to
do two things: add another test function and specialize cmu.


  template <typename>
  my_counter test (my_counter*);

  template <>
  struct cmu<my_counter>
  {
    cmu ()
    {
      cerr << "my_counter" << endl;
    }
  };

Let's now put everything together and see how it work:

  int
  main ()
  {
    auto_ptr<int> i_p;
    auto_ptr<bar> bar_p;

    auto_ptr<counter> c_p;
    auto_ptr<foo> foo_p;

    auto_ptr<my_counter> m_p;
    auto_ptr<baz> baz_p;
  }

This program prints the following

  non-invasive
  non-invasive
  counter
  counter
  my_counter
  my_counter

which is what we would expect. Attached is the complete source
code.

hth,
-boris

-------------- next part --------------
A non-text attachment was scrubbed...
Name: test.cxx
Type: text/x-c++src
Size: 1311 bytes
Desc: not available
Url : http://kolpackov.net/pipermail/notes/attachments/20040402/e11e5f38/test.cxx

-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 652 bytes
Desc: Digital signature
Url : http://www.kolpackov.net/pipermail/notes/attachments/20040402/e11e5f38/attachment.bin


More information about the notes mailing list