Topic: Additional reasons for improving TR1 Smart Pointers
Author: dave@boost-consulting.com (David Abrahams)
Date: Sun, 9 Oct 2005 22:00:36 GMT Raw View
cppljevans@cox-internet.com (Larry Evans) writes:
> Another space optimization for shared_ptr<T>,
> where T is something that has only single inheritance,
> would be elimination of the:
>
> X* sp_counted_impl_p<X>::px
>
> defined here:
>
> http://cvs.sourceforge.net/viewcvs.py/boost/boost/boost/detail/sp_counted_impl.hpp
You can't eliminate that in portable code even when you know it's
single inheritance. The standard doesn't guarantee that base classes
have the same address as derived classes under any circumstances.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Sun, 9 Oct 2005 22:00:50 GMT Raw View
"Peter Dimov" <pdimov@gmail.com> writes:
> Lance Diduck wrote:
>
>> > Using shared_ptr as a deleter is a good workaround, but has alot of
>> > overhead.
>
>> But couldn't one derive an implementation of shared_ptr that didn't
>> have this overhead for this particular use case? I can see that this is
>> an important use case that should be supported, but it appears that the
>> existing standard can support it with little contortion --i.e. it may
>> be better presented as an idiom in "Effective TR1." rather than as a
>> fifth type of smart pointer in the standard
>> (auto_ptr,auto_ptr_ref,shared_ptr, weak_ptr).
>
> (repost)
>
> The main drawback of using the deleter for shared_ptr aliasing is not
> the overhead, but the fact that the weak pointers to the alias expire
> too early (when all copies of the alias are gone, rather than when all
> aliases are gone.)
>
> You don't need a fifth type to support aliasing, the proposal just adds
> one additional constructor to shared_ptr.
At Mont Tremblant it was resolved that *if* we added such an interface
extension it should not be as a member of shared_ptr but as a free
function. There were concerns about how easy it would be to do
something unsafe.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Peter Dimov" <pdimov@gmail.com>
Date: Mon, 10 Oct 2005 10:32:12 CST Raw View
David Abrahams wrote:
> At Mont Tremblant it was resolved that *if* we added such an interface
> extension it should not be as a member of shared_ptr but as a free
> function. There were concerns about how easy it would be to do
> something unsafe.
I am not sure that I agree with the resolution. It is very easy to do
something unsafe with some of the shared_ptr constructors, so it makes
sense for an additional unsafe operation to also be expressed as a
constructor.
The aliasing support is a fundamental shared_ptr operation that makes
it possible for the casts to be implemented using only the public
interface. The logical place of fundamental operations is within the
class.
In addition, a shared_ptr created by the aliasing constructor is no
more unsafe than its raw pointer argument - or a shared_ptr constructed
by the same raw pointer argument and a null deleter. It can only
prolong the lifetime of its alias and does not introduce additional
safety issues.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Mon, 10 Oct 2005 20:49:21 GMT Raw View
"Peter Dimov" <pdimov@gmail.com> writes:
> David Abrahams wrote:
>
>> At Mont Tremblant it was resolved that *if* we added such an interface
>> extension it should not be as a member of shared_ptr but as a free
>> function. There were concerns about how easy it would be to do
>> something unsafe.
>
> I am not sure that I agree with the resolution.
Understood. And, to be fair, I think "resolved" is way too strong. I
should have said, "the consensus in the room seemed to be that...".
> It is very easy to do something unsafe with some of the shared_ptr
> constructors, so it makes sense for an additional unsafe operation
> to also be expressed as a constructor.
>
> The aliasing support is a fundamental shared_ptr operation that makes
> it possible for the casts to be implemented using only the public
> interface. The logical place of fundamental operations is within the
> class.
>
> In addition, a shared_ptr created by the aliasing constructor is no
> more unsafe than its raw pointer argument - or a shared_ptr constructed
> by the same raw pointer argument and a null deleter. It can only
> prolong the lifetime of its alias and does not introduce additional
> safety issues.
For what it's worth, I think the idea was that, if you stay away from
deleters and you properly initialize the shared_ptr with a dynamically
allocated pointer to the most-derived class, and you don't create
cycles, you get a guarantee of safety. Hmm, that's quite a few "ifs,"
though, isn't it?
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Tue, 11 Oct 2005 03:55:04 GMT Raw View
David Abrahams wrote:
> For what it's worth, I think the idea was that, if you stay away from
> deleters and you properly initialize the shared_ptr with a dynamically
> allocated pointer to the most-derived class, and you don't create
> cycles, you get a guarantee of safety. Hmm, that's quite a few "ifs,"
> though, isn't it?
How about "if you don't include some "unsafe" include file,
you get a guarantee of safety? That might actually work.
John Nagle
Animats
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: Mon, 10 Oct 2005 22:55:59 CST Raw View
>>if you stay away from deleters and you properly initialize the shared_ptr with a dynamically
>>allocated pointer to the most-derived class, and you don't create cycles, you get a guarantee of safety.
In D&E, Dr. Stroustrup made essentially the same argument why there is
no "placement delete." To paraphrase, at the point of construction we
know all there is to know about the object referred to by the pointer,
at the time of deletion, we know almost nothing. Try implementing a
placement delete sometime in user code -- and after a few months you'll
see why it was done this way -- possible to implement, nearly
impossible to test and reason about.
An intrusive implementation of shared_ptr mitigates some of these
problems, and I prefer such an implementation when using factory
methods returning pointers to abstract classes--where you don't know
the most derived object until runtime. The type knows that it is ref
counted (or whatever gc method) and the smart_ptr knows it is handling
a object meant to be managed in such a way. Millions of programmers
years in sytems like Java, COM, etc shows that this works -- in fact
C++ works better because at least the destructor is always called in
application program flow.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Wed, 28 Sep 2005 03:14:13 GMT Raw View
"jwat" <jwaterloo@idvelocity.com> writes:
> Now as per my original commemts for using aliasing. The following
> example demonstrates using aliased shared_ptr to obliterate cyclical
> references. From a client perspective they only deal with shared_ptr
> when in reality parent objects own child objects. Note how weak_ptr
> were not used at all.
But that isn't anything like your original example, where you were
storing 3 shared_ptrs in your Nodes.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: 28 Sep 2005 13:50:05 GMT Raw View
>Using shared_ptr as a deleter is a good workaround, but has alot of overhead.
But couldn't one derive an implementation of shared_ptr that didn't
have this overhead for this particular use case? I can see that this is
an important use case that should be supported, but it appears that the
existing standard can support it with little contortion --i.e. it may
be better presented as an idiom in "Effective TR1." rather than as a
fifth type of smart pointer in the standard
(auto_ptr,auto_ptr_ref,shared_ptr, weak_ptr). I give seminars to jr
developers sometimes, and to them (and me sometimes) the current menu
is quite overwhelming, and this doesn't count the Microsoft extensions.
If we needed a standards addition for every flavor we'll soon have
mark_sweep_ptr, uses_different_allocators_shared_ptr,
this_use_case_is_faster_shared_ptr, intrusive_ref_counted_shared_ptr,
works_with_COM_shared_ptr, and so on.
I would think we need to see a few more implementations of
tr1::shared_ptr rather than just the reference version, see where that
leads us in usage and implementation problems, and then adjust the
standard if needed. (Personally I would like to see a policy-based
shared_ptr, an am working on getting my stuff into the community for
review, and then perhaps I'll make a proposal for a extension if things
work out.)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Axter" <google@axter.com>
Date: Fri, 30 Sep 2005 23:42:22 CST Raw View
Lance Diduck wrote:
> >Using shared_ptr as a deleter is a good workaround, but has alot of overhead.
> But couldn't one derive an implementation of shared_ptr that didn't
> have this overhead for this particular use case? I can see that this is
> an important use case that should be supported, but it appears that the
> existing standard can support it with little contortion --i.e. it may
> be better presented as an idiom in "Effective TR1." rather than as a
> fifth type of smart pointer in the standard
> (auto_ptr,auto_ptr_ref,shared_ptr, weak_ptr). I give seminars to jr
> developers sometimes, and to them (and me sometimes) the current menu
> is quite overwhelming, and this doesn't count the Microsoft extensions.
> If we needed a standards addition for every flavor we'll soon have
> mark_sweep_ptr, uses_different_allocators_shared_ptr,
> this_use_case_is_faster_shared_ptr, intrusive_ref_counted_shared_ptr,
> works_with_COM_shared_ptr, and so on.
> I would think we need to see a few more implementations of
> tr1::shared_ptr rather than just the reference version, see where that
> leads us in usage and implementation problems, and then adjust the
> standard if needed. (Personally I would like to see a policy-based
> shared_ptr, an am working on getting my stuff into the community for
> review, and then perhaps I'll make a proposal for a extension if things
> work out.)
An optional custom deleter should be possible for shared_ptr with
little to no overhead.
See example code for the following smart pointer:
http://code.axter.com/copy_ptr.h
The above clone smart pointer uses a function pointer to provide
deallocation.
If you look at the very bottom of the code, you'll a default
constructor and deallocator function, and then a second function with
an additional template for the allocator.
When the class uses the default function, it doesn't incur the expense
of a custom allocator, and deletion is performed via delete operator.
When a custom allocator is needed, then the secondary function is used
which provides for the custom deletion using less efficient logic.
For shared_ptr, this logic would be more simplified since it only needs
the deletion logic, and does not need allocation logic.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "jwat" <jwaterloo@idvelocity.com>
Date: Fri, 30 Sep 2005 23:42:09 CST Raw View
Gino, thanks for understanding me. I would like to apologize to David
Abrahams and others confused by not clearly making the distinction from
what the user of an api would see versus the internal implementation. I
do hope each sub proposal of N1851 are considered individually so that
the more complex ones such as allocator and managed_ptr don't hinder a
more rapid adoption of the beneficial lower impact proposals of
aliasing and embedded objects.
My final example of the code mixes native pointers, aliased shared_ptr
and even weak_ptr to creating an ideal situation. It would seem that
aliased shared_ptr ensures that the object tree stays around to such an
extent that it is an ideal match for weak_ptr because it ensure that
weak_ptr.lock() returns a valid shared_ptr. The 'weak_ptr' than
benefits the api by allowing one to retreive the shared_ptr of the root
object in order to do further aliasing. Now static methods became
instance methods. The resulting api still only expose shared_ptr to
users.
// ExistingSharedPtrAliasing.cpp : Defines the entry point for the
console application.
//
#include "stdafx.h"
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
//#include <boost/scoped_ptr.hpp>
#include <algorithm>
struct aliasing
{
aliasing(boost::shared_ptr<void> p) : p(p)
{
}
void operator()(void*)
{
p.reset();
}
private:
boost::shared_ptr<void> p;
};
class Node
{
public:
static boost::shared_ptr<Node> CreateRootNode()
{
boost::shared_ptr<Node> n(new Node);
n->rootDocument = n;
return n;
}
~Node()
{
//rootDocument = NULL;
parent = NULL;
std::for_each(children.begin(), children.end(), Node::NodeDeleter);
}
boost::shared_ptr<Node> RootDocument()
{
//boost::shared_ptr<Node> q(rootDocument.lock(),
aliasing(toBeAlias));
return rootDocument.lock();
}
boost::shared_ptr<Node> Parent()
{
boost::shared_ptr<Node> q(parent, aliasing(rootDocument.lock()));
return q;
}
boost::shared_ptr<Node> Child(int index)
{
boost::shared_ptr<Node> q(children[index],
aliasing(rootDocument.lock()));
return q;
}
boost::shared_ptr<Node> appendNewChild()//boost::shared_ptr<Node>
toBeAlias)
{
boost::shared_ptr<Node> q(new Node(this),
aliasing(rootDocument.lock()));
children.push_back(q.get());
return q;
}
protected:
Node()
{
//rootDocument = this;//?
parent = NULL;
}
Node(Node* parent)
{
this->rootDocument = parent->rootDocument;
this->parent = parent;
}
private:
//Node* rootDocument;
boost::weak_ptr<Node> rootDocument;
Node* parent;
std::vector<Node*> children;
static void NodeDeleter(Node* node)
{
delete node;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
long uc1, uc2, uc3, uc4;
boost::shared_ptr<Node> n = Node::CreateRootNode();
n->appendNewChild();
n->appendNewChild();
n->appendNewChild();
boost::shared_ptr<Node> c;
uc1 = n.use_count();
{
c = n->Child(0);
uc2 = n.use_count();
n.reset();
uc3 = c.use_count();
}
uc4 = n.use_count();
return 0;
}
Gino wrote:
> > You probably didn't know this, but the Boost/TR1 shared_ptr already
> > supports aliasing. Just create a deleter that holds a shared_ptr to
> > the object whose lifetime you really _want_ to manage:
>
> Using shared_ptr as a deleter is a good workaround, but has alot of
> overhead. As observed by Howard Hinnant in a previous message in this
> thread, each "aliased" instance would construct it's own count
> structure. The proposed aliasing feature uses the same count structure
> and therefore only requires a reference count change.
>
> > > This complicates code even more because if you
> > > pass a child object to a function that needs to get its parent or root
> > > document than there is a possibility that it will fail because the
> > > reference is a weak pointer. This is where your shared pointer aliasing
> > > can help greatly.
> >
> > I don't see how.
>
> Having a weak_ptr to an object only ensures that the count structure
> remains valid. Aliases have strong references which ensures that the
> object is not destroyed. There are obviously use cases for both
> mechanisms.
>
> >
> > > Shared pointer aliasing can reduce the proliferation
> > > of weak_ptr thus making the code easier to use with more consistent
> > > results. It does this by keeping the entire document alive while the
> > > client using it is accessing any piece of it. This works when all the
> > > nodes in the object graph are sharing the same reference count. When
> > > there are no more references to the root document or any of its [grand]
> > > children it gets deleted taking out everything with it.
> >
> > <snip>
> >
> > > class Node
> > > {
> > > public:
> > > aliased_shared_array<Node> children;// a collection that
> > > owns the objects but whos indexer returns a aliased shared_ptr
> > > aliased_shared_ptr<Node> parent;// a method that returns a
> > > aliased shared pointer
> > > aliased_shared_ptr<Node> rootDocument;// a method that
> > > returns a aliased shared pointer
> > > };
> > >
> > > I don't know if you agree with this assessment.
> >
> > I don't think so. Unless I'm missing something, the references from
> > the children back to the root node still constitute circular
> > references. In a sense, using aliasing just transforms them into
> > "self-cycles" that point directly back at the node where they
> > originate. Try it using the aliasing delter I posted above, and see
> > what happens.
> >
>
> If I understand jwat's intent correctly, and there is a root object
> that owns and is responsible for recursively destroying all nodes in
> the document. Then if we assume that there is a shared_ptr to that
> object which is accessible to all nodes, then we only have to store the
> actual pointer values in each node. We would provide accessors that
> return those pointer values as shared_ptr aliases of the root
> shared_ptr. For example:
>
> class Node
> {
> Node *d_parent_p;
> Node *d_rootDocument_p;
> shared_ptr<void> *d_rootSp_p;
>
> public:
> aliased_shared_array<Node> children();// Not sure how to deal with
> shared_array
> shared_ptr<Node> parent()
> {
> return shared_ptr<Node>(*d_rootSp_p, d_parent_p);
> }
> shared_ptr<Node> rootDocument()
> {
> return shared_ptr<Node>(*d_rootSp_p, d_rootDocument_p);
> }
> };
>
> In this case, any reference to any node in the document, actually has a
> reference to the whole document.
>
>
> > > However, if you do I would imagine any minor change to the TR1 APIs
> > > that would not only increase performance but could greatly improve
> > > the ease of use, should be seriously considered.
> >
> > Well yes, but N1851 does not constitute a minor change. That's not to
> > say it's without merit, of course.
> >
>
> While there are some parts of N1851 that require more effort to
> implement, some very usefull features such as shared_ptr aliasing are
> very simple extensions. We would have no problem with them being
> consider independently.
> -- Ilougino Rocha
>
> ---
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
> [ --- Please see the FAQ before posting. --- ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: cppljevans@cox-internet.com (Larry Evans)
Date: Sat, 1 Oct 2005 17:02:41 GMT Raw View
On 10/01/2005 12:42 AM, Axter wrote:
> Lance Diduck wrote:
[snip]
>>standard if needed. (Personally I would like to see a policy-based
>>shared_ptr, an am working on getting my stuff into the community for
>>review, and then perhaps I'll make a proposal for a extension if things
>>work out.)
>
> An optional custom deleter should be possible for shared_ptr with
> little to no overhead.
Another space optimization for shared_ptr<T>,
where T is something that has only single inheritance,
would be elimination of the:
X* sp_counted_impl_p<X>::px
defined here:
http://cvs.sourceforge.net/viewcvs.py/boost/boost/boost/detail/sp_counted_impl.hpp
> See example code for the following smart pointer:
> http://code.axter.com/copy_ptr.h
>
> The above clone smart pointer uses a function pointer to provide
> deallocation.
[snip describing virtues of copy_ptr.h]
> For shared_ptr, this logic would be more simplified since it only needs
> the deletion logic, and does not need allocation logic.
It would be nice if all these ideas could be merged into the current
attempt at a boost policy_ptr ;)
http://cvs.sourceforge.net/viewcvs.py/boost-sandbox/boost-sandbox/boost/policy_ptr/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Shezan Baig" <shezanbaig2004@gmail.com>
Date: Sat, 1 Oct 2005 12:03:02 CST Raw View
Axter wrote:
>>Using shared_ptr as a deleter is a good workaround, but has alot of overhead.
> An optional custom deleter should be possible for shared_ptr with
> little to no overhead.
The overhead referred to above is not in the use of a custom deleter,
but in the creation of additional, unnecessary, reference count
structures.
-shez-
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Peter Dimov" <pdimov@gmail.com>
Date: 1 Oct 2005 18:40:01 GMT Raw View
Lance Diduck wrote:
> > Using shared_ptr as a deleter is a good workaround, but has alot of
> > overhead.
> But couldn't one derive an implementation of shared_ptr that didn't
> have this overhead for this particular use case? I can see that this is
> an important use case that should be supported, but it appears that the
> existing standard can support it with little contortion --i.e. it may
> be better presented as an idiom in "Effective TR1." rather than as a
> fifth type of smart pointer in the standard
> (auto_ptr,auto_ptr_ref,shared_ptr, weak_ptr).
(repost)
The main drawback of using the deleter for shared_ptr aliasing is not
the overhead, but the fact that the weak pointers to the alias expire
too early (when all copies of the alias are gone, rather than when all
aliases are gone.)
You don't need a fifth type to support aliasing, the proposal just adds
one additional constructor to shared_ptr.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "jwat" <jwaterloo@idvelocity.com>
Date: Fri, 23 Sep 2005 23:18:07 CST Raw View
I have recently had the privilege of reading N1851, "Improving
Usability and Performance of TR1 Smart Pointers". I believe there is
another one/two compelling reasons for the use of shared pointer
aliasing. Using shared pointer aliasing can reduce cyclical references
and make using properly constructed APIs easier with more consistent
results. Please consider this common real world example. In XML there
is a concept of a node that is the foundation of everything XML. This
node has children, a parent and a root document. Now onto cyclical
references 101, if object a references object b which references object
a, than neither of the objects will get cleaned up. This is the
potential problem with the hierarchical xml model. To break these
references it is recommended that the child nodes have only a weak_ptr
to its parent object. This complicates code even more because if you
pass a child object to a function that needs to get its parent or root
document than there is a possibility that it will fail because the
reference is a weak pointer. This is where your shared pointer aliasing
can help greatly. Shared pointer aliasing can reduce the proliferation
of weak_ptr thus making the code easier to use with more consistent
results. It does this by keeping the entire document alive while the
client using it is accessing any piece of it. This works when all the
nodes in the object graph are sharing the same reference count. When
there are no more references to the root document or any of its [grand]
children it gets deleted taking out everything with it.
// This pseudo-class is easy to use but it has cyclical references.
class Node
{
public:
shared_array<Node> children;
shared_ptr<Node> parent;
shared_ptr<Node> rootDocument;
};
// This pseudo-class is not as easy to use because there is no
guarantee you can reach its parent and root document but it doesn't
have cyclical references.
class Node
{
public:
shared_array<Node> children;
weak_ptr<Node> parent;
weak_ptr<Node> rootDocument;
};
// This pseudo-class is easy to use because the client is only working
with the guarantee of a shared _ptr but has no cyclical references as
there is only one shared counter.
class Node
{
public:
aliased_shared_array<Node> children;// a collection that
owns the objects but whos indexer returns a aliased shared_ptr
aliased_shared_ptr<Node> parent;// a method that returns a
aliased shared pointer
aliased_shared_ptr<Node> rootDocument;// a method that
returns a aliased shared pointer
};
I don't know if you agree with this assessment. However, if you do I
would imagine any minor change to the TR1 APIs that would not only
increase performance but could greatly improve the ease of use, should
be seriously considered.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: dave@boost-consulting.com (David Abrahams)
Date: Sat, 24 Sep 2005 16:28:43 GMT Raw View
"jwat" <jwaterloo@idvelocity.com> writes:
> I have recently had the privilege of reading N1851, "Improving
> Usability and Performance of TR1 Smart Pointers". I believe there is
> another one/two compelling reasons for the use of shared pointer
> aliasing. Using shared pointer aliasing can reduce cyclical references
> and make using properly constructed APIs easier with more consistent
> results.
You probably didn't know this, but the Boost/TR1 shared_ptr already
supports aliasing. Just create a deleter that holds a shared_ptr to
the object whose lifetime you really _want_ to manage:
struct aliasing
{
aliasing(boost::shared_ptr<void> p)
: p(p)
{}
void operator()(void*) { p.reset(); }
private:
boost::shared_ptr<void> p;
};
now:
void f(shared_ptr<Foo> p)
{
shared_ptr<Bar> q(&p->bar, aliasing(foo));
...
}
> This complicates code even more because if you
> pass a child object to a function that needs to get its parent or root
> document than there is a possibility that it will fail because the
> reference is a weak pointer. This is where your shared pointer aliasing
> can help greatly.
I don't see how.
> Shared pointer aliasing can reduce the proliferation
> of weak_ptr thus making the code easier to use with more consistent
> results. It does this by keeping the entire document alive while the
> client using it is accessing any piece of it. This works when all the
> nodes in the object graph are sharing the same reference count. When
> there are no more references to the root document or any of its [grand]
> children it gets deleted taking out everything with it.
<snip>
> class Node
> {
> public:
> aliased_shared_array<Node> children;// a collection that
> owns the objects but whos indexer returns a aliased shared_ptr
> aliased_shared_ptr<Node> parent;// a method that returns a
> aliased shared pointer
> aliased_shared_ptr<Node> rootDocument;// a method that
> returns a aliased shared pointer
> };
>
> I don't know if you agree with this assessment.
I don't think so. Unless I'm missing something, the references from
the children back to the root node still constitute circular
references. In a sense, using aliasing just transforms them into
"self-cycles" that point directly back at the node where they
originate. Try it using the aliasing delter I posted above, and see
what happens.
> However, if you do I would imagine any minor change to the TR1 APIs
> that would not only increase performance but could greatly improve
> the ease of use, should be seriously considered.
Well yes, but N1851 does not constitute a minor change. That's not to
say it's without merit, of course.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: Sat, 24 Sep 2005 14:47:02 CST Raw View
There seems to be some confusion between boost::shared_ptr and
tr1::shared_ptr. FWIW the tr1 shared_ptr interface can easily
accomodate and was intended to accomodate implementations other than
the one found in boost. boost is a great implementation, but now that
the interface has been standardized, we will in the near future see
other implementations. I have mine that we use at our place of
employment, that implements some things that the boost implementors did
not cover..
The boost implementation prefers to use build macros to implement
implementation policies, like allocator. However it is quite easy and
portable to make some minor modifications and move the polymorphism
from the build system to the compiler, e.g.
template<class T, class IMP=detail::shared_count<T,A> > shared_ptr;
where A is perhaps an allocator type, like I want to manage shared
memory objects. Nothing in the standard precludes this.
Nor does the standard preclude adding a "runtime selected"
implementation. The slight interface extension may not port however.
Is it possible to have a tr1 shared_ptr use T itself as the
implementation?? Like an intrusive pointer, or "inplace activation"?
Sure. Possible for a boost 1.30 shared_ptr? no .
Or an implmenentation, like Mr Abrahams suggests, where the deleter
does something funky --like deletes when the app window closes? Sure.
Or an implementation that uses a different locking strategy than what
the boost distribution uses? Absolutley
I have yet to find a reason to change the interface or semantics of the
tr1 shared_ptr. Change the boost implementation -- yes. Change the
interface, no.
This is a sign of a truly great design . Thanks!!!
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: Sat, 24 Sep 2005 14:47:12 CST Raw View
There seems to be some confusion between boost::shared_ptr and
tr1::shared_ptr. FWIW the tr1 shared_ptr interface can easily
accomodate and was intended to accomodate implementations other than
the one found in boost. boost is a great implementation, but now that
the interface has been standardized, we will in the near future see
other implementations. I have mine that we use at our place of
employment, that implements some things that the boost implementors did
not cover..
The boost implementation prefers to use build macros to implement
implementation policies, like allocator. However it is quite easy and
portable to make some minor modifications and move the polymorphism
from the build system to the compiler, e.g.
template<class T, class IMP=detail::shared_count<T,A> > shared_ptr;
where A is perhaps an allocator type, like I want to manage shared
memory objects. Nothing in the standard precludes this.
Nor does the standard preclude adding a "runtime selected"
implementation. The slight interface extension may not port however.
Is it possible to have a tr1 shared_ptr use T itself as the
implementation?? Like an intrusive pointer, or "inplace activation"?
Sure. Possible for a boost 1.30 shared_ptr? no .
Or an implmenentation, like Mr Abrahams suggests, where the deleter
does something funky --like deletes when the app window closes? Sure.
Or an implementation that uses a different locking strategy than what
the boost distribution uses? Absolutley
I have yet to find a reason to change the interface or semantics of the
tr1 shared_ptr. Change the boost implementation -- yes. Change the
interface, no.
This is a sign of a truly great design . Thanks!!!
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: Carl Barron <cbarron413@adelphia.net>
Date: Sat, 24 Sep 2005 22:27:23 CST Raw View
In article <1127587885.810880.322300@g49g2000cwa.googlegroups.com>,
Lance Diduck <lancediduck@nyc.rr.com> wrote:
>
> I have yet to find a reason to change the interface or semantics of the
> tr1 shared_ptr. Change the boost implementation -- yes. Change the
> interface, no.
> This is a sign of a truly great design . Thanks!!!
Sorry your shared_ptr does not even fit the interface of
tr1::shared_ptr. Tr1 states template <class T> class shared_ptr;
not template <class T,class A=foo<T> > class shared_ptr; which is what
your interface requires. The difference was/is signifigant enough to
remove this 'freedom' from STL components. Shared_ptr is not a policy
based smart pointer by design.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: Sun, 25 Sep 2005 11:26:00 CST Raw View
The Standard components do allow for "extra" default parameters. To see
a good article on how to use them read
http://www.cuj.com/documents/s=7994/cujcexp1906alexandr/
But you are right, tr1 states template<class T>shared_ptr. does not
mention a place to put in a template policy. The boost implementors
used build macros instead to make these decisions. Hmm perhaps I should
write my own proposal and lobby the standards committee to get MY
version of shared_ptr in there? Even though no one outside my small
team at work has used it, we have extensive experience with them, so
why not?? OR should I continue to explore these ideas, shared the
experiences with the community at large, and then perhaps make teaks
and suggestions? Which would you prefer?
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: howard.hinnant@gmail.com (Howard Hinnant)
Date: Mon, 26 Sep 2005 03:52:51 GMT Raw View
In article <1127658745.839845.175540@z14g2000cwz.googlegroups.com>,
"Lance Diduck" <lancediduck@nyc.rr.com> wrote:
> The Standard components do allow for "extra" default parameters.
Unfortunately defaulted template parameters are detectable (and thus not
allowed) in the current standard. Consider:
#include <memory>
namespace std
{
// consider the allocator parameter an extension
template <class T, class A = allocator<T> >
struct shared_ptr
{
};
}
template <template <class> class Sp, class T>
void client_code(Sp<T> p)
{
}
int main()
{
client_code(std::shared_ptr<int>());
}
Error : function call 'client_code(std::shared_ptr<int,
std::allocator<int>>)' does not match
'client_code<template<typename T> class __T0, typename __T1>(__T0<__T1>)'
HelloWorld.cp line 20 client_code(std::shared_ptr<int>());
client_code compiles if the defaulted template parameter A is removed.
-Howard
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: cbarron413@adelphia.net (Carl Barron)
Date: Mon, 26 Sep 2005 03:53:29 GMT Raw View
In article <1127658745.839845.175540@z14g2000cwz.googlegroups.com>,
Lance Diduck <lancediduck@nyc.rr.com> wrote:
> The Standard components do allow for "extra" default parameters.
Chapter and verse please.
I am under the impression that this is no longer allowed in the
standard. This is 2005 and I have both c++98
and the current as of jan 2005 draft. I don't have a searchable
modificaion database handy:) Seems that this was solved by removing
this 'liberty'. but don't have the reference handy.
There is discussion here on adding a custom allocator like it adds
deleters, but I think its going nowhere [chicken and egg problem].
note template <class T,class A=foo<T> > class bar; is different from
template <class T> class baz. and template template parameters require
complete specification of all parameters including defaults.
That is
template <template <class> class A> struct do_it
{
template <class T> void func(A<T> &){}
};
then do_it<baz> compiles. but do_it<bar> does not.
propose what you want and give a reference [and os independent]
implimentation, and it might be considered. I am not on the committee,
but in the mean time I suggest you rename it or place in in another
namespace in your code.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Lance Diduck" <lancediduck@nyc.rr.com>
Date: 26 Sep 2005 20:10:06 GMT Raw View
>but in the mean time I suggest you rename it or place in in another namespace in your code.
Who said that it was in std::tr1 namespace???? I'm saying that many of
these ideas can be implemented without changing the interface. I'm
sorry if I got ahead of myself and assumed that default template
parameters were allowed, but that's what a design review is for, eh?
Anyway I'm not proposing any changes to the committee -- the contrary
in fact -- just saying that specialty shared_ptr can have the similar
if not identical interface and semantics.
My shared_ptr is certainly in a different namespace. But the point is
well taken: basic_shared_ptr<T, IMP=blah> and then inheriting from it
to produce std::tr1::shared_ptr<T> may be compliant.
For my work I really don't care if things are in namespace std: just
that the interface and semantics are the same. I leverage the standard.
That is really what I'm getting at. In fact,I avoid using namespace std
stuff for my production code, because of the "duelling STL problems, "
which at my day job has taken mythic porportions.
The shared_ptr version I mentioned is under the copyright of my
employer, and so just can't release it on demand. I have asked that it
can be released.
But thanks for the repiles -- I'll know to try something different than
making default template parameters.
Give me a couple of months -- I'll post it on my website, so you can
give it try if you want.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "jwat" <jwaterloo@idvelocity.com>
Date: Tue, 27 Sep 2005 10:21:09 CST Raw View
You are right I did not know shared_ptr supports aliasing.
However in N1851's defense, I would imaging having a constructor that
provides aliasing support built into shared_ptr would be easier to use
and more efficient since all shared objects would be using the same
shared_count. The N1851 approach is better suited for debugging because
all shared reference shares the same shared_count, aliased shared_ptr
would report the total use_count instead of always reporting 1. [You
can run my example for this fact.] However, as someone who will use
shared_ptr no matter what state it is in, thanks for teaching how to do
aliasing with it.
Now as per my original commemts for using aliasing. The following
example demonstrates using aliased shared_ptr to obliterate cyclical
references. From a client perspective they only deal with shared_ptr
when in reality parent objects own child objects. Note how weak_ptr
were not used at all.
The only things I dislike about my example is I still need to make a
proxy wrapper around a collection of pointers that always provide
smart_ptr when indexed or called in some other way. More critical than
that is to be able to get rid of the proliferation of static methods
and replace them with more intuitive instance methods. If you have any
ideas on how to do that, let me know!
#include "stdafx.h"
#include <vector>
#include <boost/shared_ptr.hpp>
//#include <boost/scoped_ptr.hpp>
#include <algorithm>
struct aliasing
{
aliasing(boost::shared_ptr<void> p) : p(p)
{
}
void operator()(void*)
{
p.reset();
}
private:
boost::shared_ptr<void> p;
};
class Node
{
public:
Node()
{
rootDocument = this;
parent = NULL;
//children.push_back(new Node(this));
//children.push_back(new Node(this));
}
Node(Node* parent)
{
this->rootDocument = parent->rootDocument;
this->parent = parent;
}
~Node()
{
rootDocument = NULL;
parent = NULL;
std::for_each(children.begin(), children.end(), Node::NodeDeleter);
}
static boost::shared_ptr<Node> RootDocument(boost::shared_ptr<Node>
toBeAlias)
{
boost::shared_ptr<Node> q(toBeAlias->rootDocument,
aliasing(toBeAlias));
return q;
}
static boost::shared_ptr<Node> Parent(boost::shared_ptr<Node>
toBeAlias)
{
boost::shared_ptr<Node> q(toBeAlias->parent, aliasing(toBeAlias));
return q;
}
static boost::shared_ptr<Node> Child(boost::shared_ptr<Node>
toBeAlias, int index)
{
boost::shared_ptr<Node> q(toBeAlias->children[index],
aliasing(toBeAlias));
return q;
}
static boost::shared_ptr<Node> appendNewChild(boost::shared_ptr<Node>
toBeAlias)
{
boost::shared_ptr<Node> q(new Node(toBeAlias.get()),
aliasing(toBeAlias));
toBeAlias->children.push_back(q.get());
return q;
}
private:
Node* rootDocument;
Node* parent;
std::vector<Node*> children;
static void NodeDeleter(Node* node)
{
delete node;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
long uc1, uc2, uc3, uc4;
boost::shared_ptr<Node> n(new Node);
Node::appendNewChild(n);
Node::appendNewChild(n);
Node::appendNewChild(n);
boost::shared_ptr<Node> c;
uc1 = n.use_count();
{
c = Node::Child(n, 0);
uc2 = n.use_count();
n.reset();
uc3 = c.use_count();
}
uc4 = n.use_count();
return 0;
}
David Abrahams wrote:
> "jwat" <jwaterloo@idvelocity.com> writes:
>
> > I have recently had the privilege of reading N1851, "Improving
> > Usability and Performance of TR1 Smart Pointers". I believe there is
> > another one/two compelling reasons for the use of shared pointer
> > aliasing. Using shared pointer aliasing can reduce cyclical references
> > and make using properly constructed APIs easier with more consistent
> > results.
>
> You probably didn't know this, but the Boost/TR1 shared_ptr already
> supports aliasing. Just create a deleter that holds a shared_ptr to
> the object whose lifetime you really _want_ to manage:
>
> struct aliasing
> {
> aliasing(boost::shared_ptr<void> p)
> : p(p)
> {}
>
> void operator()(void*) { p.reset(); }
>
> private:
> boost::shared_ptr<void> p;
> };
>
> now:
>
> void f(shared_ptr<Foo> p)
> {
> shared_ptr<Bar> q(&p->bar, aliasing(foo));
> ...
> }
>
> > This complicates code even more because if you
> > pass a child object to a function that needs to get its parent or root
> > document than there is a possibility that it will fail because the
> > reference is a weak pointer. This is where your shared pointer aliasing
> > can help greatly.
>
> I don't see how.
>
> > Shared pointer aliasing can reduce the proliferation
> > of weak_ptr thus making the code easier to use with more consistent
> > results. It does this by keeping the entire document alive while the
> > client using it is accessing any piece of it. This works when all the
> > nodes in the object graph are sharing the same reference count. When
> > there are no more references to the root document or any of its [grand]
> > children it gets deleted taking out everything with it.
>
> <snip>
>
> > class Node
> > {
> > public:
> > aliased_shared_array<Node> children;// a collection that
> > owns the objects but whos indexer returns a aliased shared_ptr
> > aliased_shared_ptr<Node> parent;// a method that returns a
> > aliased shared pointer
> > aliased_shared_ptr<Node> rootDocument;// a method that
> > returns a aliased shared pointer
> > };
> >
> > I don't know if you agree with this assessment.
>
> I don't think so. Unless I'm missing something, the references from
> the children back to the root node still constitute circular
> references. In a sense, using aliasing just transforms them into
> "self-cycles" that point directly back at the node where they
> originate. Try it using the aliasing delter I posted above, and see
> what happens.
>
> > However, if you do I would imagine any minor change to the TR1 APIs
> > that would not only increase performance but could greatly improve
> > the ease of use, should be seriously considered.
>
> Well yes, but N1851 does not constitute a minor change. That's not to
> say it's without merit, of course.
>
> --
> Dave Abrahams
> Boost Consulting
> www.boost-consulting.com
>
> ---
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
> [ --- Please see the FAQ before posting. --- ]
> [ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]
Author: "Gino" <irocha@mac.com>
Date: Tue, 27 Sep 2005 10:21:25 CST Raw View
> You probably didn't know this, but the Boost/TR1 shared_ptr already
> supports aliasing. Just create a deleter that holds a shared_ptr to
> the object whose lifetime you really _want_ to manage:
Using shared_ptr as a deleter is a good workaround, but has alot of
overhead. As observed by Howard Hinnant in a previous message in this
thread, each "aliased" instance would construct it's own count
structure. The proposed aliasing feature uses the same count structure
and therefore only requires a reference count change.
> > This complicates code even more because if you
> > pass a child object to a function that needs to get its parent or root
> > document than there is a possibility that it will fail because the
> > reference is a weak pointer. This is where your shared pointer aliasing
> > can help greatly.
>
> I don't see how.
Having a weak_ptr to an object only ensures that the count structure
remains valid. Aliases have strong references which ensures that the
object is not destroyed. There are obviously use cases for both
mechanisms.
>
> > Shared pointer aliasing can reduce the proliferation
> > of weak_ptr thus making the code easier to use with more consistent
> > results. It does this by keeping the entire document alive while the
> > client using it is accessing any piece of it. This works when all the
> > nodes in the object graph are sharing the same reference count. When
> > there are no more references to the root document or any of its [grand]
> > children it gets deleted taking out everything with it.
>
> <snip>
>
> > class Node
> > {
> > public:
> > aliased_shared_array<Node> children;// a collection that
> > owns the objects but whos indexer returns a aliased shared_ptr
> > aliased_shared_ptr<Node> parent;// a method that returns a
> > aliased shared pointer
> > aliased_shared_ptr<Node> rootDocument;// a method that
> > returns a aliased shared pointer
> > };
> >
> > I don't know if you agree with this assessment.
>
> I don't think so. Unless I'm missing something, the references from
> the children back to the root node still constitute circular
> references. In a sense, using aliasing just transforms them into
> "self-cycles" that point directly back at the node where they
> originate. Try it using the aliasing delter I posted above, and see
> what happens.
>
If I understand jwat's intent correctly, and there is a root object
that owns and is responsible for recursively destroying all nodes in
the document. Then if we assume that there is a shared_ptr to that
object which is accessible to all nodes, then we only have to store the
actual pointer values in each node. We would provide accessors that
return those pointer values as shared_ptr aliases of the root
shared_ptr. For example:
class Node
{
Node *d_parent_p;
Node *d_rootDocument_p;
shared_ptr<void> *d_rootSp_p;
public:
aliased_shared_array<Node> children();// Not sure how to deal with
shared_array
shared_ptr<Node> parent()
{
return shared_ptr<Node>(*d_rootSp_p, d_parent_p);
}
shared_ptr<Node> rootDocument()
{
return shared_ptr<Node>(*d_rootSp_p, d_rootDocument_p);
}
};
In this case, any reference to any node in the document, actually has a
reference to the whole document.
> > However, if you do I would imagine any minor change to the TR1 APIs
> > that would not only increase performance but could greatly improve
> > the ease of use, should be seriously considered.
>
> Well yes, but N1851 does not constitute a minor change. That's not to
> say it's without merit, of course.
>
While there are some parts of N1851 that require more effort to
implement, some very usefull features such as shared_ptr aliasing are
very simple extensions. We would have no problem with them being
consider independently.
-- Ilougino Rocha
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.jamesd.demon.co.uk/csc/faq.html ]