Boost logo

Boost :

Subject: Re: [boost] [interprocess][multi_index] emplace() is not sufficient for maps
From: Thorsten Ottosen (thorsten.ottosen_at_[hidden])
Date: 2009-09-24 10:02:40


Howard Hinnant skrev:
> On Sep 19, 2009, at 4:18 AM, Thorsten Ottosen wrote:
>
>> Ion Gaztañaga skrev:
>>> Thorsten Ottosen escribió:

> 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:

I'm fine with this interface, and it seems less complicated compared to
Joaquin's modify_key().

It doesn't quite address my first issue though, namely that I need to
construct the mapped value in the node, and then compute the key from
this mapped value (after some modification).

I think if the node_ptr class had

  a) a forwarding (Key,Value) constructor

  b) an emplacing constructor that
  constructed the pair with (Key,...)

then I could do exactly what I needed :-)

-Thorsten


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk