intrusive_ptr design question

I was wondering from a design perspective, why was it decided that intrusive_ptr should rely on user-defined free functions intrusive_ptr_add_ref and intrusive_ptr_release to handle the reference counting? This makes it somewhat inconvenient, for some constant type T, to support having two different instances of intrusive_ptr<T>, each of which use a different reference counting strategy. If there's a clever way to allow this that I'm missing please advise.

2009/7/5 Zachary Turner <divisortheory@gmail.com>:
I was wondering from a design perspective, why was it decided that intrusive_ptr should rely on user-defined free functions intrusive_ptr_add_ref and intrusive_ptr_release to handle the reference counting? This makes it somewhat inconvenient, for some constant type T, to support having two different instances of intrusive_ptr<T>, each of which use a different reference counting strategy.
I'm rather confused why you'd want to keep 2 different reference counts in an object. As a workaround though, why not just use struct T1 : T {}; and struct T2 : T {}; if you need 2 distinct kinds?

On Sun, Jul 05, 2009 at 07:58:49PM -0500, Zachary Turner wrote:
I was wondering from a design perspective, why was it decided that intrusive_ptr should rely on user-defined free functions intrusive_ptr_add_ref and intrusive_ptr_release to handle the reference counting?
One of the main reasons for intrusive_ptr is to have smart pointers to third-party classes that already have their own refcount. Since you can't add a member to a class you don't control, using member functions to manipulate the refcount would rule out that use. The expectation in this case would be that the user would write these two free functions to just call the appropriate member functions on the third-party class.
This makes it somewhat inconvenient, for some constant type T, to support having two different instances of intrusive_ptr<T>, each of which use a different reference counting strategy.
If there's a clever way to allow this that I'm missing please advise.
I must admit, I'm not really sure what you are trying to achieve. It sounds like you have some instances of T, and you want some of them to store their reference count in some way, and the rest to have a completely unrelated way of storing their reference count. But if this is the case, it sounds like you actually have two different types, and Scott's solution sounds right; and even so, I don't know what you hope to achieve by that. -- "It must be accepted as a principle that the rifle, effective as it is, cannot replace the effect produced by the speed of the horse, the magnetism of the charge, and the terror of cold steel." -- British Cavalry training manual, 1907 ::: http://surreal.istic.org/

On Mon, Jul 6, 2009 at 2:04 AM, Daniel Hulme<st@istic.org> wrote:
On Sun, Jul 05, 2009 at 07:58:49PM -0500, Zachary Turner wrote:
I was wondering from a design perspective, why was it decided that intrusive_ptr should rely on user-defined free functions intrusive_ptr_add_ref and intrusive_ptr_release to handle the reference counting?
One of the main reasons for intrusive_ptr is to have smart pointers to third-party classes that already have their own refcount. Since you can't add a member to a class you don't control, using member functions to manipulate the refcount would rule out that use. The expectation in this case would be that the user would write these two free functions to just call the appropriate member functions on the third-party class. But why not just allow the intrusive_ptr to accept those free functions as constructor arguments, allowing me to pass different free functions for different instances?
This makes it somewhat inconvenient, for some constant type T, to support having two different instances of intrusive_ptr<T>, each of which use a different reference counting strategy.
If there's a clever way to allow this that I'm missing please advise.
I must admit, I'm not really sure what you are trying to achieve. It sounds like you have some instances of T, and you want some of them to store their reference count in some way, and the rest to have a completely unrelated way of storing their reference count. But if this is the case, it sounds like you actually have two different types, and Scott's solution sounds right; and even so, I don't know what you hope to achieve by that.
That is, in fact, the main problem I see. I don't think reference counting *should* be tied to a particular type. I guess the original set of use cases for intrusive_ptr consisted of exactly 1 use case. Namely, "allow smart pointer wrapping of objects which provide their own reference count". But I guess I'd like to use it for a slightly different purpose: allowing smart pointer wrapping of objects with a customizable reference counting scheme. I guess the name "intrusive_ptr" really makes it sound like it's more the original use case, but there's nothing else that really comes close ot fitting the bill, and intrusive_ptr would serve very nicely if it allowed one to specify the reference counting functions on a per-instance level. Imagine, for example, you wanted single-threaded and multi-threaded versions of a reference counting function. Obviously you'd want to use the single threaded version whenever possible since it avoids the overhead of acquiring / releasing a mutex to udpate the reference count. But occasionally you might really need a multi-threaded ref-counted pointer. In this case you could simply construct it with different ref-counting functions. For that particular example, it even fits into the original intrusive_ptr use case where you're just changing *how* you modify the original object's reference count, instead of changing what you modify. Scott's workaround is ok, but it's definitely not going to scale as it will result in combinatorial growth. Maybe what I'm really after is an implementation of shared_ptr that allows one to provide the shared_count implementation?

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 06 July 2009, Zachary Turner wrote:
One of the main reasons for intrusive_ptr is to have smart pointers to third-party classes that already have their own refcount. Since you can't add a member to a class you don't control, using member functions to manipulate the refcount would rule out that use. The expectation in this case would be that the user would write these two free functions to just call the appropriate member functions on the third-party class.
But why not just allow the intrusive_ptr to accept those free functions as constructor arguments, allowing me to pass different free functions for different instances?
Because you'd have to store 2 function pointers in every intrusive_ptr object? Why don't you just store the function pointers in your class? Then for example intrusive_ptr_add_ref can call some member function of your class, which in turn calls its object's internal function pointer. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkpSCNoACgkQ5vihyNWuA4UQ8QCeIx4/EO6K8wWBFhe6ZRKzNAMC Zf8AoNRlsNgAqoJvpHiHtGB0XKQEjqqr =wGgt -----END PGP SIGNATURE-----

On Mon, Jul 6, 2009 at 9:23 AM, Frank Mori Hess<frank.hess@nist.gov> wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Monday 06 July 2009, Zachary Turner wrote:
One of the main reasons for intrusive_ptr is to have smart pointers to third-party classes that already have their own refcount. Since you can't add a member to a class you don't control, using member functions to manipulate the refcount would rule out that use. The expectation in this case would be that the user would write these two free functions to just call the appropriate member functions on the third-party class.
But why not just allow the intrusive_ptr to accept those free functions as constructor arguments, allowing me to pass different free functions for different instances?
Because you'd have to store 2 function pointers in every intrusive_ptr object? Why don't you just store the function pointers in your class? Then for example intrusive_ptr_add_ref can call some member function of your class, which in turn calls its object's internal function pointer.
You shouldn't have to store 2 function pointers for every intrusive_ptr object. You should only have to store 2 function pointers per instance pointed to, and then 1 reference to some shared structure in each intrusive_ptr object. Much like shared_ptr currently does. Also, maybe the object is part of some library that I didn't write, but I still want to provide reference counting for it through some mechanism other than that which is provided by shared_ptr.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 06 July 2009, Zachary Turner wrote:
You shouldn't have to store 2 function pointers for every intrusive_ptr object. You should only have to store 2 function pointers per instance pointed to, and then 1 reference to some shared structure in each intrusive_ptr object. Much like shared_ptr currently does.
You'd still need a pointer in every intrusive_ptr object to point at the shared_structure, plus you'd have to do dynamic allocation of the shared object. That is never going to fly. - From the "main reasons to use intrusive_ptr" section of its documentation: "The memory footprint of intrusive_ptr is the same as the corresponding raw pointer;" -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkpSExoACgkQ5vihyNWuA4UXtwCeOYuF/Jdap1ajhsd7rI0e9bYa wY8AoMxu8bHr8+brBAMq+cGF+Ndkt2Nb =WNH1 -----END PGP SIGNATURE-----

On Mon, Jul 6, 2009 at 10:07 AM, Frank Mori Hess<frank.hess@nist.gov> wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Monday 06 July 2009, Zachary Turner wrote:
You shouldn't have to store 2 function pointers for every intrusive_ptr object. You should only have to store 2 function pointers per instance pointed to, and then 1 reference to some shared structure in each intrusive_ptr object. Much like shared_ptr currently does.
You'd still need a pointer in every intrusive_ptr object to point at the shared_structure, plus you'd have to do dynamic allocation of the shared object. That is never going to fly.
- From the "main reasons to use intrusive_ptr" section of its documentation:
"The memory footprint of intrusive_ptr is the same as the corresponding raw pointer;"
Ok, so let me rephrase then: Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?

Zachary Turner wrote:
Ok, so let me rephrase then: Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
I haven't followed your discussion closely, so I may have the wrong idea, but don't you run the risk of mixing the different ref counting techniques on objects of the same type? If the ref counting scheme isn't part of the smart pointer type, then all instances must do ref counting the same way. IOW, if all instances are my_ref_counter<T>, then having some manipulate the ref count of T's one way and others doing it another means that you can reference the same T instance from different my_ref_counter<T>'s which can manipulate the ref count differently. Put that in context of thread safety and you can see how a non-thread safe my_ref_counter<T> can wreak havoc on thread code that expects a T's ref count to be manipulated by thread safe my_ref_counter<T>s. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Mon, Jul 6, 2009 at 10:32 AM, Stewart, Robert<Robert.Stewart@sig.com> wrote:
Zachary Turner wrote:
Ok, so let me rephrase then: Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
I haven't followed your discussion closely, so I may have the wrong idea, but don't you run the risk of mixing the different ref counting techniques on objects of the same type? If the ref counting scheme isn't part of the smart pointer type, then all instances must do ref counting the same way.
That's correct. The way I have envisioned is analagous to the way shared_ptr allows you to specify a custom deleter. Whenever a copy is made, the new copy gets the same deleter. That way all copies agree on the deletion semantics. As long as all copies of the shared pointer that refer to the same underlying instance of pointed-to object agree on reference counting semantics, everything should be ok right?

Zachary Turner wrote:
On Mon, Jul 6, 2009 at 10:32 AM, Stewart, Robert<Robert.Stewart@sig.com> wrote:
Zachary Turner wrote:
Ok, so let me rephrase then: Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
I haven't followed your discussion closely, so I may have the wrong idea, but don't you run the risk of mixing the different ref counting techniques on objects of the same type? If the ref counting scheme isn't part of the smart pointer type, then all instances must do ref counting the same way.
That's correct. The way I have envisioned is analagous to the way shared_ptr allows you to specify a custom deleter. Whenever a copy is made, the new copy gets the same deleter. That way all copies agree on the deletion semantics. As long as all copies of the shared pointer that refer to the same underlying instance of pointed-to object agree on reference counting semantics, everything should be ok right?
Deleting with the wrong deleter is an obvious failure which is likely to crash an application, but only happens once per object. Manipulating a reference count, tens, hundreds, or more times, exposes a much greater danger. Yes, copies will agree on how to manipulate the reference count, but the same memory can be referenced by separately constructed, non-copied smart pointer instances. _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Mon, Jul 6, 2009 at 10:50 AM, Stewart, Robert<Robert.Stewart@sig.com> wrote:
Zachary Turner wrote:
That's correct. The way I have envisioned is analagous to the way shared_ptr allows you to specify a custom deleter. Whenever a copy is made, the new copy gets the same deleter. That way all copies agree on the deletion semantics. As long as all copies of the shared pointer that refer to the same underlying instance of pointed-to object agree on reference counting semantics, everything should be ok right?
Deleting with the wrong deleter is an obvious failure which is likely to crash an application, but only happens once per object. Manipulating a reference count, tens, hundreds, or more times, exposes a much greater danger. Yes, copies will agree on how to manipulate the reference count, but the same memory can be referenced by separately constructed, non-copied smart pointer instances.
Could you elaborate a bit more? I'm not sure I understand. Of course it's up to the programmer to specify correct reference counting functions and construct shared_ptr instances correctly or else the behavior will be undefined. Are you saying that the problem would be with something like this? T* t = new T(); shared_ptr<T> ptr_t(t, multi_threaded_ref_counter()); shared_ptr<T> ptr_t2(t); Causing the instance to be deleted twice eventually? If so, how is that different from: T* t = new T(); shared_ptr<T> ptr_t(t); shared_ptr<T> ptr_t2(t); Or for that matter, from: T* t = new T(); shared_ptr<T> ptr_t(t); delete t; Currently all copies of a shared_ptr agree on reference conting strategy because they are all hardcoded to use boost::detail::shared_count. If they all use foo::bar::mt_shared_count, or better yet if shared_count could be parameterized with a template such as: namespace boost { namespace detail { template<class ref_counter> class shared_count { }; where ref_counter is a class that provides a static interface guarantee such as: struct ref_counter { long add_ref(); long release(); }; then there wouldn't even be any extra space overhead, since these functions could be specified inline by the user. With correct use of default template arguments, shared_ptr could be modified to support this while still allowing current syntax to select the existing shared pointer ref counting scheme, making it backwards compatible. If I misunderstood your statement though, please let me know.

Zachary Turner wrote:
On Mon, Jul 6, 2009 at 10:50 AM, Stewart, Robert<Robert.Stewart@sig.com> wrote:
Zachary Turner wrote:
That's correct. The way I have envisioned is analagous to the way shared_ptr allows you to specify a custom deleter. Whenever a copy is made, the new copy gets the same deleter. That way all copies agree on the deletion semantics. As long as all copies of the shared pointer that refer to the same underlying instance of pointed-to object agree on reference counting semantics, everything should be ok right?
object. Manipulating a reference count, tens, hundreds, or more times, exposes a much greater danger. Yes, copies will agree on how to manipulate the reference count, but the same memory can be referenced by separately constructed, non-copied smart pointer instances.
Could you elaborate a bit more? I'm not sure I understand. Of course it's up to the programmer to specify correct reference counting functions and construct shared_ptr instances correctly or else the behavior will be undefined. Are you saying that
An interface that permits ready misuse is questionable.
the problem would be with something like this?
T* t = new T();
shared_ptr<T> ptr_t(t, multi_threaded_ref_counter()); shared_ptr<T> ptr_t2(t);
Causing the instance to be deleted twice eventually? If so, how
No. More below.
is that different from:
T* t = new T();
shared_ptr<T> ptr_t(t); shared_ptr<T> ptr_t2(t);
That can be found using memory analysis tools which reveal the double delete.
Or for that matter, from:
T* t = new T();
shared_ptr<T> ptr_t(t); delete t;
Likewise. My concern is the following, assuming "counter" is a class that supports your notion of construction time specification of referencing counting: int * x(new int); counter<int> a(x, unsafe_ref_count); // not thread safe ... counter<int> b(x, safe_ref_count); // thread safe In these cases, assume that (un)safe_ref_count package the reference counting logic in some handy way not specified ATM. Note that threaded code assumes it can access x's memory safely, using b, because of using safe_ref_count. Also note that other code can use a to manipulate x's reference count and access x's memory at the same time. That means that b's memory and reference count are not thread safe after all. This same effect can come about in a number of ways. If you copy a or b, the copy will use the same reference counting scheme, of course. However, a and b are the same type, so they can interoperate in ways that are either unexpected or cannot be discovered until runtime. You said (or implied, I don't recall offhand) that you'd supply the increment/decrement ref count functions to the constructor. That means that (un)safe_ref_count actually represent a pair of function pointers. Obviously, that interface precludes optimization opportunities -- at least in many cases -- and permits pairing the wrong functions. You could supply an object with such a pair of functions on it that counter can call. By grouping the functions in a UDT, you eliminate the chance of incorrect pairing and increase the chance of optimization. OTOH, if you embed the reference counting behavior into the type of the smart pointer -- counter<int, safe_ref_counter>, for example -- you reduce the opportunities of different parts of the code using different reference counting behavior for the same memory. The compiler can catch cases of misuse. Furthermore, the policy class groups the function pair in one type, thus eliminating the chance to combine them incorrectly. Boost.SmartPtr currently traffics in counter<int> types (shared_ptr<T>, intrusive_ptr<T>, etc.) and not in counter<int,policy> types, thus safe usage of your idea cannot be added to those types and must be done with a policy class in a new smart pointer type. HTH, _____ Rob Stewart robert.stewart@sig.com Software Engineer, Core Software using std::disclaimer; Susquehanna International Group, LLP http://www.sig.com IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

On Mon, Jul 6, 2009 at 2:31 PM, Stewart, Robert <Robert.Stewart@sig.com>wrote:
You could supply an object with such a pair of functions on it that counter can call. By grouping the functions in a UDT, you eliminate the chance of incorrect pairing and increase the chance of optimization.
OTOH, if you embed the reference counting behavior into the type of the smart pointer -- counter<int, safe_ref_counter>, for example -- you reduce the opportunities of different parts of the code using different reference counting behavior for the same memory. The compiler can catch cases of misuse. Furthermore, the policy class groups the function pair in one type, thus eliminating the chance to combine them incorrectly.
Boost.SmartPtr currently traffics in counter<int> types (shared_ptr<T>, intrusive_ptr<T>, etc.) and not in counter<int,policy> types, thus safe usage of your idea cannot be added to those types and must be done with a policy class in a new smart pointer type.
Thanks for the clarification. I think we basically agree in that case. My original proposal didn't mention anything about a ref counting policy, but I kind of arrived at the conclusion that that was the best way to do it anyway (see towards the end of my previous post). Making major interface changes to existing libraries is always inherently risky but is there any reason that adding a policy to shared_ptr, with a default policy template argument that uses the existing ref counting strategy, would be bad? I can't think of any scenarios that this would break existing code, except maybe if someone was using a shared_ptr to parameterize a class that was expecting a template template parameter, and this seems pretty unlikely. It seems that it would be easier to do this than to design yet another smart pointer class, which would then have to work correctly in the presence of all the other multitude of smart pointer classes. In theory, intrusive_ptr and weak_ptr could then even become special cases of shared_ptr by having boost supply policies implementing weak_ptr / intrusive_ptr reference semantics. Note that I haven't actually thought about that aspect in tremendous detail, but it seems like it could be made to work. Even the requirement of intrusive_ptr to have the same memory footprint of a raw pointer should be do-able if it doesn't instantiate the policy class as a member of the smart pointer.

-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 On Monday 06 July 2009, Zachary Turner wrote:
Making major interface changes to existing libraries is always inherently risky but is there any reason that adding a policy to shared_ptr, with a default policy template argument that uses the existing ref counting strategy, would be bad?
The FAQ of the shared_ptr documentation addresses this question. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkpSaH0ACgkQ5vihyNWuA4VAjQCgkhd0/dlTLHukJxGADLC4DUfG fmcAn27Ggst7A8rQcy/5mgfhnYYVczZT =dcNY -----END PGP SIGNATURE-----

Zachary Turner wrote:
Ok, so let me rephrase then: Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
This is not unreasonable. It can't be done currently because the sp_counted_base members that implement the reference counting are non-virtual and inline.

On Monday 06 July 2009 17:23:41 Zachary Turner wrote:
Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
Maybe it's already there: shared_ptr doesn't care if the passed deleter actually deletes anything. If you now take an object with an existing reference count, you just increment that count and pass the function that decrements it as deleter function to a shared_ptr constructor. This creates a parallel reference count, but it should be safe. Nothing can pull the object from under the shared_ptr's feet because it holds a reference. The shared_ptr doesn't destroy the object either, at most it decrements the reference count. Uli

On Wed, Jul 8, 2009 at 8:21 PM, Ulrich Eckhardt <doomster@knuut.de> wrote:
On Monday 06 July 2009 17:23:41 Zachary Turner wrote:
Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
Maybe it's already there: shared_ptr doesn't care if the passed deleter actually deletes anything. If you now take an object with an existing reference count, you just increment that count and pass the function that decrements it as deleter function to a shared_ptr constructor. This creates a parallel reference count, but it should be safe. Nothing can pull the object from under the shared_ptr's feet because it holds a reference. The shared_ptr doesn't destroy the object either, at most it decrements the reference count.
It isn't already there, because unfortunately, in general, this doesn't work. You need the reference count to increase when the shared pointer is copied, for example. I am of the opinion that this isn't a problem we should solve. Inherently mixing lifetime idioms on a single object instance is dangerous. Interoperation with shared_ptr does not make semantic sense. The only working scenario I can imagine is one where the shared_ptr would behave exactly like the intrusive_ptr. As others have indicated this would increase the memory footprint of shared_ptr since any customisable functor for the reference count increment / decrement would need to a have a reference to the count. Hence I have yet to see a convincing case where the design of such interoperation would be the optimal design, and the implementation overhead does not obey the zero overhead principle.
Uli
Regards, Neil Groves

On Wednesday 08 July 2009 21:33:50 Neil Groves wrote:
On Wed, Jul 8, 2009 at 8:21 PM, Ulrich Eckhardt <doomster@knuut.de> wrote:
On Monday 06 July 2009 17:23:41 Zachary Turner wrote:
Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
Maybe it's already there: shared_ptr doesn't care if the passed deleter actually deletes anything. If you now take an object with an existing reference count, you just increment that count and pass the function that decrements it as deleter function to a shared_ptr constructor. This creates a parallel reference count, but it should be safe. Nothing can pull the object from under the shared_ptr's feet because it holds a reference. The shared_ptr doesn't destroy the object either, at most it decrements the reference count.
It isn't already there, because unfortunately, in general, this doesn't work. You need the reference count to increase when the shared pointer is copied, for example.
There is no "the reference count", but actually two of them. The internal reference count of the object doesn't increase but the external one, shared by the shared pointers. That external one counts how many shared pointers share a single internal reference. // first pointer, increases internal refcount intrusive_ptr<InternallyRefcounted> ptr1(raw_ptr); // increment refcount manually inc_refcount(raw_ptr); // second pointer, living on the above manual reference shared_ptr<InternallyRefcounted> ptr2(raw_ptr, &dec_refcount); Reset either of ptr1 or ptr2. For the intrusive_ptr, it would decrement the internal reference count. For the shared_ptr, it would decrement its own reference count and if that reaches zero, it would also decrement the object's internal one as part of invoking the deleter. Should work, unless I'm missing something. Also, I'm not 100% sure that this is what the OP is actually looking for. Uli

On Sunday, July 12, 2009, Ulrich Eckhardt <doomster@knuut.de> wrote:
On Wednesday 08 July 2009 21:33:50 Neil Groves wrote:
On Wed, Jul 8, 2009 at 8:21 PM, Ulrich Eckhardt <doomster@knuut.de> wrote:
On Monday 06 July 2009 17:23:41 Zachary Turner wrote:
Is there a case to be made for a more flexible shared_ptr (either through modification or through a new class addition) that allows one to provide custom reference counting semantics much the same way that shared_ptr allows one to provide custom deletion semantics?
Maybe it's already there: shared_ptr doesn't care if the passed deleter actually deletes anything. If you now take an object with an existing reference count, you just increment that count and pass the function that decrements it as deleter function to a shared_ptr constructor. This creates a parallel reference count, but it should be safe. Nothing can pull the object from under the shared_ptr's feet because it holds a reference. The shared_ptr doesn't destroy the object either, at most it decrements the reference count.
It isn't already there, because unfortunately, in general, this doesn't work. You need the reference count to increase when the shared pointer is copied, for example.
There is no "the reference count", but actually two of them. The internal reference count of the object doesn't increase but the external one, shared by the shared pointers. That external one counts how many shared pointers share a single internal reference.
// first pointer, increases internal refcount intrusive_ptr<InternallyRefcounted> ptr1(raw_ptr);
// increment refcount manually inc_refcount(raw_ptr); // second pointer, living on the above manual reference shared_ptr<InternallyRefcounted> ptr2(raw_ptr, &dec_refcount);
Reset either of ptr1 or ptr2. For the intrusive_ptr, it would decrement the internal reference count. For the shared_ptr, it would decrement its own reference count and if that reaches zero, it would also decrement the object's internal one as part of invoking the deleter.
Should work, unless I'm missing something. Also, I'm not 100% sure that this is what the OP is actually looking for.
Uli
It does work. I've used this technique before with some internally ref counted that I inherited. Tony

On Mon, Jul 6, 2009 at 11:07 AM, Frank Mori Hess<frank.hess@nist.gov> wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
On Monday 06 July 2009, Zachary Turner wrote:
You shouldn't have to store 2 function pointers for every intrusive_ptr object. You should only have to store 2 function pointers per instance pointed to, and then 1 reference to some shared structure in each intrusive_ptr object. Much like shared_ptr currently does.
You'd still need a pointer in every intrusive_ptr object to point at the shared_structure, plus you'd have to do dynamic allocation of the shared object. That is never going to fly.
- From the "main reasons to use intrusive_ptr" section of its documentation:
"The memory footprint of intrusive_ptr is the same as the corresponding raw pointer;"
I'm not sure if this does what the OP wants, but the ref counting could be a policy: template <typename T> default_intrusive_ptr_refcount_policy { static void add_ref(T * t) { // by default call the global function intrusive_ptr_add_ref(t); } static void remove_ref(T * t) { // by default call the global function intrusive_ptr_remove_ref(t); } }; template intrusive_ptr<typename T, typename RefCountPolicy = default_intrusive_ptr_refcount_policy<T> > { // intrusive_ptr details... // on add ref, we call RefCountPolicy::add_ref(t) // on remove ref, we call RefCountPolicy::remove_ref(t) }; // examples: struct Foo { ... }; struct Bar { ... }; intrusive_ptr<Foo> pFoo; // usual intrusive_ptr, same as always struct BarRefCountPolicy1 { static add_ref(Bar * bar) { // add ref to bar some way }; static remove_ref(Bar * bar) { // remove ref some way } }; struct BarRefCountPolicy2 { static add_ref(Bar * bar) { // add ref to bar some *pther* way }; static remove_ref(Bar * bar) { // remove ref some *other* way } }; intrusive_ptr<Bar, BarRefCountPolicy1> pBar1; intrusive_ptr<Bar, BarRefCountPolicy2> pBar2; For better or worse... ? Tony
participants (9)
-
Daniel Hulme
-
Frank Mori Hess
-
Gottlob Frege
-
Neil Groves
-
Peter Dimov
-
Scott McMurray
-
Stewart, Robert
-
Ulrich Eckhardt
-
Zachary Turner