Boost logo

Boost :

Subject: Re: [boost] [interprocess][multi_index] emplace() is not sufficient for maps
From: Howard Hinnant (howard.hinnant_at_[hidden])
Date: 2009-09-24 11:47:49


On Sep 24, 2009, at 11:23 AM, Thorsten Ottosen wrote:

> Howard Hinnant skrev:
>> On Sep 24, 2009, at 10:02 AM, Thorsten Ottosen wrote:
>
>>> 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 :-)
>> With what allocator would the node_ptr allocate the node?
>
> Ok, I guess that is why I originally spoke about the following
> interface:
>
> typedef ... map;
> map m;
> map::node_ptr p = m.get_node_ptr(<like emplace, but only for
> constructing the mapped value>);
> p->pair.first = modify( p->pair.second );
> m.insert( boost::move(p) );
>
> So get_node_ptr( ... ) with two overloads would be needed.

This is effectively the same thing as:

typedef ... map;
map m;
map::node_ptr p = m.remove(m.emplace_hint(m.begin(),
my_special_cheap_key_value, mv1, mv2));
p->first = modify( p->second );
m.insert( boost::move(p) );

The main differences are:

1. map never exposes a partially constructed pair to the client.
2. the onus is on the client to figure out how to cheaply and
temporarily construct a key (when he needs to).
3. exception safety handling is much simplified.

It is a tradeoff that I think is good. Having an interface that
exposes a partially constructed object to everyone would be bad (for
almost everyone). Having an interface that makes it more difficult
for a few clients to extract a little more performance, isn't good,
but it is better than exposing partially constructed objects. These
partially constructed objects would need more complicated exception
safety protection, which could only be partially encapsulated by the
node_ptr:

  typedef ... map;
  map m;
  map::node_ptr p = m.get_node_ptr(<like emplace, but only for
                                   constructing the mapped value>);
// p->first not constructed here
// if construction fails, ~node_ptr() only destructs p->second, not p-
>first
  ::new (&p->first) key_type(modify( p->second ));
  // tell node_ptr that it is now responsible for p->first
  p.first_constructed(true);
  m.insert( boost::move(p) );

-Howard


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