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