diff --git a/CMakeLists.txt b/CMakeLists.txt index b8f2a176..9bcd6769 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,15 @@ target_link_libraries(boost_unordered Boost::throw_exception ) +if(CMAKE_VERSION VERSION_GREATER 3.18 AND CMAKE_GENERATOR MATCHES "Visual Studio") + + file(GLOB_RECURSE boost_unordered_IDEFILES CONFIGURE_DEPENDS include/*.hpp) + source_group(TREE ${PROJECT_SOURCE_DIR}/include FILES ${boost_unordered_IDEFILES} PREFIX "Header Files") + list(APPEND boost_unordered_IDEFILES extra/boost_unordered.natvis) + target_sources(boost_unordered PRIVATE ${boost_unordered_IDEFILES}) + +endif() + target_compile_features(boost_unordered INTERFACE cxx_std_11) if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt") diff --git a/doc/unordered.adoc b/doc/unordered.adoc index 1febb283..8f339623 100644 --- a/doc/unordered.adoc +++ b/doc/unordered.adoc @@ -18,6 +18,7 @@ include::unordered/concurrent.adoc[] include::unordered/hash_quality.adoc[] include::unordered/compliance.adoc[] include::unordered/structures.adoc[] +include::unordered/debuggability.adoc[] include::unordered/benchmarks.adoc[] include::unordered/rationale.adoc[] include::unordered/ref.adoc[] diff --git a/doc/unordered/changes.adoc b/doc/unordered/changes.adoc index 56a2dd4c..ffdaf242 100644 --- a/doc/unordered/changes.adoc +++ b/doc/unordered/changes.adoc @@ -11,6 +11,7 @@ * Added container `pmr` aliases when header `` is available. The alias `boost::unordered::pmr::[container]` refers to `boost::unordered::[container]` with a `std::pmr::polymorphic_allocator` allocator type. * Equipped open-addressing and concurrent containers to internally calculate and provide statistical metrics affected by the quality of the hash function. This functionality is enabled by the global macro `BOOST_UNORDERED_ENABLE_STATS`. * Avalanching hash functions must now be marked via an `is_avalanching` typedef with an embedded `value` constant set to `true` (typically, defining `is_avalanching` as `std::true_type`). `using is_avalanching = void` is deprecated but allowed for backwards compatibility. +* Added Visual Studio Natvis framework custom visualizations for containers and iterators. This works for all containers with an allocator using raw pointers. In this release, containers and iterators are not supported if their allocator uses fancy pointers. This may be addressed in later releases. == Release 1.85.0 diff --git a/doc/unordered/debuggability.adoc b/doc/unordered/debuggability.adoc new file mode 100644 index 00000000..cd40e498 --- /dev/null +++ b/doc/unordered/debuggability.adoc @@ -0,0 +1,16 @@ +[#debuggability] +:idprefix: debuggability_ + += Debuggability + +== 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. + +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. + +By default, the closed-addressing containers will show the `[hash_function]` and `[key_eq]`, the `[spare_hash_function]` and `[spare_key_eq]` if applicable, the `[allocator]`, and the elements. Using the view "detailed" adds the `[bucket_count]` and `[max_load_factor]`. Conversely, using the view "simple" shows only the elements, with no other items present. + +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 }`. diff --git a/extra/boost_unordered.natvis b/extra/boost_unordered.natvis new file mode 100644 index 00000000..d4dbc186 --- /dev/null +++ b/extra/boost_unordered.natvis @@ -0,0 +1,380 @@ + + + + + + + + + + + + + + + + + + + + + hash(active_idx()) + key_eq(active_idx()) + hash(spare_idx()) + key_eq(spare_idx()) + + + + + + + + + + + + current_bucket = &buckets[bucket_index] + node = current_bucket->next + + node->buf.t_ + node = node->next + + ++bucket_index + + + + + + + + + + + + + + current_bucket = &buckets[bucket_index] + node = current_bucket->next + + node->buf.t_ + node = node->next + + ++bucket_index + + + + + + + + + + {{ size={table_.size_} }} + + table_.buckets_.size_ + table_.mlf_ + *reinterpret_cast<table::functions*>(&table_) + *reinterpret_cast<table::bucket_array_type::node_allocator_type*>(&table_.buckets_) + table_.buckets_ + + + + + + {{ size={table_.size_} }} + + table_.buckets_.size_ + table_.mlf_ + *reinterpret_cast<table::functions*>(&table_) + *reinterpret_cast<table::bucket_array_type::node_allocator_type*>(&table_.buckets_) + table_.buckets_,view(MapHelper) + + + + + + + + + {p->buf.t_} + {{ end iterator }} + + p->buf.t_ + + + + + + + {*p} + + *p + + + + + + ({p->first}, {p->second}) + + p->first + p->second + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + stats + + insertion + successful_lookup + unsuccessful_lookup + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ count = {n} }} + + n + + {{ average = {average(0)} }} + + average(0) + variance(0) + deviation(0) + + + + {{ average = {average(1)} }} + + average(1) + variance(1) + deviation(1) + + + + + + + + + + + + + + + + + + + + + + + + + + + cstats + + + + + + + + + + + + + *p_ + + first_time = false + + n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type) + pc_ -= (ptrdiff_t)n0 + + mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1) + + pc_ += sizeof(group_type) + p_ += group_type::N + mask = reinterpret_cast<group_type*>(pc_)->match_occupied() + + + n = countr_zero(mask) + + p_ = nullptr + + + pc_ += (ptrdiff_t)n + p_ -= (ptrdiff_t)n0 + p_ += (ptrdiff_t)n + + + + + + + + + + + cstats + + + + + + + + + + + + + *p_ + + first_time = false + + n0 = reinterpret_cast<uintptr_t>(pc_) % sizeof(group_type) + pc_ -= (ptrdiff_t)n0 + + mask = (reinterpret_cast<group_type*>(pc_)->match_occupied() >> (n0+1)) << (n0+1) + + pc_ += sizeof(group_type) + p_ += group_type::N + mask = reinterpret_cast<group_type*>(pc_)->match_occupied() + + + n = countr_zero(mask) + + p_ = nullptr + + + pc_ += (ptrdiff_t)n + p_ -= (ptrdiff_t)n0 + p_ += (ptrdiff_t)n + + + + + + + + + + + + + + {{ size={table_.size_ctrl.size} }} + + *reinterpret_cast<hasher*>(static_cast<table_type::super::base1*>(&table_)) + *reinterpret_cast<key_equal*>(static_cast<table_type::super::base2*>(&table_)) + *reinterpret_cast<allocator_type*>(static_cast<table_type::super::base3*>(&table_)) + table_ + + + + + + + {{ size={table_.size_ctrl.size} }} + + *reinterpret_cast<hasher*>(static_cast<table_type::super::base1*>(&table_)) + *reinterpret_cast<key_equal*>(static_cast<table_type::super::base2*>(&table_)) + *reinterpret_cast<allocator_type*>(static_cast<table_type::super::base3*>(&table_)) + table_,view(MapHelper) + + + + + + + + {*p_} + {{ end iterator }} + + *p_ + + + + diff --git a/include/boost/unordered/detail/foa/core.hpp b/include/boost/unordered/detail/foa/core.hpp index db73a866..513b433d 100644 --- a/include/boost/unordered/detail/foa/core.hpp +++ b/include/boost/unordered/detail/foa/core.hpp @@ -1413,6 +1413,12 @@ __declspec(empty_bases) /* activate EBO with multiple inheritance */ table_core:empty_value,empty_value,empty_value { +private: + // These `baseN` aliases are for debugger visualizations, like natvis + using base1=empty_value; + using base2=empty_value; + using base3=empty_value; + public: using type_policy=TypePolicy; using group_type=Group;