|
Boost : |
Subject: Re: [boost] [interprocess][multi_index] emplace() is not sufficient for maps
From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2009-09-19 11:02:09
On Sep 19, 2009, at 4:18 AM, Thorsten Ottosen wrote:
> Ion Gaztañaga skrev:
>> Thorsten Ottosen escribió:
>
>>>
>>> Therefore I would like to see the follwing member:
>>>
>>> iterator modify_key( const_iterator element, const Key& new_key );
>>>
>>> This function only needs to rebalance/resort the keys and can
>>> completely avoid deallocation/allocation.
>> Interesting. What about exception safety? We can't assure strong
>> guarantee. It could just erase the element if the change fails? We
>> could also use the assignment operator instead of destroy+construct
>> to reuse some resources.
>
> For keys where assignment don't throw, we get the strong guarantee.
> For
> the case where assignment could throw, I agree the element should be
> erase (the user can then reinsert manually if that happens). So maybe
> we should return
>
> pair<iterator,bool>
>
> to tell the user if the update succeeded?
>
> For cases where the ordering is based merely on a non-throwing part
> of the Key type, it could be usable with
>
>
> template< class Key2 >
> iterator modify_key( const_iterator element, const Key2& new_key );
>
> This function would place the existing node in its right place, but
> not
> change the key object which the user then has to do, e.g.
>
> iterator i = modify_key( x, new_object.sub_object );
> i->swap( new_object );
>
> I guess there are some additional overloads for movable types.
Suggestion:
Using std::map as an example as I am not familiar with [interprocess]
[multi_index]. Also using C++0X notation (to be emulated in C++03).
Add to the container:
template <class Key, class T, class Compare, class Allocator>
class map
{
public:
...
typedef /details/ node_ptr;
node_ptr remove(const_iterator p);
pair<iterator, bool> insert(node_ptr&& nd);
iterator insert(const_iterator p, node_ptr&& nd);
...
};
map::node_ptr is a move-only smart pointer which holds an Allocator*
and a map::node* (or equivalent smart pointer as interprocess may
need?). node_ptr roughly looks like:
template <class Key, class T, class Allocator>
class node_ptr
{
typedef typename Allocator::value_type node;
node* node_;
Allocator* alloc_;
node_ptr(const node_ptr&);
node_ptr& operator=(const node_ptr&);
public:
typedef pair<Key, T> value_type;
node_ptr() : node_(), alloc_() {}
~node_ptr() {reset();}
node_ptr(node_ptr&& n)
: node_(n.node_),
alloc_(n.alloc_)
{
n.node_ = nullptr;
n.alloc_ = nullptr;
}
node_ptr& operator=(node_ptr&& n)
{
reset();
node_ = n.node_;
alloc_ = n.alloc_;
n.node_ = nullptr;
n.alloc_ = nullptr;
return *this;
}
void reset()
{
if (node_ != nullptr)
{
node_->__value_.~pair<const Key, T>();
alloc_->deallocate(node_, 1);
}
}
value_type& operator*() const
{
return *(value_type*)addressof(node_->__value_);
}
value_type* operator->() const
{
return (value_type*)addressof(node_->__value_);
}
private:
node_ptr(node* n, Allocator* a) : node_(n), alloc_(a) {}
node* release()
{
node* r = node_;
node_ = 0;
alloc_ = 0;
return r;
}
template <class, class, class, class> friend class map;
};
The client can default construct a node_ptr, or get one from
map::remove(). He can extract a node, get const-free access to the
node's value field (as if node_ptr pointed straight at the value
field), and insert the node back into any map that has an equal
allocator:
M::node_ptr p = m1.remove(next(m1.cbegin(), 3));
p->first = 10;
m2.insert(std::move(p));
The remove() is nothrow.
The client manipulates the node's value outside of the container. If
that manipulation throws, or is abandoned, node_ptr cleans up.
insert() can only throw if the Compare operation throws, and if that
happens, node_ptr continues to own the node. Thus the client can
catch exceptions if desired outside the insert without loosing
ownership of the node (which is why insert doesn't take node_ptr by
value).
Constraints:
A node_ptr returned from m.remove() must not outlive m, unless it is
reset/move-assigned first.
Notes:
This solution addresses both Thorsten's use case and Alan's:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#839
Has been prototyped (using raw pointers) on std::map and lightly
tested (just a sanity check). Actual test code below (so you can see
how little I tested it, and any details I may have neglected to
describe:
-Howard
#include <iostream>
#include <map>
#include <new>
#include <cstdlib>
int outstanding_news = 0;
void* operator new(std::size_t s) throw(std::bad_alloc)
{
++outstanding_news;
return std::malloc(s);
}
void operator delete(void* p) throw()
{
if (p)
{
--outstanding_news;
std::free(p);
}
}
template <class C>
void display(const char* msg, const C& c)
{
std::cout << msg << '\n';
for (typename C::const_iterator i = c.begin(), e = c.end(); i !=
e; ++i)
std::cout << '(' << i->first << ", " << i->second << ")\n";
std::cout << '\n';
}
int main()
{
{
typedef std::map<int, int> M;
typedef M::value_type V;
M m1;
m1.insert(V(1, 1));
m1.insert(V(2, 1));
m1.insert(V(3, 1));
m1.insert(V(4, 1));
m1.insert(V(5, 1));
m1.insert(V(6, 1));
std::cout << "outstanding_news = " << outstanding_news << '\n';
display("Before remove\nm1 =", m1);
// new interface
M::node_ptr p = m1.remove(next(m1.cbegin(), 3));
display("After remove\nm1 =", m1);
std::cout << "removed node = (" << p->first << ", " << p->second
<< ")\n";
std::cout << "outstanding_news = " << outstanding_news << '\n';
// new interface
p->first = 10;
std::cout << "removed node = (" << p->first << ", " << p->second
<< ")\n";
M m2;
// new interface
m2.insert(std::move(p));
display("m2 =", m2);
std::cout << "outstanding_news = " << outstanding_news << '\n';
}
std::cout << "outstanding_news = " << outstanding_news << '\n';
}
outstanding_news = 6
Before remove
m1 =
(1, 1)
(2, 1)
(3, 1)
(4, 1)
(5, 1)
(6, 1)
After remove
m1 =
(1, 1)
(2, 1)
(3, 1)
(5, 1)
(6, 1)
removed node = (4, 1)
outstanding_news = 6
removed node = (10, 1)
m2 =
(10, 1)
outstanding_news = 6
outstanding_news = 0
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk