mirror of
https://github.com/boostorg/unordered.git
synced 2025-05-09 23:23:59 +00:00
Sync from upstream.
This commit is contained in:
commit
228fede498
@ -30,6 +30,7 @@ local linux_pipeline(name, image, environment, packages = "", sources = [], arch
|
||||
name: "everything",
|
||||
image: image,
|
||||
environment: environment,
|
||||
privileged: true,
|
||||
commands:
|
||||
[
|
||||
'set -e',
|
||||
|
@ -14,6 +14,11 @@ DRONE_BUILD_DIR=$(pwd)
|
||||
BOOST_BRANCH=develop
|
||||
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
|
||||
|
||||
if [[ $(uname) == "Linux" && ( "$TSAN" == 1 || "$ASAN" == 1 ) ]]; then
|
||||
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
|
||||
sudo sysctl vm.mmap_rnd_bits=28
|
||||
fi
|
||||
|
||||
cd ..
|
||||
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
|
||||
cd boost-root
|
||||
|
@ -6,6 +6,13 @@
|
||||
:github-pr-url: https://github.com/boostorg/unordered/pull
|
||||
:cpp: C++
|
||||
|
||||
== Release 1.87.0
|
||||
|
||||
* Made visitation exclusive-locked within certain
|
||||
`boost::concurrent_flat_set` operations to allow for safe mutable modification of elements
|
||||
({github-pr-url}/265[PR#265^]).
|
||||
* In Visual Studio Natvis, supported any container with an allocator that uses fancy pointers. This applies to any fancy pointer type, as long as the proper Natvis customization point "Intrinsic" functions are written for the fancy pointer type.
|
||||
|
||||
== Release 1.86.0
|
||||
|
||||
* Added container `pmr` aliases when header `<memory_resource>` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type.
|
||||
|
@ -389,9 +389,13 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
|
||||
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
|
||||
only when an internal rehashing is issued.
|
||||
|
||||
Access or modification of an element of a `boost::concurrent_flat_map` passed by reference to a
|
||||
user-provided visitation function do not introduce data races when the visitation function
|
||||
is executed internally by the `boost::concurrent_flat_map`.
|
||||
When executed internally by a `boost::concurrent_flat_map`, the following operations by a
|
||||
user-provided visitation function on the element passed do not introduce data races:
|
||||
|
||||
* Read access to the element.
|
||||
* Non-mutable modification of the element.
|
||||
* Mutable modification of the element (if the container operation executing the visitation function is not const
|
||||
and its name does not contain `cvisit`.)
|
||||
|
||||
Any `boost::concurrent_flat_map operation` that inserts or modifies an element `e`
|
||||
synchronizes with the internal invocation of a visitation function on `e`.
|
||||
|
@ -98,25 +98,35 @@ namespace boost {
|
||||
|
||||
|
||||
// visitation
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f);
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[visit](const key_type& k, F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const key_type& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f);
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[visit](const K& k, F f) const;
|
||||
template<class K, class F> size_t xref:#concurrent_flat_set_cvisit[cvisit](const K& k, F f) const;
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[visit](FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
size_t xref:concurrent_flat_set_bulk_visit[cvisit](FwdIterator first, FwdIterator last, F f) const;
|
||||
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f);
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[visit_all](F f) const;
|
||||
template<class F> size_t xref:#concurrent_flat_set_cvisit_all[cvisit_all](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[visit_all](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
void xref:#concurrent_flat_set_parallel_cvisit_all[cvisit_all](ExecutionPolicy&& policy, F f) const;
|
||||
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f);
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[visit_while](F f) const;
|
||||
template<class F> bool xref:#concurrent_flat_set_cvisit_while[cvisit_while](F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F>
|
||||
bool xref:#concurrent_flat_set_parallel_cvisit_while[visit_while](ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F>
|
||||
@ -340,9 +350,13 @@ prior blocking operations on `x` synchronize with *op*. So, blocking operations
|
||||
An operation is said to be _blocking on rehashing of_ ``__x__`` if it blocks on `x`
|
||||
only when an internal rehashing is issued.
|
||||
|
||||
Access or modification of an element of a `boost::concurrent_flat_set` passed by reference to a
|
||||
user-provided visitation function do not introduce data races when the visitation function
|
||||
is executed internally by the `boost::concurrent_flat_set`.
|
||||
When executed internally by a `boost::concurrent_flat_set`, the following operations by a
|
||||
user-provided visitation function on the element passed do not introduce data races:
|
||||
|
||||
* Read access to the element.
|
||||
* Non-mutable modification of the element.
|
||||
* Mutable modification of the element (if the container operation executing the visitation function is not const
|
||||
and its name does not contain `cvisit`.)
|
||||
|
||||
Any `boost::concurrent_flat_set operation` that inserts or modifies an element `e`
|
||||
synchronizes with the internal invocation of a visitation function on `e`.
|
||||
@ -708,8 +722,10 @@ Concurrency:;; Blocking on `*this`.
|
||||
==== [c]visit
|
||||
|
||||
```c++
|
||||
template<class F> size_t visit(const key_type& k, F f);
|
||||
template<class F> size_t visit(const key_type& k, F f) const;
|
||||
template<class F> size_t cvisit(const key_type& k, F f) const;
|
||||
template<class K, class F> size_t visit(const K& k, F f);
|
||||
template<class K, class F> size_t visit(const K& k, F f) const;
|
||||
template<class K, class F> size_t cvisit(const K& k, F f) const;
|
||||
```
|
||||
@ -725,6 +741,8 @@ Notes:;; The `template<class K, class F>` overloads only participate in overload
|
||||
==== Bulk visit
|
||||
|
||||
```c++
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f);
|
||||
template<class FwdIterator, class F>
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const;
|
||||
template<class FwdIterator, class F>
|
||||
@ -758,6 +776,7 @@ Returns:;; The number of elements visited.
|
||||
==== [c]visit_all
|
||||
|
||||
```c++
|
||||
template<class F> size_t visit_all(F f);
|
||||
template<class F> size_t visit_all(F f) const;
|
||||
template<class F> size_t cvisit_all(F f) const;
|
||||
```
|
||||
@ -772,6 +791,7 @@ Returns:;; The number of elements visited.
|
||||
==== Parallel [c]visit_all
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> void visit_all(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> void cvisit_all(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
@ -792,6 +812,7 @@ Unsequenced execution policies are not allowed.
|
||||
==== [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class F> bool visit_while(F f);
|
||||
template<class F> bool visit_while(F f) const;
|
||||
template<class F> bool cvisit_while(F f) const;
|
||||
```
|
||||
@ -807,6 +828,7 @@ Returns:;; `false` iff `f` ever returns `false`.
|
||||
==== Parallel [c]visit_while
|
||||
|
||||
```c++
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f);
|
||||
template<class ExecutionPolicy, class F> bool visit_while(ExecutionPolicy&& policy, F f) const;
|
||||
template<class ExecutionPolicy, class F> bool cvisit_while(ExecutionPolicy&& policy, F f) const;
|
||||
```
|
||||
|
@ -5,7 +5,13 @@
|
||||
|
||||
== Visual Studio Natvis
|
||||
|
||||
All containers and iterators have custom visualizations in the Natvis framework, as long as their allocator uses regular raw pointers. Any container or iterator with an allocator using fancy pointers does not have a custom visualization right now.
|
||||
All containers and iterators have custom visualizations in the Natvis framework.
|
||||
|
||||
=== Using in your project
|
||||
|
||||
To visualize Boost.Unordered containers in the Natvis framework in your project, simply add the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis] to your Visual Studio project as an "Existing Item".
|
||||
|
||||
=== Visualization structure
|
||||
|
||||
The visualizations mirror those for the standard unordered containers. A container has a maximum of 100 elements displayed at once. Each set element has its item name listed as `[i]`, where `i` is the index in the display, starting at `0`. Each map element has its item name listed as `[\{key-display}]` by default. For example, if the first element is the pair `("abc", 1)`, the item name will be `["abc"]`. This behaviour can be overridden by using the view "ShowElementsByIndex", which switches the map display behaviour to name the elements by index. This same view name is used in the standard unordered containers.
|
||||
|
||||
@ -13,4 +19,8 @@ By default, the closed-addressing containers will show the `[hash_function]` and
|
||||
|
||||
By default, the open-addressing containers will show the `[hash_function]`, `[key_eq]`, `[allocator]`, and the elements. Using the view "simple" shows only the elements, with no other items present. Both the SIMD and the non-SIMD implementations are viewable through the Natvis framework.
|
||||
|
||||
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `\{ end iterator }`.
|
||||
Iterators are displayed similarly to their standard counterparts. An iterator is displayed as though it were the element that it points to. An end iterator is simply displayed as `{ end iterator }`.
|
||||
|
||||
=== Fancy pointers
|
||||
|
||||
The container visualizations also work if you are using fancy pointers in your allocator, such as `boost::interprocess::offset_ptr`. While this is rare, Boost.Unordered has natvis customization points to support any type of fancy pointer. `boost::interprocess::offset_ptr` has support already defined in the Boost.Interprocess library, and you can add support to your own type by following the instructions contained in a comment near the end of the file link:https://github.com/boostorg/unordered/blob/develop/extra/boost_unordered.natvis[/extra/boost_unordered.natvis].
|
||||
|
@ -30,18 +30,50 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::grouped_bucket_array<*>" Inheritable="false">
|
||||
<!--
|
||||
The expression `&**p` is used so the Intrinsic fails to parse for a fancy pointer type.
|
||||
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
|
||||
For a raw pointer, this expression is exactly equivalent to `*p`.
|
||||
For a fancy pointer, this expression will try to call a user-defined `operator*()`, which is not allowed in Natvis, and it will fail.
|
||||
-->
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<!--
|
||||
The casting expression `(xyz_pointer)p` is used so the Intrinsic fails to parse for a fancy pointer type.
|
||||
Only one of the definitions can exist at any given time, so the other must always fail to parse, similar to SFINAE in C++.
|
||||
In this case, `(xyz_pointer)p` is either a no-op for a raw pointer type, or it's an invalid expression.
|
||||
-->
|
||||
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer)p) + n">
|
||||
<Parameter Name="p" Type="bucket_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((bucket_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="bucket_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
|
||||
<Expand>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Variable Name="current_bucket" InitialValue="to_address(&buckets)" />
|
||||
<Variable Name="node" InitialValue="to_address(&current_bucket->next)" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Exec>current_bucket = next(to_address(&buckets), bucket_index)</Exec>
|
||||
<Exec>node = to_address(&current_bucket->next)</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item>node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
<Exec>node = to_address(&node->next)</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
@ -54,14 +86,14 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="size" InitialValue="size_" />
|
||||
<Variable Name="bucket_index" InitialValue="0" />
|
||||
<Variable Name="current_bucket" InitialValue="&buckets[bucket_index]" />
|
||||
<Variable Name="node" InitialValue="current_bucket->next" />
|
||||
<Variable Name="current_bucket" InitialValue="to_address(&buckets)" />
|
||||
<Variable Name="node" InitialValue="to_address(&current_bucket->next)" />
|
||||
<Loop Condition="bucket_index != size">
|
||||
<Exec>current_bucket = &buckets[bucket_index]</Exec>
|
||||
<Exec>node = current_bucket->next</Exec>
|
||||
<Exec>current_bucket = next(to_address(&buckets), bucket_index)</Exec>
|
||||
<Exec>node = to_address(&current_bucket->next)</Exec>
|
||||
<Loop Condition="node != nullptr">
|
||||
<Item Name="[{node->buf.t_.first}]">node->buf.t_</Item>
|
||||
<Exec>node = node->next</Exec>
|
||||
<Exec>node = to_address(&node->next)</Exec>
|
||||
</Loop>
|
||||
<Exec>++bucket_index</Exec>
|
||||
</Loop>
|
||||
@ -99,29 +131,46 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
<Type Name="boost::unordered::detail::iterator_detail::iterator<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::iterator_detail::c_iterator<*>" />
|
||||
<Intrinsic Name="valid" Expression="p != nullptr && itb.p != nullptr && itb.pbg != nullptr" />
|
||||
<DisplayString Condition="valid()">{p->buf.t_}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="node_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="bucket_iterator::bucket_group_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="valid" Expression="to_address(&p) && to_address(&itb.p) && to_address(&itb.pbg)" />
|
||||
<DisplayString Condition="valid()">{to_address(&p)->buf.t_}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">p->buf.t_</ExpandedItem>
|
||||
<ExpandedItem Condition="valid()">to_address(&p)->buf.t_</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- FOA and CFOA helpers -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<*>" Priority="Medium" Inheritable="false">
|
||||
<DisplayString>{*p}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="get" Expression="to_address(&p)" />
|
||||
|
||||
<DisplayString>{*get()}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem>*p</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::element_type<std::pair<*>,*>" Priority="MediumHigh" Inheritable="false">
|
||||
<!-- Manually expand when holding a `std::pair`, otherwise the debugger complains about recursion-->
|
||||
<DisplayString>({p->first}, {p->second})</DisplayString>
|
||||
<Expand>
|
||||
<Item Name="first">p->first</Item>
|
||||
<Item Name="second">p->second</Item>
|
||||
<ExpandedItem>*get()</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
@ -227,10 +276,39 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Type Name="boost::unordered::detail::foa::table<*>" Inheritable="false">
|
||||
<AlternativeType Name="boost::unordered::detail::foa::concurrent_table<*>" />
|
||||
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="arrays_type::value_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="arrays_type::group_type_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer)p) + n">
|
||||
<Parameter Name="p" Type="arrays_type::value_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer)p) + n">
|
||||
<Parameter Name="p" Type="unsigned char*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::value_type_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="arrays_type::value_type*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="next" Optional="true" Expression="((arrays_type::char_pointer*)nullptr)->boost_next(p, n)">
|
||||
<Parameter Name="p" Type="unsigned char*" />
|
||||
<Parameter Name="n" Type="ptrdiff_t" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e">
|
||||
<Parameter Name="e" Type="value_type*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e->p">
|
||||
<Intrinsic Optional="true" Name="get_value" ReturnType="value_type*" Expression="e->get()">
|
||||
<Parameter Name="e" Type="element_type*" />
|
||||
</Intrinsic>
|
||||
|
||||
@ -254,8 +332,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(arrays.groups_)" />
|
||||
<Variable Name="p_" InitialValue="arrays.elements_" />
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(to_address(&arrays.groups_))" />
|
||||
<Variable Name="p_" InitialValue="to_address(&arrays.elements_)" />
|
||||
<Variable Name="first_time" InitialValue="true" />
|
||||
<Variable Name="mask" InitialValue="(int)0" />
|
||||
<Variable Name="n0" InitialValue="(size_t)0" />
|
||||
@ -264,18 +342,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Loop Condition="p_ != nullptr">
|
||||
|
||||
<!-- This if block mirrors the condition in the begin() call -->
|
||||
<If Condition="!first_time || !(arrays.elements_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<If Condition="!first_time || !(p_ && !(to_address(&arrays.groups_)[0].match_occupied() & 0x1))">
|
||||
<Item>*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
|
||||
<Exec>p_ = next(p_, group_type::N)</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
@ -284,9 +362,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Exec>p_ = nullptr</Exec>
|
||||
</If>
|
||||
<Else>
|
||||
<Exec>pc_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>p_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>p_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
|
||||
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
|
||||
</Else>
|
||||
|
||||
</Loop>
|
||||
@ -299,8 +376,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Expand>
|
||||
<Item Name="[stats]" Optional="true">cstats</Item>
|
||||
<CustomListItems MaxItemsPerView="100">
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(arrays.groups_)" />
|
||||
<Variable Name="p_" InitialValue="arrays.elements_" />
|
||||
<Variable Name="pc_" InitialValue="reinterpret_cast<unsigned char*>(to_address(&arrays.groups_))" />
|
||||
<Variable Name="p_" InitialValue="to_address(&arrays.elements_)" />
|
||||
<Variable Name="first_time" InitialValue="true" />
|
||||
<Variable Name="mask" InitialValue="(int)0" />
|
||||
<Variable Name="n0" InitialValue="(size_t)0" />
|
||||
@ -309,18 +386,18 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Loop Condition="p_ != nullptr">
|
||||
|
||||
<!-- This if block mirrors the condition in the begin() call -->
|
||||
<If Condition="!first_time || !(arrays.elements_ && !(arrays.groups_[0].match_occupied() & 0x1))">
|
||||
<If Condition="!first_time || !(p_ && !(to_address(&arrays.groups_)[0].match_occupied() & 0x1))">
|
||||
<Item Name="[{get_value(p_)->first}]">*p_</Item>
|
||||
</If>
|
||||
<Exec>first_time = false</Exec>
|
||||
|
||||
<Exec>n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type)</Exec>
|
||||
<Exec>pc_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>pc_ = next(pc_, -(ptrdiff_t)n0)</Exec>
|
||||
|
||||
<Exec>mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1)</Exec>
|
||||
<Loop Condition="mask == 0">
|
||||
<Exec>pc_ += sizeof(group_type)</Exec>
|
||||
<Exec>p_ += group_type::N</Exec>
|
||||
<Exec>pc_ = next(pc_, sizeof(group_type))</Exec>
|
||||
<Exec>p_ = next(p_, group_type::N)</Exec>
|
||||
<Exec>mask = reinterpret_cast<group_type*>(pc_)->match_occupied()</Exec>
|
||||
</Loop>
|
||||
|
||||
@ -329,9 +406,8 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<Exec>p_ = nullptr</Exec>
|
||||
</If>
|
||||
<Else>
|
||||
<Exec>pc_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>p_ -= (ptrdiff_t)n0</Exec>
|
||||
<Exec>p_ += (ptrdiff_t)n</Exec>
|
||||
<Exec>pc_ = next(pc_, (ptrdiff_t)n)</Exec>
|
||||
<Exec>p_ = next(p_, (ptrdiff_t)n - (ptrdiff_t)n0)</Exec>
|
||||
</Else>
|
||||
|
||||
</Loop>
|
||||
@ -369,12 +445,62 @@ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
<!-- FOA iterators -->
|
||||
|
||||
<Type Name="boost::unordered::detail::foa::table_iterator<*>" Inheritable="false">
|
||||
<Intrinsic Name="valid" Expression="p_ != nullptr && pc_ != nullptr" />
|
||||
<DisplayString Condition="valid()">{*p_}</DisplayString>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="table_element_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="table_element_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="&**p">
|
||||
<Parameter Name="p" Type="char_pointer*" />
|
||||
</Intrinsic>
|
||||
<Intrinsic Name="to_address" Optional="true" Expression="p->boost_to_address()">
|
||||
<Parameter Name="p" Type="char_pointer*" />
|
||||
</Intrinsic>
|
||||
|
||||
<Intrinsic Name="valid" Expression="to_address(&p_) != nullptr && to_address(&pc_) != nullptr" />
|
||||
<DisplayString Condition="valid()">{*to_address(&p_)}</DisplayString>
|
||||
<DisplayString Condition="!valid()">{{ end iterator }}</DisplayString>
|
||||
<Expand>
|
||||
<ExpandedItem Condition="valid()">*p_</ExpandedItem>
|
||||
<ExpandedItem Condition="valid()">*to_address(&p_)</ExpandedItem>
|
||||
</Expand>
|
||||
</Type>
|
||||
|
||||
<!-- Fancy pointer support -->
|
||||
|
||||
<!--
|
||||
To allow your own fancy pointer type to interact with Boost.Unordered Natvis,
|
||||
add the following intrinsics to your type, with the following conditions.
|
||||
|
||||
(Note, this is assuming the presence of a type alias `pointer` for the underlying
|
||||
raw pointer type, and a type alias `difference_type` for your fancy pointer
|
||||
difference type. Substitute whichever names are applicable in your case.)
|
||||
|
||||
`boost_to_address`
|
||||
* Takes no parameters
|
||||
* Returns the raw pointer equivalent to your fancy pointer
|
||||
|
||||
`boost_next`
|
||||
* Parameter 1, an underlying raw pointer of type `pointer`
|
||||
* Parameter 2, an offset of type `difference_type`
|
||||
* Returns the raw pointer equivalent to your fancy pointer, as if you did the following operations
|
||||
1. Convert the incoming raw pointer to your fancy pointer
|
||||
2. Use operator+= to add the offset to the fancy pointer
|
||||
3. Convert back to the raw pointer
|
||||
* Note, you will not actually do these operations as stated. You will do equivalent lower-level operations that emulate having done the above.
|
||||
|
||||
Example
|
||||
```
|
||||
<Type Name="my_fancy_ptr<*>">
|
||||
...
|
||||
<Intrinsic Name="boost_to_address" ReturnType="pointer" Expression="..." />
|
||||
<Intrinsic Name="boost_next" ReturnType="pointer" Expression="...">
|
||||
<Parameter Name="ptr" Type="pointer" />
|
||||
<Parameter Name="offset" Type="difference_type" />
|
||||
</Intrinsic>
|
||||
...
|
||||
</Type>
|
||||
```
|
||||
-->
|
||||
|
||||
</AutoVisualizer>
|
||||
|
@ -227,6 +227,13 @@ namespace boost {
|
||||
return size() == 0;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE size_type visit(key_type const& k, F f) const
|
||||
{
|
||||
@ -241,6 +248,15 @@ namespace boost {
|
||||
return table_.visit(k, f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
BOOST_FORCEINLINE typename std::enable_if<
|
||||
detail::are_transparent<K, hasher, key_equal>::value, size_type>::type
|
||||
@ -259,6 +275,15 @@ namespace boost {
|
||||
return table_.visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_BULK_VISIT_ITERATOR(FwdIterator)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template<class FwdIterator, class F>
|
||||
BOOST_FORCEINLINE
|
||||
size_t visit(FwdIterator first, FwdIterator last, F f) const
|
||||
@ -277,6 +302,12 @@ namespace boost {
|
||||
return table_.visit(first, last, f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_all(f);
|
||||
}
|
||||
|
||||
template <class F> size_type visit_all(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
@ -290,6 +321,16 @@ namespace boost {
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
visit_all(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
table_.visit_all(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
void>::type
|
||||
@ -311,6 +352,12 @@ namespace boost {
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class F> bool visit_while(F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.visit_while(f);
|
||||
}
|
||||
|
||||
template <class F> bool visit_while(F f) const
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
@ -324,6 +371,16 @@ namespace boost {
|
||||
}
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
visit_while(ExecPolicy&& p, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
BOOST_UNORDERED_STATIC_ASSERT_EXEC_POLICY(ExecPolicy)
|
||||
return table_.visit_while(p, f);
|
||||
}
|
||||
|
||||
template <class ExecPolicy, class F>
|
||||
typename std::enable_if<detail::is_execution_policy<ExecPolicy>::value,
|
||||
bool>::type
|
||||
@ -384,14 +441,14 @@ namespace boost {
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type const& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(obj, f);
|
||||
return table_.insert_or_visit(obj, f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
BOOST_FORCEINLINE bool insert_or_visit(value_type&& obj, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.insert_or_cvisit(std::move(obj), f);
|
||||
return table_.insert_or_visit(std::move(obj), f);
|
||||
}
|
||||
|
||||
template <class K, class F>
|
||||
@ -401,7 +458,7 @@ namespace boost {
|
||||
insert_or_visit(K&& k, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
return table_.try_emplace_or_cvisit(std::forward<K>(k), f);
|
||||
return table_.try_emplace_or_visit(std::forward<K>(k), f);
|
||||
}
|
||||
|
||||
template <class InputIterator, class F>
|
||||
@ -409,7 +466,7 @@ namespace boost {
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
for (; first != last; ++first) {
|
||||
table_.emplace_or_cvisit(*first, f);
|
||||
table_.emplace_or_visit(*first, f);
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,7 +474,7 @@ namespace boost {
|
||||
void insert_or_visit(std::initializer_list<value_type> ilist, F f)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_CONST_INVOCABLE(F)
|
||||
this->insert_or_cvisit(ilist.begin(), ilist.end(), f);
|
||||
this->insert_or_visit(ilist.begin(), ilist.end(), f);
|
||||
}
|
||||
|
||||
template <class F>
|
||||
@ -469,7 +526,7 @@ namespace boost {
|
||||
BOOST_FORCEINLINE bool emplace_or_visit(Arg&& arg, Args&&... args)
|
||||
{
|
||||
BOOST_UNORDERED_STATIC_ASSERT_LAST_ARG_CONST_INVOCABLE(Arg, Args...)
|
||||
return table_.emplace_or_cvisit(
|
||||
return table_.emplace_or_visit(
|
||||
std::forward<Arg>(arg), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
@ -1017,6 +1017,11 @@ struct table_arrays
|
||||
rebind<group_type>;
|
||||
using group_type_pointer_traits=boost::pointer_traits<group_type_pointer>;
|
||||
|
||||
// For natvis purposes
|
||||
using char_pointer=
|
||||
typename boost::pointer_traits<value_type_pointer>::template
|
||||
rebind<unsigned char>;
|
||||
|
||||
table_arrays(
|
||||
std::size_t gsi,std::size_t gsm,
|
||||
group_type_pointer pg,value_type_pointer pe):
|
||||
|
@ -45,6 +45,8 @@ path-constant BOOST_UNORDERED_TEST_DIR : . ;
|
||||
|
||||
run quick.cpp ;
|
||||
|
||||
compile natvis_tests.cpp ;
|
||||
|
||||
compile unordered/self_include_tests_obj.cpp
|
||||
: <define>BOOST_UNORDERED_HEADER="boost/unordered_map.hpp"
|
||||
: tl_unordered_map_hpp ;
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2023 Christian Mazakas
|
||||
// Copyright (C) 2023 Joaquin M Lopez Munoz
|
||||
// Copyright (C) 2023-2024 Joaquin M Lopez Munoz
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
|
||||
#include <boost/compat/latch.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
|
||||
@ -27,7 +28,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
@ -971,6 +975,147 @@ namespace {
|
||||
boost::unordered::concurrent_flat_set<raii, transp_hash,
|
||||
transp_key_equal>* transp_set;
|
||||
|
||||
struct mutable_pair
|
||||
{
|
||||
mutable_pair(int first_ = 0, int second_ = 0):
|
||||
first{first_}, second{second_} {}
|
||||
|
||||
int first = 0;
|
||||
mutable int second = 0;
|
||||
};
|
||||
|
||||
struct null_mutable_pair
|
||||
{
|
||||
operator mutable_pair() const { return {0,0}; }
|
||||
};
|
||||
|
||||
struct mutable_pair_hash
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
std::size_t operator()(const mutable_pair& x) const
|
||||
{
|
||||
return boost::hash<int>{}(x.first);
|
||||
}
|
||||
|
||||
std::size_t operator()(const null_mutable_pair&) const
|
||||
{
|
||||
return boost::hash<int>{}(0);
|
||||
}
|
||||
};
|
||||
|
||||
struct mutable_pair_equal_to
|
||||
{
|
||||
using is_transparent = void;
|
||||
|
||||
bool operator()(const mutable_pair& x, const mutable_pair& y) const
|
||||
{
|
||||
return x.first == y.first;
|
||||
}
|
||||
|
||||
bool operator()(const null_mutable_pair&, const mutable_pair& y) const
|
||||
{
|
||||
return 0 == y.first;
|
||||
}
|
||||
|
||||
bool operator()(const mutable_pair& x, const null_mutable_pair&) const
|
||||
{
|
||||
return x.first == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename F>
|
||||
void exclusive_access_for(F f)
|
||||
{
|
||||
std::atomic_int num_started{0};
|
||||
std::atomic_int in_visit{0};
|
||||
boost::compat::latch finish{1};
|
||||
|
||||
auto bound_f = [&] {
|
||||
++num_started;
|
||||
f([&] (const mutable_pair& x) {
|
||||
++in_visit;
|
||||
++x.second;
|
||||
finish.wait();
|
||||
--in_visit;
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
std::thread t1{bound_f}, t2{bound_f};
|
||||
while(num_started != 2) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
BOOST_TEST(in_visit <= 1);
|
||||
finish.count_down();
|
||||
t1.join();
|
||||
t2.join();
|
||||
}
|
||||
|
||||
template<class X>
|
||||
void exclusive_access_set_visit(X*)
|
||||
{
|
||||
using visit_function = std::function<void (const mutable_pair&)>;
|
||||
using returning_visit_function = std::function<bool (const mutable_pair&)>;
|
||||
X x;
|
||||
x.insert({0, 0});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit({0, 0}, f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit(null_mutable_pair{}, f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
mutable_pair a[] = {{0, 0}};
|
||||
x.visit(std::begin(a), std::end(a), f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit_all(f);
|
||||
});
|
||||
exclusive_access_for([&](returning_visit_function f) {
|
||||
x.visit_while(f);
|
||||
});
|
||||
|
||||
#if defined(BOOST_UNORDERED_PARALLEL_ALGORITHMS)
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.visit_all(std::execution::par, f);
|
||||
});
|
||||
exclusive_access_for([&](returning_visit_function f) {
|
||||
x.visit_while(std::execution::par, f);
|
||||
});
|
||||
#endif
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
const mutable_pair p;
|
||||
x.insert_or_visit(p, f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.insert_or_visit({0,0}, f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.insert_or_visit(null_mutable_pair{}, f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
mutable_pair a[] = {{0, 0}};
|
||||
x.insert_or_visit(std::begin(a), std::end(a), f);
|
||||
});
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
std::initializer_list<mutable_pair> il = {{0, 0}};
|
||||
x.insert_or_visit(il, f);
|
||||
});
|
||||
|
||||
exclusive_access_for([&](visit_function f) {
|
||||
x.emplace_or_visit(0, 0, f);
|
||||
});
|
||||
}
|
||||
|
||||
boost::concurrent_flat_set<
|
||||
mutable_pair, mutable_pair_hash, mutable_pair_equal_to>* mutable_set;
|
||||
|
||||
} // namespace
|
||||
|
||||
using test::default_generator;
|
||||
@ -1024,6 +1169,13 @@ UNORDERED_TEST(
|
||||
((sequential))
|
||||
)
|
||||
|
||||
// https://github.com/boostorg/unordered/issues/260
|
||||
|
||||
UNORDERED_TEST(
|
||||
exclusive_access_set_visit,
|
||||
((mutable_set))
|
||||
)
|
||||
|
||||
// clang-format on
|
||||
|
||||
RUN_TESTS()
|
||||
|
187
test/natvis_tests.cpp
Normal file
187
test/natvis_tests.cpp
Normal file
@ -0,0 +1,187 @@
|
||||
// Copyright 2024 Braden Ganetsky
|
||||
// Distributed under the Boost Software License, Version 1.0.
|
||||
// https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
// This test applies to MSVC only. This is a file for manual testing.
|
||||
// Run this test and break manually at the variable called `break_here`.
|
||||
// Inspect the variables using the Visual Studio debugger to test correctness.
|
||||
|
||||
#if !defined(BOOST_MSVC)
|
||||
|
||||
#include <boost/config/pragma_message.hpp>
|
||||
BOOST_PRAGMA_MESSAGE("These tests are for Visual Studio only.")
|
||||
int main() {}
|
||||
|
||||
#else
|
||||
|
||||
#if 0 // Change to `#if 1` to test turning off SIMD optimizations
|
||||
#define BOOST_UNORDERED_DISABLE_SSE2
|
||||
#define BOOST_UNORDERED_DISABLE_NEON
|
||||
#endif
|
||||
|
||||
#define BOOST_UNORDERED_ENABLE_STATS
|
||||
|
||||
#include <boost/core/lightweight_test.hpp>
|
||||
|
||||
#include <boost/interprocess/allocators/allocator.hpp>
|
||||
#include <boost/interprocess/managed_shared_memory.hpp>
|
||||
#include <boost/unordered/concurrent_flat_map.hpp>
|
||||
#include <boost/unordered/concurrent_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_flat_map.hpp>
|
||||
#include <boost/unordered/unordered_flat_set.hpp>
|
||||
#include <boost/unordered/unordered_map.hpp>
|
||||
#include <boost/unordered/unordered_node_map.hpp>
|
||||
#include <boost/unordered/unordered_node_set.hpp>
|
||||
#include <boost/unordered/unordered_set.hpp>
|
||||
#include <boost/uuid/random_generator.hpp>
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
|
||||
// Prevent any "unused" errors
|
||||
template <class... Args> void use(Args&&...) {}
|
||||
|
||||
using map_value_type = std::pair<const std::string, int>;
|
||||
using set_value_type = std::string;
|
||||
|
||||
template <class Tester> void natvis_test(Tester& tester)
|
||||
{
|
||||
// clang-format off
|
||||
auto fca_map = tester.template construct_map<boost::unordered_map>();
|
||||
auto fca_multimap = tester.template construct_map<boost::unordered_multimap>();
|
||||
auto fca_set = tester.template construct_set<boost::unordered_set>();
|
||||
auto fca_multiset = tester.template construct_set<boost::unordered_multiset>();
|
||||
|
||||
auto foa_flat_map = tester.template construct_map<boost::unordered_flat_map>();
|
||||
auto foa_flat_set = tester.template construct_set<boost::unordered_flat_set>();
|
||||
auto foa_node_map = tester.template construct_map<boost::unordered_node_map>();
|
||||
auto foa_node_set = tester.template construct_set<boost::unordered_node_set>();
|
||||
|
||||
auto cfoa_flat_map = tester.template construct_map<boost::concurrent_flat_map>();
|
||||
auto cfoa_flat_set = tester.template construct_set<boost::concurrent_flat_set>();
|
||||
// clang-format on
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
const auto str = std::to_string(i * 2);
|
||||
const auto num = i * 11;
|
||||
|
||||
fca_map->emplace(str, num);
|
||||
fca_multimap->emplace(str, num);
|
||||
fca_multimap->emplace(str, num + 1);
|
||||
foa_flat_map->emplace(str, num);
|
||||
foa_node_map->emplace(str, num);
|
||||
cfoa_flat_map->emplace(str, num);
|
||||
|
||||
fca_set->emplace(str);
|
||||
fca_multiset->emplace(str);
|
||||
fca_multiset->emplace(str);
|
||||
foa_flat_set->emplace(str);
|
||||
foa_node_set->emplace(str);
|
||||
cfoa_flat_set->emplace(str);
|
||||
}
|
||||
|
||||
auto fca_map_begin = fca_map->begin();
|
||||
auto fca_map_end = fca_map->end();
|
||||
auto fca_multimap_begin = fca_multimap->begin();
|
||||
auto fca_multimap_end = fca_multimap->end();
|
||||
auto fca_set_begin = fca_set->begin();
|
||||
auto fca_set_end = fca_set->end();
|
||||
auto fca_multiset_begin = fca_multiset->begin();
|
||||
auto fca_multiset_end = fca_multiset->end();
|
||||
|
||||
auto foa_flat_map_begin = foa_flat_map->begin();
|
||||
auto foa_flat_map_end = foa_flat_map->end();
|
||||
auto foa_flat_set_begin = foa_flat_set->begin();
|
||||
auto foa_flat_set_end = foa_flat_set->end();
|
||||
auto foa_node_map_begin = foa_node_map->begin();
|
||||
auto foa_node_map_end = foa_node_map->end();
|
||||
auto foa_node_set_begin = foa_node_set->begin();
|
||||
auto foa_node_set_end = foa_node_set->end();
|
||||
|
||||
use(cfoa_flat_map, cfoa_flat_set);
|
||||
use(fca_map_begin, fca_map_end, fca_multimap_begin, fca_multimap_end,
|
||||
fca_set_begin, fca_set_end, fca_multiset_begin, fca_multiset_end);
|
||||
use(foa_flat_map_begin, foa_flat_map_end, foa_flat_set_begin,
|
||||
foa_flat_set_end, foa_node_map_begin, foa_node_map_end, foa_node_set_begin,
|
||||
foa_node_set_end);
|
||||
|
||||
int break_here = 0;
|
||||
use(break_here);
|
||||
}
|
||||
|
||||
class offset_ptr_tester_
|
||||
{
|
||||
static constexpr std::size_t SEGMENT_SIZE = 64 * 1024;
|
||||
std::string segment_name = to_string(boost::uuids::random_generator()());
|
||||
boost::interprocess::managed_shared_memory segment{
|
||||
boost::interprocess::create_only, segment_name.c_str(), SEGMENT_SIZE};
|
||||
|
||||
using map_allocator = boost::interprocess::allocator<map_value_type,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
using set_allocator = boost::interprocess::allocator<set_value_type,
|
||||
boost::interprocess::managed_shared_memory::segment_manager>;
|
||||
|
||||
template <template <class...> class MapTemplate>
|
||||
using map_type = MapTemplate<std::string, int, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, map_allocator>;
|
||||
template <template <class...> class SetTemplate>
|
||||
using set_type = SetTemplate<std::string, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, set_allocator>;
|
||||
|
||||
public:
|
||||
offset_ptr_tester_()
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(segment_name.c_str());
|
||||
}
|
||||
~offset_ptr_tester_()
|
||||
{
|
||||
boost::interprocess::shared_memory_object::remove(segment_name.c_str());
|
||||
}
|
||||
|
||||
template <template <class...> class MapTemplate>
|
||||
map_type<MapTemplate>* construct_map()
|
||||
{
|
||||
return &*segment.construct<map_type<MapTemplate> >(
|
||||
typeid(map_type<MapTemplate>).name())(
|
||||
map_allocator(segment.get_segment_manager()));
|
||||
}
|
||||
template <template <class...> class SetTemplate>
|
||||
set_type<SetTemplate>* construct_set()
|
||||
{
|
||||
return &*segment.construct<set_type<SetTemplate> >(
|
||||
typeid(set_type<SetTemplate>).name())(
|
||||
set_allocator(segment.get_segment_manager()));
|
||||
}
|
||||
} offset_ptr_tester;
|
||||
|
||||
class default_tester_
|
||||
{
|
||||
template <template <class...> class MapTemplate>
|
||||
using map_type = MapTemplate<std::string, int, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, std::allocator<map_value_type> >;
|
||||
template <template <class...> class SetTemplate>
|
||||
using set_type = SetTemplate<std::string, boost::hash<std::string>,
|
||||
std::equal_to<std::string>, std::allocator<set_value_type> >;
|
||||
|
||||
public:
|
||||
template <template <class...> class MapTemplate>
|
||||
std::unique_ptr<map_type<MapTemplate> > construct_map()
|
||||
{
|
||||
return std::make_unique<map_type<MapTemplate> >();
|
||||
}
|
||||
template <template <class...> class SetTemplate>
|
||||
std::unique_ptr<set_type<SetTemplate> > construct_set()
|
||||
{
|
||||
return std::make_unique<set_type<SetTemplate> >();
|
||||
}
|
||||
} default_tester;
|
||||
|
||||
int main()
|
||||
{
|
||||
natvis_test(default_tester);
|
||||
natvis_test(offset_ptr_tester);
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
#endif // !defined(BOOST_MSVC)
|
Loading…
x
Reference in New Issue
Block a user