added insert_and_visit and similar operations to concurrent containers (#283)

This commit is contained in:
joaquintides 2024-09-21 10:58:30 +02:00 committed by GitHub
parent 57546ed7e3
commit 834580b539
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 2473 additions and 75 deletions

View File

@ -9,6 +9,9 @@
== Release 1.87.0 - Major update == Release 1.87.0 - Major update
* Added concurrent, node-based containers `boost::concurrent_node_map` and `boost::concurrent_node_set`. * Added concurrent, node-based containers `boost::concurrent_node_map` and `boost::concurrent_node_set`.
* Added `insert_and_visit(x, f1, f2)` and similar operations to concurrent containers, which
allow for visitation of an element right after insertion (by contrast, `insert_or_visit(x, f)` only
visits the element if insertion did _not_ take place).
* Made visitation exclusive-locked within certain * Made visitation exclusive-locked within certain
`boost::concurrent_flat_set` operations to allow for safe mutable modification of elements `boost::concurrent_flat_set` operations to allow for safe mutable modification of elements
({github-pr-url}/265[PR#265^]). ({github-pr-url}/265[PR#265^]).

View File

@ -132,6 +132,23 @@ will grant visitation functions const/non-const access to the element depending
by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result by using `cvisit` overloads (for instance, `insert_or_cvisit`) and may result
in higher parallelization. For concurrent sets, on the other hand, in higher parallelization. For concurrent sets, on the other hand,
visitation is always const access. visitation is always const access.
Although expected to be used much less frequently, concurrent containers
also provide insertion operations where an element can be visited right after
element creation (in addition to the usual visitation when an equivalent
element already exists):
[source,c++]
----
m.insert_and_cvisit(x,
[](const auto& y) {
std::cout<< "(" << y.first << ", " << y.second <<") inserted\n";
},
[](const auto& y) {
std::cout<< "(" << y.first << ", " << y.second << ") already exists\n";
});
----
Consult the references of Consult the references of
xref:#concurrent_node_set[`boost::concurrent_node_set`], xref:#concurrent_node_set[`boost::concurrent_node_set`],
xref:#concurrent_flat_map[`boost::concurrent_node_map`], xref:#concurrent_flat_map[`boost::concurrent_node_map`],

View File

@ -169,6 +169,27 @@ namespace boost {
template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f); template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f);
template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f); template<class F> size_type xref:#concurrent_flat_map_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_map_move_insert_and_cvisit[insert_and_cvisit](init_type&& obj, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args); template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](const key_type& k, Args&&... args);
template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args); template<class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](key_type&& k, Args&&... args);
template<class K, class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](K&& k, Args&&... args); template<class K, class... Args> bool xref:#concurrent_flat_map_try_emplace[try_emplace](K&& k, Args&&... args);
@ -186,6 +207,19 @@ namespace boost {
template<class K, class... Args, class F> template<class K, class... Args, class F>
bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f); bool xref:#concurrent_flat_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_visit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool xref:#concurrent_flat_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj);
template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); template<class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj);
template<class K, class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); template<class K, class M> bool xref:#concurrent_flat_map_insert_or_assign[insert_or_assign](K&& k, M&& obj);
@ -394,8 +428,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element. * Read access to the element.
* Non-mutable modification of the element. * Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const * Mutable modification of the element:
and its name does not contain `cvisit`.) ** Within a container function accepting two visitation functions, always for the first function.
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e` Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`. synchronizes with the internal invocation of a visitation function on `e`.
@ -1113,7 +1148,113 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to Equivalent to
[listing,subs="+macros,+quotes"] [listing,subs="+macros,+quotes"]
----- -----
this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f); this->xref:#concurrent_flat_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), f);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
```
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff `emplace_and_cvisit` is used.
[horizontal]
Requires:;; `value_type` is constructible from `args`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
---
==== Copy insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `const value_type&` argument participate in overload resolution
only if `std::remove_cv<std::remove_reference<decltype(obj)>::type>::type` is `value_type`.
---
==== Move insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `value_type&&` argument participate in overload resolution
only if `std::remove_reference<decltype(obj)>::type` is `value_type`.
---
==== Insert Iterator Range and Visit
```c++
template<class InputIterator, class F1, class F2>
size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator, class F1, class F2>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_flat_map_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Initializer List and Visit
```c++
template<class F1, class F2>
size_type insert_or_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_or_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_flat_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), f1, f2);
----- -----
[horizontal] [horizontal]
@ -1207,6 +1348,55 @@ The `template<class K, class\... Args, class F>` overloads only participate in o
--- ---
==== try_emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2);
```
Inserts an element constructed from `k` and `args` into the table if there is no existing element with key `k` contained within it,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; No `value_type` is constructed
if there is an element with an equivalent key; otherwise, the construction is of the form: +
+
--
```c++
// first four overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// last two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
Invalidates pointers and references to elements if a rehashing is issued.
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
The `template<class K, class\... Args, class F1, class F2>` overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
--
---
==== insert_or_assign ==== insert_or_assign
```c++ ```c++
template<class M> bool insert_or_assign(const key_type& k, M&& obj); template<class M> bool insert_or_assign(const key_type& k, M&& obj);

View File

@ -160,6 +160,25 @@ namespace boost {
template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f); template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_visit](std::initializer_list<value_type> il, F f);
template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f); template<class F> size_type xref:#concurrent_flat_set_insert_initializer_list_or_visit[insert_or_cvisit](std::initializer_list<value_type> il, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_flat_set_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_flat_set_transparent_insert_and_cvisit[insert_and_cvisit](K&& k, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_flat_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
size_type xref:#concurrent_flat_set_erase[erase](const key_type& k); size_type xref:#concurrent_flat_set_erase[erase](const key_type& k);
template<class K> size_type xref:#concurrent_flat_set_erase[erase](const K& k); template<class K> size_type xref:#concurrent_flat_set_erase[erase](const K& k);
@ -355,8 +374,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element. * Read access to the element.
* Non-mutable modification of the element. * Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const * Mutable modification of the element:
and its name does not contain `cvisit`.) ** Within a container function accepting two visitation functions, always for the first function.
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
Any `boost::concurrent_flat_set operation` that inserts or modifies an element `e` Any `boost::concurrent_flat_set operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`. synchronizes with the internal invocation of a visitation function on `e`.
@ -1086,7 +1106,123 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to Equivalent to
[listing,subs="+macros,+quotes"] [listing,subs="+macros,+quotes"]
----- -----
this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f); this->xref:#concurrent_flat_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), f);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
```
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is constructible from `args`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
---
==== Copy insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
---
==== Move insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued.
---
==== Transparent insert_and_[c]visit
```c++
template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2);
```
Inserts an element constructed from `std::forward<K>(k)` in the container if and only if there is no element in the container with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Invalidates pointers and references to elements if a rehashing is issued. +
+
These overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
---
==== Insert Iterator Range and Visit
```c++
template<class InputIterator,class F1, class F2>
size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_flat_set_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Initializer List and Visit
```c++
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_flat_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), f1, f2);
----- -----
[horizontal] [horizontal]

View File

@ -175,6 +175,31 @@ namespace boost {
template<class F> insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_visit](node_type&& nh, F f); template<class F> insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_visit](node_type&& nh, F f);
template<class F> insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f); template<class F> insert_return_type xref:#concurrent_node_map_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_visit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_copy_insert_and_cvisit[insert_and_cvisit](const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_visit](init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_map_move_insert_and_cvisit[insert_and_cvisit](init_type&& obj, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_node_map_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_node_map_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_node_map_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_node_map_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_map_insert_node_and_visit[insert_and_visit](node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_map_insert_node_and_visit[insert_and_cvisit](node_type&& nh, F1 f1, F2 f2);
template<class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](const key_type& k, Args&&... args); template<class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](const key_type& k, Args&&... args);
template<class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](key_type&& k, Args&&... args); template<class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](key_type&& k, Args&&... args);
template<class K, class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](K&& k, Args&&... args); template<class K, class... Args> bool xref:#concurrent_node_map_try_emplace[try_emplace](K&& k, Args&&... args);
@ -192,6 +217,20 @@ namespace boost {
template<class K, class... Args, class F> template<class K, class... Args, class F>
bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f); bool xref:#concurrent_node_map_try_emplace_or_cvisit[try_emplace_or_cvisit](K&& k, Args&&... args, F&& f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_visit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_visit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_visit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool xref:#concurrent_node_map_try_emplace_and_cvisit[try_emplace_and_cvisit](K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj); template<class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](const key_type& k, M&& obj);
template<class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj); template<class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](key_type&& k, M&& obj);
template<class K, class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](K&& k, M&& obj); template<class K, class M> bool xref:#concurrent_node_map_insert_or_assign[insert_or_assign](K&& k, M&& obj);
@ -406,8 +445,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element. * Read access to the element.
* Non-mutable modification of the element. * Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const * Mutable modification of the element:
and its name does not contain `cvisit`.) ** Within a container function accepting two visitation functions, always for the first function.
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
Any `boost::concurrent_node_map operation` that inserts or modifies an element `e` Any `boost::concurrent_node_map operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`. synchronizes with the internal invocation of a visitation function on `e`.
@ -1167,7 +1207,7 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to Equivalent to
[listing,subs="+macros,+quotes"] [listing,subs="+macros,+quotes"]
----- -----
this->xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f); this->xref:#concurrent_node_map_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), f);
----- -----
[horizontal] [horizontal]
@ -1196,6 +1236,130 @@ Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` a
--- ---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_and_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_and_cvisit(Args&&... args, F1&& f1, F2&& f2);
```
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff `emplace_and_cvisit` is used.
[horizontal]
Requires:;; `value_type` is constructible from `args`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
---
==== Copy insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(const init_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const init_type& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `const value_type&` argument participate in overload resolution
only if `std::remove_cv<std::remove_reference<decltype(obj)>::type>::type` is `value_type`.
---
==== Move insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_visit(init_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; In a call of the form `insert_and_[c]visit(obj, f1, f2)`, the overloads accepting a `value_type&&` argument participate in overload resolution
only if `std::remove_reference<decltype(obj)>::type` is `value_type`.
---
==== Insert Iterator Range and Visit
```c++
template<class InputIterator, class F1, class F2>
size_type insert_or_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator, class F1, class F2>
size_type insert_or_cvisit(InputIterator first, InputIterator last, F1 f2, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_node_map_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Initializer List and Visit
```c++
template<class F1, class F2>
size_type insert_or_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_or_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_node_map_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Node and Visit
```c++
template<class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2);
```
If `nh` is empty, does nothing.
Otherwise, inserts the associated element in the table if and only if there is no element in the table with a key equivalent to `nh.key()`,
and then invokes `f1` with a non-const reference to the newly inserted element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff `insert_or_cvisit` is used.
[horizontal]
Returns:;; An `insert_return_type` object constructed from `inserted` and `node`: +
* If `nh` is empty, `inserted` is `false` and `node` is empty.
* Otherwise if the insertion took place, `inserted` is true and `node` is empty.
* If the insertion failed, `inserted` is false and `node` has the previous value of `nh`.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` or call to `f1` or `f2`, the function has no effect.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` and the container are not equal.
---
==== try_emplace ==== try_emplace
```c++ ```c++
template<class... Args> bool try_emplace(const key_type& k, Args&&... args); template<class... Args> bool try_emplace(const key_type& k, Args&&... args);
@ -1278,6 +1442,53 @@ The `template<class K, class\... Args, class F>` overloads only participate in o
--- ---
==== try_emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(const key_type& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_visit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool try_emplace_and_cvisit(key_type&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_visit(K&& k, Args&&... args, F1&& f1, F2&& f2);
template<class K, class... Args, class F1, class F2>
bool try_emplace_and_cvisit(K&& k, Args&&... args, F1&& f1, F2&& f2);
```
Inserts an element constructed from `k` and `args` into the table if there is no existing element with key `k` contained within it,
and then invokes `f1` with a non-const reference to the newly created element.
Otherwise, invokes `f2` with a reference to the equivalent element; such reference is const iff a `*_cvisit` overload is used.
[horizontal]
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; No `value_type` is constructed
if there is an element with an equivalent key; otherwise, the construction is of the form: +
+
--
```c++
// first four overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<Key>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
// last two overloads
value_type(std::piecewise_construct,
std::forward_as_tuple(std::forward<K>(k)),
std::forward_as_tuple(std::forward<Args>(args)...))
```
The interface is exposition only, as C++ does not allow to declare parameter `f1` and `f2` after a variadic parameter pack.
The `template<class K, class\... Args, class F1, class F2>` overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
--
---
==== insert_or_assign ==== insert_or_assign
```c++ ```c++
template<class M> bool insert_or_assign(const key_type& k, M&& obj); template<class M> bool insert_or_assign(const key_type& k, M&& obj);

View File

@ -166,6 +166,29 @@ namespace boost {
template<class F> insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_visit](node_type&& nh, F f); template<class F> insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_visit](node_type&& nh, F f);
template<class F> insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f); template<class F> insert_return_type xref:#concurrent_node_set_insert_node_or_visit[insert_or_cvisit](node_type&& nh, F f);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_visit](Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_cvisit](Args&&... args, F1&& f1, F2&& f2);
template<class F1, class F2> bool xref:#concurrent_node_set_copy_insert_and_cvisit[insert_and_visit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_set_copy_insert_and_cvisit[insert_and_cvisit](const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_set_move_insert_and_cvisit[insert_and_visit](value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool xref:#concurrent_node_set_move_insert_and_cvisit[insert_and_cvisit](value_type&& obj, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_node_set_transparent_insert_and_cvisit[insert_and_visit](K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool xref:#concurrent_node_set_transparent_insert_and_cvisit[insert_and_cvisit](K&& k, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_node_set_insert_iterator_range_and_visit[insert_and_visit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class F2>
size_type xref:#concurrent_node_set_insert_iterator_range_and_visit[insert_and_cvisit](InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_node_set_insert_initializer_list_and_visit[insert_and_visit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type xref:#concurrent_node_set_insert_initializer_list_and_visit[insert_and_cvisit](std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_set_insert_node_and_visit[insert_and_visit](node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type xref:#concurrent_node_set_insert_node_and_visit[insert_and_cvisit](node_type&& nh, F1 f1, F2 f2);
size_type xref:#concurrent_node_set_erase[erase](const key_type& k); size_type xref:#concurrent_node_set_erase[erase](const key_type& k);
template<class K> size_type xref:#concurrent_node_set_erase[erase](const K& k); template<class K> size_type xref:#concurrent_node_set_erase[erase](const K& k);
@ -368,8 +391,9 @@ user-provided visitation function on the element passed do not introduce data ra
* Read access to the element. * Read access to the element.
* Non-mutable modification of the element. * Non-mutable modification of the element.
* Mutable modification of the element (if the container operation executing the visitation function is not const * Mutable modification of the element:
and its name does not contain `cvisit`.) ** Within a container function accepting two visitation functions, always for the first function.
** Within a non-const container function whose name does not contain `cvisit`, for the last (or only) visitation function.
Any `boost::concurrent_node_set operation` that inserts or modifies an element `e` Any `boost::concurrent_node_set operation` that inserts or modifies an element `e`
synchronizes with the internal invocation of a visitation function on `e`. synchronizes with the internal invocation of a visitation function on `e`.
@ -1140,7 +1164,7 @@ template<class F> size_type insert_or_cvisit(std::initializer_list<value_type> i
Equivalent to Equivalent to
[listing,subs="+macros,+quotes"] [listing,subs="+macros,+quotes"]
----- -----
this->xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or[c\]visit](il.begin(), il.end(), f); this->xref:#concurrent_node_set_insert_iterator_range_or_visit[insert_or_[c\]visit](il.begin(), il.end(), f);
----- -----
[horizontal] [horizontal]
@ -1169,6 +1193,140 @@ Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` a
--- ---
==== emplace_and_[c]visit
```c++
template<class... Args, class F1, class F2>
bool emplace_or_visit(Args&&... args, F1&& f1, F2&& f2);
template<class... Args, class F1, class F2>
bool emplace_or_cvisit(Args&&... args, F1&& f1, F2&& f2);
```
Inserts an object, constructed with the arguments `args`, in the table if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is constructible from `args`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; The interface is exposition only, as C++ does not allow to declare parameters `f1` and `f2` after a variadic parameter pack.
---
==== Copy insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(const value_type& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(const value_type& obj, F1 f2, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/CopyInsertable[CopyInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
---
==== Move insert_and_[c]visit
```c++
template<class F1, class F2> bool insert_and_visit(value_type&& obj, F1 f1, F2 f2);
template<class F1, class F2> bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2);
```
Inserts `obj` in the table if and only if there is no element in the table with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/MoveInsertable[MoveInsertable^].
Returns:;; `true` if an insert took place. +
Concurrency:;; Blocking on rehashing of `*this`.
---
==== Transparent insert_and_[c]visit
```c++
template<class K, class F1, class F2> bool insert_and_visit(K&& k, F1 f1, F2 f2);
template<class K, class F1, class F2> bool insert_and_cvisit(K&& k, F1 f1, F2 f2);
```
Inserts an element constructed from `std::forward<K>(k)` in the container if and only if there is no element in the container with an equivalent key,
and then invokes `f1` with a const reference to the newly created element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Requires:;; `value_type` is https://en.cppreference.com/w/cpp/named_req/EmplaceConstructible[EmplaceConstructible^] from `k`.
Returns:;; `true` if an insert took place.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; These overloads only participate in overload resolution if `Hash::is_transparent` and `Pred::is_transparent` are valid member typedefs. The library assumes that `Hash` is callable with both `K` and `Key` and that `Pred` is transparent. This enables heterogeneous lookup which avoids the cost of instantiating an instance of the `Key` type.
---
==== Insert Iterator Range and Visit
```c++
template<class InputIterator,class F1, class F2>
size_type insert_and_visit(InputIterator first, InputIterator last, F1 f1, F2 f2);
template<class InputIterator,class F1, class f2>
size_type insert_and_cvisit(InputIterator first, InputIterator last, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
while(first != last) this->xref:#concurrent_node_set_emplace_and_cvisit[emplace_and_[c\]visit](*first++, f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Initializer List and Visit
```c++
template<class F1, class F2>
size_type insert_and_visit(std::initializer_list<value_type> il, F1 f1, F2 f2);
template<class F1, class F2>
size_type insert_and_cvisit(std::initializer_list<value_type> il, F1 f1, F2 f2);
```
Equivalent to
[listing,subs="+macros,+quotes"]
-----
this->xref:#concurrent_node_set_insert_iterator_range_and_visit[insert_and_[c\]visit](il.begin(), il.end(), f1, f2);
-----
[horizontal]
Returns:;; The number of elements inserted.
---
==== Insert Node and Visit
```c++
template<class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2);
template<class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2);
```
If `nh` is empty, does nothing.
Otherwise, inserts the associated element in the table if and only if there is no element in the table with a key equivalent to `nh.value()`,
and then invokes `f1` with a const reference to the newly inserted element.
Otherwise, invokes `f2` with a const reference to the equivalent element.
[horizontal]
Returns:;; An `insert_return_type` object constructed from `inserted` and `node`: +
* If `nh` is empty, `inserted` is `false` and `node` is empty.
* Otherwise if the insertion took place, `inserted` is true and `node` is empty.
* If the insertion failed, `inserted` is false and `node` has the previous value of `nh`.
Throws:;; If an exception is thrown by an operation other than a call to `hasher` or call to `f1` or `f2`, the function has no effect.
Concurrency:;; Blocking on rehashing of `*this`.
Notes:;; Behavior is undefined if `nh` is not empty and the allocators of `nh` and the container are not equal.
---
==== erase ==== erase
```c++ ```c++
size_type erase(const key_type& k); size_type erase(const key_type& k);

View File

@ -517,6 +517,80 @@ namespace boost {
this->insert_or_cvisit(ilist.begin(), ilist.end(), f); this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
} }
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args) template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{ {
return table_.emplace(std::forward<Args>(args)...); return table_.emplace(std::forward<Args>(args)...);
@ -538,6 +612,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...); std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class... Args> template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args) BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{ {
@ -613,6 +711,78 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...); std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k) BOOST_FORCEINLINE size_type erase(key_type const& k)
{ {
return table_.erase(k); return table_.erase(k);

View File

@ -517,6 +517,101 @@ namespace boost {
this->insert_or_cvisit(ilist.begin(), ilist.end(), f); this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
} }
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_visit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_cvisit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args) template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{ {
return table_.emplace(std::forward<Args>(args)...); return table_.emplace(std::forward<Args>(args)...);
@ -538,6 +633,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...); std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k) BOOST_FORCEINLINE size_type erase(key_type const& k)
{ {
return table_.erase(k); return table_.erase(k);

View File

@ -585,6 +585,124 @@ namespace boost {
} }
} }
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_visit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_visit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class Ty, class F1, class F2>
BOOST_FORCEINLINE auto insert_and_cvisit(Ty&& value, F1 f1, F2 f2)
-> decltype(table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2))
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::forward<Ty>(value), f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args) template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{ {
return table_.emplace(std::forward<Args>(args)...); return table_.emplace(std::forward<Args>(args)...);
@ -606,6 +724,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...); std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class... Args> template <class... Args>
BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args) BOOST_FORCEINLINE bool try_emplace(key_type const& k, Args&&... args)
{ {
@ -681,6 +823,78 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...); std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type const& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
k, std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
key_type&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(
std::move(k), std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_visit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class K, class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(
K&& k, Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.try_emplace_and_cvisit(std::forward<K>(k),
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k) BOOST_FORCEINLINE size_type erase(key_type const& k)
{ {
return table_.erase(k); return table_.erase(k);

View File

@ -585,6 +585,145 @@ namespace boost {
} }
} }
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_visit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_visit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_visit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_visit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_visit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_visit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_visit(std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_visit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_visit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_visit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(
value_type const& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(obj, f1, f2);
}
template <class F1, class F2>
BOOST_FORCEINLINE bool insert_and_cvisit(value_type&& obj, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.insert_and_cvisit(std::move(obj), f1, f2);
}
template <class K, class F1, class F2>
BOOST_FORCEINLINE typename std::enable_if<
detail::are_transparent<K, hasher, key_equal>::value,
bool >::type
insert_and_cvisit(K&& k, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
return table_.try_emplace_and_cvisit(std::forward<K>(k), f1, f2);
}
template <class InputIterator, class F1, class F2>
void insert_and_cvisit(
InputIterator first, InputIterator last, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
for (; first != last; ++first) {
table_.emplace_and_cvisit(*first, f1, f2);
}
}
template <class F1, class F2>
void insert_and_cvisit(
std::initializer_list<value_type> ilist, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
this->insert_and_cvisit(ilist.begin(), ilist.end(), f1, f2);
}
template <class F1, class F2>
insert_return_type insert_and_cvisit(node_type&& nh, F1 f1, F2 f2)
{
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F1)
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F2)
using access = detail::foa::node_handle_access;
if (nh.empty()) {
return {false, node_type{}};
}
// Caveat: get_allocator() incurs synchronization (not cheap)
BOOST_ASSERT(get_allocator() == nh.get_allocator());
if (table_.insert_and_cvisit(std::move(access::element(nh)), f1, f2)) {
access::reset(nh);
return {true, node_type{}};
} else {
return {false, std::move(nh)};
}
}
template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args) template <class... Args> BOOST_FORCEINLINE bool emplace(Args&&... args)
{ {
return table_.emplace(std::forward<Args>(args)...); return table_.emplace(std::forward<Args>(args)...);
@ -606,6 +745,30 @@ namespace boost {
std::forward<Arg>(arg), std::forward<Args>(args)...); std::forward<Arg>(arg), std::forward<Args>(args)...);
} }
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_visit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_visit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
template <class Arg1, class Arg2, class... Args>
BOOST_FORCEINLINE bool emplace_and_cvisit(
Arg1&& arg1, Arg2&& arg2, Args&&... args)
{
BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE(
Arg1, Arg2, Args...)
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg2, Args...)
return table_.emplace_and_cvisit(
std::forward<Arg1>(arg1), std::forward<Arg2>(arg2),
std::forward<Args>(args)...);
}
BOOST_FORCEINLINE size_type erase(key_type const& k) BOOST_FORCEINLINE size_type erase(key_type const& k)
{ {
return table_.erase(k); return table_.erase(k);

View File

@ -1,5 +1,5 @@
/* Copyright 2023 Christian Mazakas. /* Copyright 2023 Christian Mazakas.
* Copyright 2023 Joaquin M Lopez Munoz. * Copyright 2023-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -58,6 +58,24 @@
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \ BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args)) BOOST_UNORDERED_DETAIL_LAST_ARG(Arg, Args))
#define BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args) \
mp11::mp_at_c<mp11::mp_list< \
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
>, \
mp11::mp_size<mp11::mp_list< \
Arg1 BOOST_UNORDERED_DETAIL_COMMA Arg2 BOOST_UNORDERED_DETAIL_COMMA Args \
>>::value - 2>
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_INVOCABLE( \
Arg1, Arg2, Args) \
BOOST_UNORDERED_STATIC_ASSERT_INVOCABLE( \
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
#define BOOST_UNORDERED_STATIC_ASSERT_PENULTIMATE_ARG_CONST_INVOCABLE( \
Arg1, Arg2, Args) \
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE( \
BOOST_UNORDERED_DETAIL_PENULTIMATE_ARG(Arg1, Arg2, Args))
namespace boost { namespace boost {
namespace unordered { namespace unordered {
namespace detail { namespace detail {

View File

@ -215,7 +215,7 @@ struct atomic_integral
/* Group-level concurrency protection. It provides a rw mutex plus an /* Group-level concurrency protection. It provides a rw mutex plus an
* atomic insertion counter for optimistic insertion (see * atomic insertion counter for optimistic insertion (see
* unprotected_norehash_emplace_or_visit). * unprotected_norehash_emplace_and_visit).
*/ */
struct group_access struct group_access
@ -755,6 +755,22 @@ public:
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...); try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
} }
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace_and_visit(Key&& x,Args&&... args)
{
return emplace_and_visit_flast(
group_exclusive{},
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename Key,typename... Args>
BOOST_FORCEINLINE bool try_emplace_and_cvisit(Key&& x,Args&&... args)
{
return emplace_and_visit_flast(
group_shared{},
try_emplace_args_t{},std::forward<Key>(x),std::forward<Args>(args)...);
}
template<typename... Args> template<typename... Args>
BOOST_FORCEINLINE bool emplace_or_visit(Args&&... args) BOOST_FORCEINLINE bool emplace_or_visit(Args&&... args)
{ {
@ -769,86 +785,121 @@ public:
group_shared{},std::forward<Args>(args)...); group_shared{},std::forward<Args>(args)...);
} }
template<typename F> template<typename... Args>
BOOST_FORCEINLINE bool insert_or_visit(const init_type& x,F&& f) BOOST_FORCEINLINE bool emplace_and_visit(Args&&... args)
{ {
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x); return construct_and_emplace_and_visit_flast(
group_exclusive{},std::forward<Args>(args)...);
} }
template<typename F> template<typename... Args>
BOOST_FORCEINLINE bool insert_or_cvisit(const init_type& x,F&& f) BOOST_FORCEINLINE bool emplace_and_cvisit(Args&&... args)
{ {
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x); return construct_and_emplace_and_visit_flast(
group_shared{},std::forward<Args>(args)...);
} }
template<typename F> template<typename Value,typename F>
BOOST_FORCEINLINE bool insert_or_visit(init_type&& x,F&& f) BOOST_FORCEINLINE bool insert_or_visit(Value&& x,F&& f)
{ {
return emplace_or_visit_impl( return insert_and_visit(
group_exclusive{},std::forward<F>(f),std::move(x)); std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
} }
template<typename F> template<typename Value,typename F>
BOOST_FORCEINLINE bool insert_or_cvisit(init_type&& x,F&& f) BOOST_FORCEINLINE bool insert_or_cvisit(Value&& x,F&& f)
{ {
return emplace_or_visit_impl( return insert_and_cvisit(
group_shared{},std::forward<F>(f),std::move(x)); std::forward<Value>(x),[](const value_type&){},std::forward<F>(f));
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_visit(const init_type& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_cvisit(const init_type& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_visit(init_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
}
template<typename F1,typename F2>
BOOST_FORCEINLINE bool insert_and_cvisit(init_type&& x,F1&& f1,F2&& f2)
{
return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
} }
/* SFINAE tilts call ambiguities in favor of init_type */ /* SFINAE tilts call ambiguities in favor of init_type */
template<typename Value,typename F> template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_or_visit(const Value& x,F&& f) BOOST_FORCEINLINE auto insert_and_visit(const Value& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool> ->enable_if_is_value_type<Value,bool>
{ {
return emplace_or_visit_impl(group_exclusive{},std::forward<F>(f),x); return emplace_and_visit_impl(
group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),x);
} }
template<typename Value,typename F> template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_or_cvisit(const Value& x,F&& f) BOOST_FORCEINLINE auto insert_and_cvisit(const Value& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool> ->enable_if_is_value_type<Value,bool>
{ {
return emplace_or_visit_impl(group_shared{},std::forward<F>(f),x); return emplace_and_visit_impl(
group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),x);
} }
template<typename Value,typename F> template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_or_visit(Value&& x,F&& f) BOOST_FORCEINLINE auto insert_and_visit(Value&& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool> ->enable_if_is_value_type<Value,bool>
{ {
return emplace_or_visit_impl( return emplace_and_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x)); group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
} }
template<typename Value,typename F> template<typename Value,typename F1,typename F2>
BOOST_FORCEINLINE auto insert_or_cvisit(Value&& x,F&& f) BOOST_FORCEINLINE auto insert_and_cvisit(Value&& x,F1&& f1,F2&& f2)
->enable_if_is_value_type<Value,bool> ->enable_if_is_value_type<Value,bool>
{ {
return emplace_or_visit_impl( return emplace_and_visit_impl(
group_shared{},std::forward<F>(f),std::move(x)); group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
} }
template<typename F,typename T=element_type> template<typename F1,typename F2,typename T=element_type>
BOOST_FORCEINLINE BOOST_FORCEINLINE
typename std::enable_if< typename std::enable_if<
!std::is_same<T,value_type>::value, !std::is_same<T,value_type>::value,
bool bool
>::type >::type
insert_or_visit(element_type&& x, F&& f) insert_and_visit(element_type&& x,F1&& f1,F2&& f2)
{ {
return emplace_or_visit_impl( return emplace_and_visit_impl(
group_exclusive{},std::forward<F>(f),std::move(x)); group_exclusive{},std::forward<F1>(f1),std::forward<F2>(f2),
std::move(x));
} }
template<typename F,typename T=element_type> template<typename F1,typename F2,typename T=element_type>
BOOST_FORCEINLINE BOOST_FORCEINLINE
typename std::enable_if< typename std::enable_if<
!std::is_same<T,value_type>::value, !std::is_same<T,value_type>::value,
bool bool
>::type >::type
insert_or_cvisit(element_type&& x, F&& f) insert_and_cvisit(element_type&& x,F1&& f1,F2&& f2)
{ {
return emplace_or_visit_impl( return emplace_and_visit_impl(
group_shared{},std::forward<F>(f),std::move(x)); group_shared{},std::forward<F1>(f1),std::forward<F2>(f2),std::move(x));
} }
template<typename Key> template<typename Key>
@ -1408,23 +1459,59 @@ private:
); );
} }
struct call_construct_and_emplace_and_visit
{
template<typename... Args>
BOOST_FORCEINLINE bool operator()(
concurrent_table* this_,Args&&... args)const
{
return this_->construct_and_emplace_and_visit(
std::forward<Args>(args)...);
}
};
template<typename GroupAccessMode,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_and_visit_flast(
GroupAccessMode access_mode,Args&&... args)
{
return mp11::tuple_apply(
call_construct_and_emplace_and_visit{},
std::tuple_cat(
std::make_tuple(this,access_mode),
tuple_rotate_right<2>(
std::forward_as_tuple(std::forward<Args>(args)...))
)
);
}
template<typename GroupAccessMode,typename F,typename... Args> template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_or_visit( BOOST_FORCEINLINE bool construct_and_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args) GroupAccessMode access_mode,F&& f,Args&&... args)
{
return construct_and_emplace_and_visit(
access_mode,[](const value_type&){},std::forward<F>(f),
std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE bool construct_and_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{ {
auto lck=shared_access(); auto lck=shared_access();
alloc_cted_insert_type<type_policy,Allocator,Args...> x( alloc_cted_insert_type<type_policy,Allocator,Args...> x(
this->al(),std::forward<Args>(args)...); this->al(),std::forward<Args>(args)...);
int res=unprotected_norehash_emplace_or_visit( int res=unprotected_norehash_emplace_and_visit(
access_mode,std::forward<F>(f),type_policy::move(x.value())); access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
type_policy::move(x.value()));
if(BOOST_LIKELY(res>=0))return res!=0; if(BOOST_LIKELY(res>=0))return res!=0;
lck.unlock(); lck.unlock();
rehash_if_full(); rehash_if_full();
return noinline_emplace_or_visit( return noinline_emplace_and_visit(
access_mode,std::forward<F>(f),type_policy::move(x.value())); access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
type_policy::move(x.value()));
} }
template<typename... Args> template<typename... Args>
@ -1442,6 +1529,15 @@ private:
access_mode,std::forward<F>(f),std::forward<Args>(args)...); access_mode,std::forward<F>(f),std::forward<Args>(args)...);
} }
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_NOINLINE bool noinline_emplace_and_visit(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{
return emplace_and_visit_impl(
access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
std::forward<Args>(args)...);
}
struct call_emplace_or_visit_impl struct call_emplace_or_visit_impl
{ {
template<typename... Args> template<typename... Args>
@ -1465,15 +1561,49 @@ private:
); );
} }
struct call_emplace_and_visit_impl
{
template<typename... Args>
BOOST_FORCEINLINE bool operator()(
concurrent_table* this_,Args&&... args)const
{
return this_->emplace_and_visit_impl(std::forward<Args>(args)...);
}
};
template<typename GroupAccessMode,typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit_flast(
GroupAccessMode access_mode,Args&&... args)
{
return mp11::tuple_apply(
call_emplace_and_visit_impl{},
std::tuple_cat(
std::make_tuple(this,access_mode),
tuple_rotate_right<2>(
std::forward_as_tuple(std::forward<Args>(args)...))
)
);
}
template<typename GroupAccessMode,typename F,typename... Args> template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE bool emplace_or_visit_impl( BOOST_FORCEINLINE bool emplace_or_visit_impl(
GroupAccessMode access_mode,F&& f,Args&&... args) GroupAccessMode access_mode,F&& f,Args&&... args)
{
return emplace_and_visit_impl(
access_mode,[](const value_type&){},std::forward<F>(f),
std::forward<Args>(args)...);
}
template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE bool emplace_and_visit_impl(
GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{ {
for(;;){ for(;;){
{ {
auto lck=shared_access(); auto lck=shared_access();
int res=unprotected_norehash_emplace_or_visit( int res=unprotected_norehash_emplace_and_visit(
access_mode,std::forward<F>(f),std::forward<Args>(args)...); access_mode,std::forward<F1>(f1),std::forward<F2>(f2),
std::forward<Args>(args)...);
if(BOOST_LIKELY(res>=0))return res!=0; if(BOOST_LIKELY(res>=0))return res!=0;
} }
rehash_if_full(); rehash_if_full();
@ -1498,6 +1628,16 @@ private:
return true; return true;
} }
template<typename GroupAccessMode,typename F,typename... Args>
BOOST_FORCEINLINE int
unprotected_norehash_emplace_or_visit(
GroupAccessMode access_mode,F&& f,Args&&... args)
{
return unprotected_norehash_emplace_and_visit(
access_mode,[&](const value_type&){},
std::forward<F>(f),std::forward<Args>(args)...);
}
struct reserve_size struct reserve_size
{ {
reserve_size(concurrent_table& x_):x(x_) reserve_size(concurrent_table& x_):x(x_)
@ -1539,10 +1679,10 @@ private:
bool commit_=false; bool commit_=false;
}; };
template<typename GroupAccessMode,typename F,typename... Args> template<typename GroupAccessMode,typename F1,typename F2,typename... Args>
BOOST_FORCEINLINE int BOOST_FORCEINLINE int
unprotected_norehash_emplace_or_visit( unprotected_norehash_emplace_and_visit(
GroupAccessMode access_mode,F&& f,Args&&... args) GroupAccessMode access_mode,F1&& f1,F2&& f2,Args&&... args)
{ {
const auto &k=this->key_from(std::forward<Args>(args)...); const auto &k=this->key_from(std::forward<Args>(args)...);
auto hash=this->hash_for(k); auto hash=this->hash_for(k);
@ -1552,7 +1692,7 @@ private:
startover: startover:
boost::uint32_t counter=insert_counter(pos0); boost::uint32_t counter=insert_counter(pos0);
if(unprotected_visit( if(unprotected_visit(
access_mode,k,pos0,hash,std::forward<F>(f)))return 0; access_mode,k,pos0,hash,std::forward<F2>(f2)))return 0;
reserve_size rsize(*this); reserve_size rsize(*this);
if(BOOST_LIKELY(rsize.succeeded())){ if(BOOST_LIKELY(rsize.succeeded())){
@ -1572,6 +1712,7 @@ private:
this->construct_element(p,std::forward<Args>(args)...); this->construct_element(p,std::forward<Args>(args)...);
rslot.commit(); rslot.commit();
rsize.commit(); rsize.commit();
f1(cast_for(group_exclusive{},type_policy::value_from(*p)));
BOOST_UNORDERED_ADD_STATS(this->cstats.insertion,(pb.length())); BOOST_UNORDERED_ADD_STATS(this->cstats.insertion,(pb.length()));
return 1; return 1;
} }

View File

@ -1,4 +1,4 @@
/* Copyright 2023 Joaquin M Lopez Munoz. /* Copyright 2023-2024 Joaquin M Lopez Munoz.
* Distributed under the Boost Software License, Version 1.0. * Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE_1_0.txt or copy at * (See accompanying file LICENSE_1_0.txt or copy at
* http://www.boost.org/LICENSE_1_0.txt) * http://www.boost.org/LICENSE_1_0.txt)
@ -19,27 +19,28 @@ namespace unordered{
namespace detail{ namespace detail{
namespace foa{ namespace foa{
template<typename Tuple> template<std::size_t Offset,typename Tuple>
using tuple_rotate_right_return_type=mp11::mp_rotate_right_c< using tuple_rotate_right_return_type=mp11::mp_rotate_right_c<
typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type, typename std::remove_cv<typename std::remove_reference<Tuple>::type>::type,
1 Offset
>; >;
template<std::size_t... Is,typename Tuple> template<std::size_t Offset,std::size_t... Is,typename Tuple>
tuple_rotate_right_return_type<Tuple> tuple_rotate_right_return_type<Offset,Tuple>
tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x) tuple_rotate_right_aux(mp11::index_sequence<Is...>,Tuple&& x)
{ {
return tuple_rotate_right_return_type<Tuple>{ return tuple_rotate_right_return_type<Offset,Tuple>{
std::get<(Is+sizeof...(Is)-1)%sizeof...(Is)>(std::forward<Tuple>(x))...}; std::get<(Is+sizeof...(Is)-Offset)%sizeof...(Is)>(
std::forward<Tuple>(x))...};
} }
template<typename Tuple> template<std::size_t Offset=1,typename Tuple>
tuple_rotate_right_return_type<Tuple> tuple_rotate_right(Tuple&& x) tuple_rotate_right_return_type<Offset,Tuple> tuple_rotate_right(Tuple&& x)
{ {
using RawTuple=typename std::remove_cv< using RawTuple=typename std::remove_cv<
typename std::remove_reference<Tuple>::type>::type; typename std::remove_reference<Tuple>::type>::type;
return tuple_rotate_right_aux( return tuple_rotate_right_aux<Offset>(
mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{}, mp11::make_index_sequence<std::tuple_size<RawTuple>::value>{},
std::forward<Tuple>(x)); std::forward<Tuple>(x));
} }

View File

@ -53,6 +53,34 @@ namespace {
return x.emplace_or_cvisit(v.first.x_, v.second.x_, f); return x.emplace_or_cvisit(v.first.x_, v.second.x_, f);
} }
template <typename Container, typename Value, typename F1, typename F2>
bool member_emplace_and_visit(Container& x, Value& v, F1 f1, F2 f2)
{
return x.emplace_and_visit(v.x_, f1, f2);
}
template <
typename Container, typename Key, typename Value, typename F1, typename F2>
bool member_emplace_and_visit(
Container& x, std::pair<Key, Value>& v, F1 f1, F2 f2)
{
return x.emplace_and_visit(v.first.x_, v.second.x_, f1, f2);
}
template <typename Container, typename Value, typename F1, typename F2>
bool member_emplace_and_cvisit(Container& x, Value& v, F1 f1, F2 f2)
{
return x.emplace_and_cvisit(v.x_, f1, f2);
}
template <
typename Container, typename Key, typename Value, typename F1, typename F2>
bool member_emplace_and_cvisit(
Container& x, std::pair<Key, Value>& v, F1 f1, F2 f2)
{
return x.emplace_and_cvisit(v.first.x_, v.second.x_, f1, f2);
}
struct lvalue_emplacer_type struct lvalue_emplacer_type
{ {
template <class T, class X> void call_impl(std::vector<T>& values, X& x) template <class T, class X> void call_impl(std::vector<T>& values, X& x)
@ -133,6 +161,55 @@ namespace {
} }
} lvalue_emplace_or_cvisit; } lvalue_emplace_or_cvisit;
struct lvalue_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_and_cvisit(
x, r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_and_cvisit;
struct lvalue_emplace_or_visit_type struct lvalue_emplace_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -176,6 +253,55 @@ namespace {
} }
} lvalue_emplace_or_visit; } lvalue_emplace_or_visit;
struct lvalue_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = member_emplace_and_visit(
x, r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, value_type_cardinality * x.size());
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_emplace_and_visit;
struct copy_emplacer_type struct copy_emplacer_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -312,6 +438,13 @@ UNORDERED_TEST(
(lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer)) (lvalue_emplace_or_cvisit)(lvalue_emplace_or_visit)(copy_emplacer)(move_emplacer))
((default_generator)(sequential)(limited_range))) ((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
emplace,
((map)(node_map)(set)(node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_emplace_and_cvisit)(lvalue_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on // clang-format on
namespace { namespace {

View File

@ -86,7 +86,7 @@ namespace {
while (!nh.empty()) { while (!nh.empty()) {
auto& o = out[br2++ % out.size()]; auto& o = out[br2++ % out.size()];
typename X::insert_return_type r; typename X::insert_return_type r;
switch (br3++ % 3) { switch (br3++ % 5) {
case 0: case 0:
r = o.insert(std::move(nh)); r = o.insert(std::move(nh));
break; break;
@ -97,13 +97,37 @@ namespace {
(void)v2; (void)v2;
}); });
break; break;
case 2: default: case 2:
r = o.insert_or_cvisit( r = o.insert_or_cvisit(
std::move(nh), [&](arg_visit_type const& v2) { std::move(nh), [&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2)); BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2; (void)v2;
}); });
break; break;
case 3:
r = o.insert_and_visit(
std::move(nh),
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
},
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
case 4: default:
r = o.insert_and_cvisit(
std::move(nh),
[&](arg_visit_type& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
},
[&](arg_visit_type const& v2) {
BOOST_ASSERT(test::get_key<X>(v) == test::get_key<X>(v2));
(void)v2;
});
break;
} }
BOOST_ASSERT(r.inserted || !r.node.empty()); BOOST_ASSERT(r.inserted || !r.node.empty());
nh = std::move(r.node); nh = std::move(r.node);
@ -144,6 +168,20 @@ namespace {
BOOST_TEST(!r.inserted); BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty()); BOOST_TEST(r.node.empty());
} }
{
node_type nh;
auto r = x.insert_and_visit(
std::move(nh), [](value_type const&) {}, [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
{
node_type nh;
auto r = x.insert_and_cvisit(
std::move(nh), [](value_type const&) {}, [](value_type const&) {});
BOOST_TEST(!r.inserted);
BOOST_TEST(r.node.empty());
}
} }
} // namespace } // namespace
@ -156,7 +194,7 @@ UNORDERED_TEST(
UNORDERED_TEST( UNORDERED_TEST(
insert_empty_node_tests, insert_empty_node_tests,
((test_node_map)(test_node_set))) ((test_node_map)(test_node_set)))
// clang-format on // clang-format on
RUN_TESTS() RUN_TESTS()

View File

@ -376,6 +376,62 @@ namespace {
} }
} lvalue_insert_or_cvisit; } lvalue_insert_or_cvisit;
struct lvalue_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_cvisit(
r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(
raii::copy_constructor, value_type_cardinality * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_and_cvisit;
struct lvalue_insert_or_visit_type struct lvalue_insert_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -424,6 +480,61 @@ namespace {
} }
} lvalue_insert_or_visit; } lvalue_insert_or_visit;
struct lvalue_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b =
x.insert_and_visit(r,
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
BOOST_TEST_EQ(raii::copy_constructor, value_type_cardinality * x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} lvalue_insert_and_visit;
struct rvalue_insert_or_cvisit_type struct rvalue_insert_or_cvisit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -470,6 +581,66 @@ namespace {
} }
} rvalue_insert_or_cvisit; } rvalue_insert_or_cvisit;
struct rvalue_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_cvisit(
std::move(r),
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} rvalue_insert_and_cvisit;
struct rvalue_insert_or_visit_type struct rvalue_insert_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -522,6 +693,65 @@ namespace {
} }
} rvalue_insert_or_visit; } rvalue_insert_or_visit;
struct rvalue_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.insert_and_visit(
std::move(r),
[&num_inserts_internal](arg_type& v) {
(void)v;
++num_inserts_internal;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 0u);
if (std::is_same<T, typename X::value_type>::value) {
if (std::is_same<typename X::key_type,
typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
else {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(
raii::move_constructor, value_type_cardinality * x.size());
}
}
} rvalue_insert_and_visit;
struct iterator_range_insert_or_cvisit_type struct iterator_range_insert_or_cvisit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -561,6 +791,58 @@ namespace {
} }
} iterator_range_insert_or_cvisit; } iterator_range_insert_or_cvisit;
struct iterator_range_insert_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(
values2, [&x, &num_inserts, &num_invokes](boost::span<raii_convertible> s) {
x.insert_and_cvisit(s.begin(), s.end(),
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_and_cvisit;
struct iterator_range_insert_or_visit_type struct iterator_range_insert_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -568,6 +850,13 @@ namespace {
static constexpr auto value_type_cardinality = static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value; value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2; std::vector<raii_convertible> values2;
values2.reserve(values.size()); values2.reserve(values.size());
for (auto const& v : values) { for (auto const& v : values) {
@ -578,7 +867,7 @@ namespace {
thread_runner( thread_runner(
values2, [&x, &num_invokes](boost::span<raii_convertible> s) { values2, [&x, &num_invokes](boost::span<raii_convertible> s) {
x.insert_or_visit(s.begin(), s.end(), x.insert_or_visit(s.begin(), s.end(),
[&num_invokes](typename X::value_type const& v) { [&num_invokes](arg_type& v) {
(void)v; (void)v;
++num_invokes; ++num_invokes;
}); });
@ -600,6 +889,58 @@ namespace {
} }
} iterator_range_insert_or_visit; } iterator_range_insert_or_visit;
struct iterator_range_insert_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
static constexpr auto value_type_cardinality =
value_cardinality<typename X::value_type>::value;
// concurrent_flat_set visit is always const access
using arg_type = typename std::conditional<
std::is_same<typename X::key_type, typename X::value_type>::value,
typename X::value_type const,
typename X::value_type
>::type;
std::vector<raii_convertible> values2;
values2.reserve(values.size());
for (auto const& v : values) {
values2.push_back(raii_convertible(v));
}
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(
values2, [&x, &num_inserts, &num_invokes](boost::span<raii_convertible> s) {
x.insert_and_visit(s.begin(), s.end(),
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(
raii::default_constructor, value_type_cardinality * values2.size());
#if (BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 50300) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50500)) || \
(BOOST_WORKAROUND(BOOST_GCC_VERSION, >= 40900) && \
BOOST_WORKAROUND(BOOST_GCC_VERSION, < 50000))
// skip test
#else
BOOST_TEST_EQ(raii::copy_constructor, 0u);
#endif
BOOST_TEST_GT(raii::move_constructor, 0u);
}
} iterator_range_insert_and_visit;
template <class X, class GF, class F> template <class X, class GF, class F>
void insert(X*, GF gen_factory, F inserter, test::random_generator rg) void insert(X*, GF gen_factory, F inserter, test::random_generator rg)
{ {
@ -721,6 +1062,62 @@ namespace {
BOOST_TEST_EQ(raii::copy_assignment, 0u); BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u); BOOST_TEST_EQ(raii::move_assignment, 0u);
} }
{
{
std::atomic<std::uint64_t> num_inserts{0};
std::atomic<std::uint64_t> num_invokes{0};
X x;
thread_runner(dummy,
[&x, &init_list, &num_inserts, &num_invokes](boost::span<raii>) {
x.insert_and_visit(init_list,
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](arg_type& v) {
(void)v;
++num_invokes;
});
x.insert_and_cvisit(
init_list,
[&num_inserts](arg_type& v) {
(void)v;
++num_inserts;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
});
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, (init_list.size() - x.size()) +
(num_threads - 1) * init_list.size() +
num_threads * init_list.size());
BOOST_TEST_EQ(x.size(), reference_cont.size());
BOOST_TEST_EQ(x.size(), x.visit_all([&](value_type const& v) {
BOOST_TEST(reference_cont.contains(get_key(v)));
BOOST_TEST_EQ(v, *reference_cont.find(get_key(v)));
}));
}
BOOST_TEST_GE(raii::default_constructor, 0u);
BOOST_TEST_GE(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, 0u);
BOOST_TEST_GT(raii::destructor, 0u);
BOOST_TEST_EQ(raii::default_constructor + raii::copy_constructor +
raii::move_constructor,
raii::destructor);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
BOOST_TEST_EQ(raii::move_assignment, 0u);
}
} }
@ -735,6 +1132,9 @@ namespace {
x.insert_or_visit({2, 3}, [](value_type&) {}); x.insert_or_visit({2, 3}, [](value_type&) {});
x.insert_or_cvisit({3, 4}, [](value_type const&) {}); x.insert_or_cvisit({3, 4}, [](value_type const&) {});
x.insert_and_visit({4, 5}, [](value_type&) {}, [](value_type&) {});
x.insert_and_cvisit({5, 6}, [](value_type&) {}, [](value_type const&) {});
} }
boost::unordered::concurrent_flat_map<raii, raii>* map; boost::unordered::concurrent_flat_map<raii, raii>* map;
@ -825,7 +1225,8 @@ using test::sequential;
// clang-format off // clang-format off
UNORDERED_TEST( UNORDERED_TEST(
insert_initializer_list, insert_initializer_list,
((map_and_init_list)(node_map_and_init_list)(set_and_init_list))) ((map_and_init_list)(node_map_and_init_list)
(set_and_init_list)(node_set_and_init_list)))
UNORDERED_TEST( UNORDERED_TEST(
insert, insert,
@ -840,6 +1241,16 @@ UNORDERED_TEST(
((default_generator)(sequential)(limited_range))) ((default_generator)(sequential)(limited_range)))
UNORDERED_TEST( UNORDERED_TEST(
insert,
((map)(fancy_map)(node_map)(fancy_node_map)
(set)(fancy_set)(node_set)(fancy_node_set))
((value_type_generator_factory)(init_type_generator_factory))
((lvalue_insert_and_cvisit)(lvalue_insert_and_visit)
(rvalue_insert_and_cvisit)(rvalue_insert_and_visit)
(iterator_range_insert_and_cvisit)(iterator_range_insert_and_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
insert, insert,
((map)(node_map)) ((map)(node_map))
((init_type_generator_factory)) ((init_type_generator_factory))

View File

@ -177,6 +177,53 @@ namespace {
} }
} lvalue_try_emplace_or_cvisit; } lvalue_try_emplace_or_cvisit;
struct lvalue_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
r.first, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_and_cvisit;
struct lvalue_try_emplace_or_visit_type struct lvalue_try_emplace_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -217,6 +264,53 @@ namespace {
} }
} lvalue_try_emplace_or_visit; } lvalue_try_emplace_or_visit;
struct lvalue_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
r.first, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
// don't check move construction count here because of rehashing
BOOST_TEST_GT(raii::move_constructor, 0u);
}
BOOST_TEST_EQ(raii::move_assignment, 0u);
BOOST_TEST_EQ(raii::copy_assignment, 0u);
}
} lvalue_try_emplace_and_visit;
struct rvalue_try_emplace_or_cvisit_type struct rvalue_try_emplace_or_cvisit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -258,6 +352,54 @@ namespace {
} }
} rvalue_try_emplace_or_cvisit; } rvalue_try_emplace_or_cvisit;
struct rvalue_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
std::move(r.first), r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_and_cvisit;
struct rvalue_try_emplace_or_visit_type struct rvalue_try_emplace_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -298,6 +440,53 @@ namespace {
} }
} rvalue_try_emplace_or_visit; } rvalue_try_emplace_or_visit;
struct rvalue_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
std::move(r.first), r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, x.size());
if (std::is_same<T, typename X::value_type>::value) {
BOOST_TEST_EQ(raii::copy_constructor, x.size());
if (is_container_node_based<X>::value) {
BOOST_TEST_EQ(raii::move_constructor, 0u);
}
else{
BOOST_TEST_GE(raii::move_constructor, x.size());
}
} else {
BOOST_TEST_EQ(raii::copy_constructor, 0u);
BOOST_TEST_GE(raii::move_constructor, x.size());
}
}
} rvalue_try_emplace_and_visit;
struct transp_try_emplace_or_cvisit_type struct transp_try_emplace_or_cvisit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -326,6 +515,41 @@ namespace {
} }
} transp_try_emplace_or_cvisit; } transp_try_emplace_or_cvisit;
struct transp_try_emplace_and_cvisit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_cvisit(
r.first.x_, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type const& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_and_cvisit;
struct transp_try_emplace_or_visit_type struct transp_try_emplace_or_visit_type
{ {
template <class T, class X> void operator()(std::vector<T>& values, X& x) template <class T, class X> void operator()(std::vector<T>& values, X& x)
@ -355,6 +579,42 @@ namespace {
} }
} transp_try_emplace_or_visit; } transp_try_emplace_or_visit;
struct transp_try_emplace_and_visit_type
{
template <class T, class X> void operator()(std::vector<T>& values, X& x)
{
std::atomic<std::uint64_t> num_inserts{0}, num_inserts_internal{0};
std::atomic<std::uint64_t> num_invokes{0};
thread_runner(values,
[&x, &num_inserts, &num_inserts_internal, &num_invokes](boost::span<T> s) {
for (auto& r : s) {
bool b = x.try_emplace_and_visit(
r.first.x_, r.second.x_,
[&num_inserts_internal](typename X::value_type& v)
{
(void)v;
++num_inserts_internal;
},
[&num_invokes](typename X::value_type& v) {
(void)v;
++num_invokes;
});
if (b) {
++num_inserts;
}
}
});
BOOST_TEST_EQ(num_inserts, num_inserts_internal);
BOOST_TEST_EQ(num_inserts, x.size());
BOOST_TEST_EQ(num_invokes, values.size() - x.size());
BOOST_TEST_EQ(raii::default_constructor, 2 * x.size());
BOOST_TEST_EQ(raii::copy_constructor, 0u);
}
} transp_try_emplace_and_visit;
template <class X, class G, class F> template <class X, class G, class F>
void try_emplace(X*, G gen, F try_emplacer, test::random_generator rg) void try_emplace(X*, G gen, F try_emplacer, test::random_generator rg)
{ {
@ -417,6 +677,14 @@ UNORDERED_TEST(
(rvalue_try_emplace_or_cvisit)(rvalue_try_emplace_or_visit)) (rvalue_try_emplace_or_cvisit)(rvalue_try_emplace_or_visit))
((default_generator)(sequential)(limited_range))) ((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((map)(node_map))
((value_type_generator)(init_type_generator))
((lvalue_try_emplace_and_cvisit)(lvalue_try_emplace_and_visit)
(rvalue_try_emplace_and_cvisit)(rvalue_try_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
UNORDERED_TEST( UNORDERED_TEST(
try_emplace, try_emplace,
((transp_map)(transp_node_map)) ((transp_map)(transp_node_map))
@ -424,6 +692,13 @@ UNORDERED_TEST(
((transp_try_emplace)(norehash_transp_try_emplace) ((transp_try_emplace)(norehash_transp_try_emplace)
(transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit)) (transp_try_emplace_or_cvisit)(transp_try_emplace_or_visit))
((default_generator)(sequential)(limited_range))) ((default_generator)(sequential)(limited_range)))
UNORDERED_TEST(
try_emplace,
((transp_map)(transp_node_map))
((init_type_generator))
((transp_try_emplace_and_cvisit)(transp_try_emplace_and_visit))
((default_generator)(sequential)(limited_range)))
// clang-format on // clang-format on
RUN_TESTS() RUN_TESTS()