Topic: N3000 Problem replacing the allocator for node based containers using a sentinel
Author: "Bo Persson" <bop@gmb.dk>
Date: Tue, 15 Dec 2009 11:14:26 CST Raw View
Perhaps I need a How-to guide for this, but I just cannot get it to
work. Assuming a node based container using a sentinel node, how are
you supposed to re-establish the sentinel in an exception safe manner
after replacing the allocator?
I tried with this code:
_RedBlackTree& operator=(const _RedBlackTree& _Other)
{
if (this != &_Other)
{
clear();
if (__node_allocator_traits::
propagate_on_container_copy_assignment::value)
{
_DropSentinel();
_MyAllocator = _Other._MyAllocator;
_CreateSentinel();
}
insert(_Other.cbegin(), _Other.cend());
}
return *this;
}
Before replacing the allocator, I obviously must deallocate everything
through the old allocator, including the sentinel node.
Now, if _CreateSentinel() throws a bad_alloc because the new allocator
is fresh out of resources, I cannot re-establish my invariant that the
container always has a sentinel node! I'm toast!!
Then what??
Bo Persson
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Tue, 15 Dec 2009 19:02:57 CST Raw View
Bo Persson wrote:
> Perhaps I need a How-to guide for this, but I just cannot get it to
> work. Assuming a node based container using a sentinel node, how are
> you supposed to re-establish the sentinel in an exception safe
> manner after replacing the allocator?
>
> I tried with this code:
>
> _RedBlackTree& operator=(const _RedBlackTree& _Other)
> {
> if (this != &_Other)
> {
> clear();
>
> if (__node_allocator_traits::
> propagate_on_container_copy_assignment::value)
> {
> _DropSentinel();
> _MyAllocator = _Other._MyAllocator;
> _CreateSentinel();
> }
>
> insert(_Other.cbegin(), _Other.cend());
> }
>
> return *this;
> }
>
>
> Before replacing the allocator, I obviously must deallocate
> everything through the old allocator, including the sentinel node.
>
> Now, if _CreateSentinel() throws a bad_alloc because the new
> allocator is fresh out of resources, I cannot re-establish my
> invariant that the container always has a sentinel node! I'm toast!!
>
> Then what??
>
Answering myself again (it was late yesterday :-)
This must be one place to use the copy-swap idiom:
First make a copy of the parameter and then, if that works, swap the
copy with the current object.
if (__node_allocator_traits::
propagate_on_container_copy_assignment::value)
{
_RedBlackTree _Tmp(_Other);
this->swap(_Tmp);
}
else
{
insert(_Other.cbegin(), _Other.cend());
}
Was that too hard? :-)
Bo Persson
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Kaz Kylheku <kkylheku@gmail.com>
Date: Tue, 15 Dec 2009 19:02:31 CST Raw View
On 2009-12-15, Bo Persson <bop@gmb.dk> wrote:
> Perhaps I need a How-to guide for this, but I just cannot get it to
> work. Assuming a node based container using a sentinel node, how are
> you supposed to re-establish the sentinel in an exception safe manner
> after replacing the allocator?
>
> I tried with this code:
>
> _RedBlackTree& operator=(const _RedBlackTree& _Other)
> {
> if (this != &_Other)
> {
> clear();
>
> if (__node_allocator_traits::
> propagate_on_container_copy_assignment::value)
> {
> _DropSentinel();
> _MyAllocator = _Other._MyAllocator;
> _CreateSentinel();
> }
>
> insert(_Other.cbegin(), _Other.cend());
> }
>
> return *this;
> }
>
>
> Before replacing the allocator, I obviously must deallocate everything
> through the old allocator, including the sentinel node.
Isn't the sentinel node embedded in the _RedBlackTree object itself?
If not, that's silly. Since the tree always needs a sentinel node,
there is no point in allocating it separately. Just make it a member.
class _RedBlackTree {
...
private:
_RedBlackNode _sentinel;
...
};
There is a 1:1 association between trees and sentinels; each tree
needs its own, for any hope of thread safety.
> Now, if _CreateSentinel() throws a bad_alloc because the new allocator
The functions _CreateSentinel() and _DropSentinel() shouldn't even exist;
this makes no sense.
The constructor of _RedBlackTree should initialize the sentinel node,
which should be correctly maintained thereafter by all of the
operations.
The clear() operation should result in an empty tree, with a sentinel
that points back to itself, ready to accept new insertions.
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Wed, 16 Dec 2009 18:00:23 CST Raw View
Kaz Kylheku wrote:
> On 2009-12-15, Bo Persson <bop@gmb.dk> wrote:
>> Perhaps I need a How-to guide for this, but I just cannot get it to
>> work. Assuming a node based container using a sentinel node, how
>> are you supposed to re-establish the sentinel in an exception safe
>> manner after replacing the allocator?
>>
>> I tried with this code:
>>
>> _RedBlackTree& operator=(const _RedBlackTree& _Other)
>> {
>> if (this != &_Other)
>> {
>> clear();
>>
>> if (__node_allocator_traits::
>> propagate_on_container_copy_assignment::value)
>> {
>> _DropSentinel();
>> _MyAllocator = _Other._MyAllocator;
>> _CreateSentinel();
>> }
>>
>> insert(_Other.cbegin(), _Other.cend());
>> }
>>
>> return *this;
>> }
>>
>>
>> Before replacing the allocator, I obviously must deallocate
>> everything through the old allocator, including the sentinel node.
>
> Isn't the sentinel node embedded in the _RedBlackTree object itself?
> If not, that's silly. Since the tree always needs a sentinel node,
> there is no point in allocating it separately. Just make it a
> member.
>
> class _RedBlackTree {
> ...
> private:
> _RedBlackNode _sentinel;
> ...
> };
>
> There is a 1:1 association between trees and sentinels; each tree
> needs its own, for any hope of thread safety.
Having a separate sentinel node makes it easier to swap the entire
tree to another container. Otherwise you might have to find and adjust
all pointers pointing back to the sentinel.
>
>> Now, if _CreateSentinel() throws a bad_alloc because the new
>> allocator
>
> The functions _CreateSentinel() and _DropSentinel() shouldn't even
> exist; this makes no sense.
True, that is my problem. :-)
However, when the container's allocator is replaced, all of its
allocated memory must first be released through the old allocator.
This is a new requirement that might have effects that require some
retuning of other container operations.
>
> The constructor of _RedBlackTree should initialize the sentinel
> node, which should be correctly maintained thereafter by all of the
> operations.
>
> The clear() operation should result in an empty tree, with a
> sentinel that points back to itself, ready to accept new insertions.
True, but if the sentinel is a separate object is has to go, before
replacing the allocator. Like I wrote elsewhere, I later recognized
that this can be solved by creating the new copy before destroying the
old one. The copy-swap idiom.
Bo Persson
--
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@netlab.cs.rpi.edu]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]