Re: Question about boost::multi_index emplace operations

Joaquin M López Muñoz via Boost-users <[email protected]>
Newsgroups gmane.comp.lib.boost.user
Message-ID <[email protected]>
El 01/11/2022 a las 16:44, Pavel Vazharov via Boost-users escribió:
> Hi there,
>
> Is there a way to emplace (key, value) in boost multi_index hashed 
> index in such a way that the value is
> not moved/stolen if the key is already present?
> As far as I checked the implementation for emplace (and emplace_) the 
> node is eagerly created and the
> key and the value are moved into the node and then the insertion 
> operation takes place. If the insertion
> fails the node is destroyed but the outside value is lost/stolen in 
> all cases.
> What I actually need is to create the value only if the key is not 
> present, because the creation is expensive,
> and I'd like to avoid the find+emplace or insert empty value + modify 
> call. (As far as I checked, the value
> can't be modified directly from the returned iterator because it's 
> const and will need const_cast to work).
> [...]

Insert+modify works fine as far as I can see:
(http://coliru.stacked-crooked.com/a/eb5dc2b569dff95e )

     // C++20, easily portable to older C++ versions

     #include <boost/multi_index_container.hpp>
     #include <boost/multi_index/hashed_index.hpp>
     #include <boost/multi_index/key.hpp>
     #include <cassert>
     #include <string>
     #include <utility>

     using namespace boost::multi_index;

     struct element
     {
       int         id;
       std::string str;
     };

     using container=multi_index_container<
       element,
       indexed_by<
         hashed_unique<key<&element::id>>
       >
     >;

     template<typename Index,typename Str>
     auto lazy_emplace(Index& i,int id,Str&& str)
     {
       auto        p=i.emplace(id,std::string{});
       const auto& [it,b]=p;
       if(p.second)i.modify(it,[&](auto& e){e.str=std::forward<Str>(str);});
       return p;
     }

     int main()
     {
       container   c={{0,"hello"}};
       std::string str="boost";

       auto [it,b]=lazy_emplace(c,0,std::move(str));
       assert(b==false&&!str.empty());

       std::tie(it,b)=lazy_emplace(c,1,std::move(str));
       assert(b==true&&str.empty());
     }

Your particular problem hints at the more general issue that 
Boost.MultiIndex indices do not have
something like try_emplace, which is the semantics you're after. The 
reason why this function
is not available is that we have set-like indices, not map-like indices. 
For instance, this expression

c.try_emplace(0,"hello");

assumes that value_type is constructible from 0 and "hello" *and* that 0 
is the key. With
Boost.MultiIndex, this assumption does not hold true in general, as the 
key is gotten via a key
extractor and there's no mapping between construction args and keys or 
anything. That said,
the following could be a potentially interesting addition to the library:

template<typename Key,typename... Args>
c.try_emplace(const Key& k,Args&&... args);

where the value is constructed from args... and not from (k,args...), 
so, going back to your
example, the syntax would be:

s.try_emplace(0,0,"hello"); // first 0 is the key, (0,"hello") 
constructs element

I have to think about this.

Best,

Joaquín M López Muñoz

_______________________________________________
Boost-users mailing list
[email protected]
https://lists.boost.org/mailman/listinfo.cgi/boost-users
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.