From 4e65777b5ad476a7ef8f59fdededf168579be982 Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 22 Apr 2021 15:16:48 +0200 Subject: [PATCH 01/14] Started to add fancy pointer support to the sparse array class and a lot of tests to ensure that nothing is broken. --- include/tsl/sparse_hash.h | 32 ++++-- tests/CMakeLists.txt | 27 +++-- tests/fancy_pointer/CustomAllocator.h | 54 ++++++++++ tests/fancy_pointer/main.cpp | 7 ++ tests/fancy_pointer/sparse_array_tests.cpp | 113 +++++++++++++++++++++ 5 files changed, 216 insertions(+), 17 deletions(-) create mode 100644 tests/fancy_pointer/CustomAllocator.h create mode 100644 tests/fancy_pointer/main.cpp create mode 100644 tests/fancy_pointer/sparse_array_tests.cpp diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 96f754c..9a524a8 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -40,6 +40,7 @@ #include #include "sparse_growth_policy.h" +#include #ifdef __INTEL_COMPILER #include // For _popcnt32 and _popcnt64 @@ -177,6 +178,16 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } } // namespace detail_popcount namespace detail_sparse_hash { +/** This is a quick and dirty way of adding to_address to the code. + * In the long run the "real" function must be inserted without boost dependability. + * @tparam T + * @param v + * @return + */ +template +auto to_address(T v) { + return boost::to_address(v); +} template struct make_void { @@ -293,8 +304,11 @@ class sparse_array { using value_type = T; using size_type = std::uint_least8_t; using allocator_type = Allocator; - using iterator = value_type *; - using const_iterator = const value_type *; + using allocator_traits = std::allocator_traits; + using pointer = typename allocator_traits::pointer; + using const_pointer = typename allocator_traits::const_pointer; + using iterator = pointer; + using const_iterator = const_pointer; private: static const size_type CAPACITY_GROWTH_STEP = @@ -673,18 +687,18 @@ class sparse_array { private: template - static void construct_value(allocator_type &alloc, value_type *value, + static void construct_value(allocator_type &alloc, pointer value, Args &&... value_args) { std::allocator_traits::construct( - alloc, value, std::forward(value_args)...); + alloc, to_address(value), std::forward(value_args)...); } - static void destroy_value(allocator_type &alloc, value_type *value) noexcept { - std::allocator_traits::destroy(alloc, value); + static void destroy_value(allocator_type &alloc, pointer value) noexcept { + std::allocator_traits::destroy(alloc, to_address(value)); } static void destroy_and_deallocate_values( - allocator_type &alloc, value_type *values, size_type nb_values, + allocator_type &alloc, pointer values, size_type nb_values, size_type capacity_values) noexcept { for (size_type i = 0; i < nb_values; i++) { destroy_value(alloc, values + i); @@ -808,7 +822,7 @@ class sparse_array { size_type new_capacity, Args &&... value_args) { tsl_sh_assert(new_capacity > m_nb_elements); - value_type *new_values = alloc.allocate(new_capacity); + pointer new_values = alloc.allocate(new_capacity); // Allocate should throw if there is a failure tsl_sh_assert(new_values != nullptr); @@ -944,7 +958,7 @@ class sparse_array { } private: - value_type *m_values; + pointer m_values; bitmap_type m_bitmap_vals; bitmap_type m_bitmap_deleted_vals; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 26cea96..fa45ae9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -2,25 +2,36 @@ cmake_minimum_required(VERSION 3.8) project(tsl_sparse_map_tests) -add_executable(tsl_sparse_map_tests "main.cpp" - "custom_allocator_tests.cpp" - "policy_tests.cpp" - "popcount_tests.cpp" - "sparse_map_tests.cpp" - "sparse_set_tests.cpp") +add_executable(tsl_sparse_map_tests "main.cpp" + "custom_allocator_tests.cpp" + "policy_tests.cpp" + "popcount_tests.cpp" + "sparse_map_tests.cpp" + "sparse_set_tests.cpp") + +add_executable(tsl_fancy_pointer_tests "fancy_pointer/main.cpp" + "fancy_pointer/sparse_array_tests.cpp" + "fancy_pointer/sparse_hash_tests.cpp" + "fancy_pointer/CustomAllocator.h") + target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) +target_compile_features(tsl_fancy_pointer_tests PRIVATE cxx_std_11) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") target_compile_options(tsl_sparse_map_tests PRIVATE -Werror -Wall -Wextra -Wold-style-cast -DTSL_DEBUG -UNDEBUG) + target_compile_options(tsl_fancy_pointer_tests PRIVATE -Werror -Wall -Wextra -Wold-style-cast -DTSL_DEBUG -UNDEBUG) elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") target_compile_options(tsl_sparse_map_tests PRIVATE /bigobj /WX /W3 /DTSL_DEBUG /UNDEBUG) + target_compile_options(tsl_fancy_pointer_tests PRIVATE /bigobj /WX /W3 /DTSL_DEBUG /UNDEBUG) endif() # Boost::unit_test_framework find_package(Boost 1.54.0 REQUIRED COMPONENTS unit_test_framework) -target_link_libraries(tsl_sparse_map_tests PRIVATE Boost::unit_test_framework) +target_link_libraries(tsl_sparse_map_tests PRIVATE Boost::unit_test_framework) +target_link_libraries(tsl_fancy_pointer_tests PRIVATE Boost::unit_test_framework) # tsl::sparse_map add_subdirectory(../ ${CMAKE_CURRENT_BINARY_DIR}/tsl) -target_link_libraries(tsl_sparse_map_tests PRIVATE tsl::sparse_map) +target_link_libraries(tsl_sparse_map_tests PRIVATE tsl::sparse_map) +target_link_libraries(tsl_fancy_pointer_tests PRIVATE tsl::sparse_map) diff --git a/tests/fancy_pointer/CustomAllocator.h b/tests/fancy_pointer/CustomAllocator.h new file mode 100644 index 0000000..d6a9655 --- /dev/null +++ b/tests/fancy_pointer/CustomAllocator.h @@ -0,0 +1,54 @@ +// +// Created by lukas on 22.04.21. +// + +#ifndef TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H +#define TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H + +#include + +/** A custom allocator that simply wrapes all pointers into boost offset ptr. +* +* @tparam T +*/ +template +struct OffsetAllocator { + using value_type = T; + template using offset_ptr = boost::interprocess::offset_ptr

; + using pointer = offset_ptr; + using const_pointer = const offset_ptr; + using void_pointer = offset_ptr; + using const_void_pointer = const offset_ptr; + using difference_type = typename offset_ptr::difference_type; + + OffsetAllocator() noexcept = default; + + OffsetAllocator(OffsetAllocator const &) noexcept = default; + + OffsetAllocator &operator=(OffsetAllocator const &) noexcept = default; + + OffsetAllocator(OffsetAllocator &&) noexcept = default; + + OffsetAllocator &operator=(OffsetAllocator &&) noexcept = default; + + template + OffsetAllocator(OffsetAllocator) noexcept {} + + pointer allocate(std::size_t n) { + return pointer(static_cast(::operator new(n*sizeof(T)))); + } + + void deallocate(pointer p, std::size_t) noexcept { + ::operator delete(p.get()); + } + + friend bool operator==(OffsetAllocator const &, OffsetAllocator const &) noexcept { + return true; + } + + friend bool operator!=(OffsetAllocator const &l, OffsetAllocator const &r) noexcept { + return !(l == r); + } +}; + +#endif //TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H \ No newline at end of file diff --git a/tests/fancy_pointer/main.cpp b/tests/fancy_pointer/main.cpp new file mode 100644 index 0000000..4ac4edb --- /dev/null +++ b/tests/fancy_pointer/main.cpp @@ -0,0 +1,7 @@ +// +// Created by lukas on 22.04.21. +// +#define BOOST_TEST_MODULE sparse_fancy_pointer_tests +#define BOOST_TEST_DYN_LINK + +#include \ No newline at end of file diff --git a/tests/fancy_pointer/sparse_array_tests.cpp b/tests/fancy_pointer/sparse_array_tests.cpp new file mode 100644 index 0000000..e0fec4a --- /dev/null +++ b/tests/fancy_pointer/sparse_array_tests.cpp @@ -0,0 +1,113 @@ +// +// Created by lukas on 22.04.21. +// + +#include +#include +#include "CustomAllocator.h" + +// Globals +constexpr auto MAX_INDEX = 32; //BITMAP_NB_BITS + +/* templated tests + * T is a struct with T::Array and T::Allocator. +*/ +template +void compilation() { + typename T::Array test; +} + +template +void construction() { + typename T::Allocator a; + typename T::Array test(MAX_INDEX, a); + test.clear(a); //needed because destructor asserts +} + +namespace details { + template + auto generate_test_array(typename T::Allocator &a) { + typename T::Array arr(MAX_INDEX, a); + for (std::size_t i = 0; i < MAX_INDEX; ++i) { + arr.set(a, i, i); + } + return arr; + } + + template + auto generate_check_for_test_array() { + std::vector check(MAX_INDEX); + for (std::size_t i = 0; i < MAX_INDEX; ++i) { + check[i] = i; + } + return check; + } +} + +template +void set() { + typename T::Allocator a; + auto test = details::generate_test_array(a); + auto check = details::generate_check_for_test_array(); + BOOST_TEST_REQUIRE(std::equal(test.begin(), test.end(), check.begin()), + "'set' did not create the correct order of items"); + test.clear(a); //needed because destructor asserts +} + +template +void copy_construction() { + typename T::Allocator a; + //needs to be its own line, otherwise the move-construction would take place + auto test = details::generate_test_array(a); + typename T::Array copy(test, a); + auto check = details::generate_check_for_test_array(); + BOOST_TEST_REQUIRE(std::equal(copy.begin(), copy.end(), check.begin()), + "'copy' changed the order of the items"); + test.clear(a); + copy.clear(a); +} + +template +void move_construction() { + typename T::Allocator a; + auto moved_to(details::generate_test_array(a)); + auto check = details::generate_check_for_test_array(); + BOOST_TEST_REQUIRE(std::equal(moved_to.begin(), moved_to.end(), check.begin()), + "'move' changed the order of the items"); + check.clear(a); +} + + + +// Template test structs +template +struct STD { + using Allocator = std::allocator; + using Array = tsl::detail_sparse_hash::sparse_array, Sparsity>; +}; + +template +struct CUSTOM { + using Allocator = OffsetAllocator; + using Array = tsl::detail_sparse_hash::sparse_array, Sparsity>; +}; + + +/* Tests. + * I don't use the boost template test cases because with this I can set the title of every test case. + */ +BOOST_AUTO_TEST_SUITE(sparse_array_tests) + +BOOST_AUTO_TEST_CASE(std_alloc_compile) {compilation>();} +BOOST_AUTO_TEST_CASE(std_alloc_construction) {construction>();} +BOOST_AUTO_TEST_CASE(std_alloc_set) {set>();} +BOOST_AUTO_TEST_CASE(std_alloc_copy_construction) {copy_construction>();} +BOOST_AUTO_TEST_CASE(std_alloc_move_construction) {copy_construction>();} + +BOOST_AUTO_TEST_CASE(custom_alloc_compile) {compilation>();} +BOOST_AUTO_TEST_CASE(custom_alloc_construction) {construction>();} +BOOST_AUTO_TEST_CASE(custom_alloc_set) {set>();} +BOOST_AUTO_TEST_CASE(custom_alloc_copy_construction) {copy_construction>();} +BOOST_AUTO_TEST_CASE(custom_alloc_move_construction) {copy_construction>();} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 083182e196caee3dbaf517185653c4f539eedc3a Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 22 Apr 2021 15:40:13 +0200 Subject: [PATCH 02/14] Fixxed a bug in the move construction test. --- tests/fancy_pointer/sparse_array_tests.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/fancy_pointer/sparse_array_tests.cpp b/tests/fancy_pointer/sparse_array_tests.cpp index e0fec4a..687e807 100644 --- a/tests/fancy_pointer/sparse_array_tests.cpp +++ b/tests/fancy_pointer/sparse_array_tests.cpp @@ -70,11 +70,13 @@ void copy_construction() { template void move_construction() { typename T::Allocator a; - auto moved_to(details::generate_test_array(a)); + //two lines needed. Otherwise move/copy elision + auto moved_from = details::generate_test_array(a); + typename T::Array moved_to(std::move(moved_from)); auto check = details::generate_check_for_test_array(); BOOST_TEST_REQUIRE(std::equal(moved_to.begin(), moved_to.end(), check.begin()), "'move' changed the order of the items"); - check.clear(a); + moved_to.clear(a); } @@ -102,12 +104,12 @@ BOOST_AUTO_TEST_CASE(std_alloc_compile) {compilation>();} BOOST_AUTO_TEST_CASE(std_alloc_construction) {construction>();} BOOST_AUTO_TEST_CASE(std_alloc_set) {set>();} BOOST_AUTO_TEST_CASE(std_alloc_copy_construction) {copy_construction>();} -BOOST_AUTO_TEST_CASE(std_alloc_move_construction) {copy_construction>();} +BOOST_AUTO_TEST_CASE(std_alloc_move_construction) {move_construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_compile) {compilation>();} BOOST_AUTO_TEST_CASE(custom_alloc_construction) {construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_set) {set>();} BOOST_AUTO_TEST_CASE(custom_alloc_copy_construction) {copy_construction>();} -BOOST_AUTO_TEST_CASE(custom_alloc_move_construction) {copy_construction>();} +BOOST_AUTO_TEST_CASE(custom_alloc_move_construction) {move_construction>();} BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 34b0549c0d9d61390e87bf9502308e8b25bca3fd Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 29 Apr 2021 13:52:20 +0200 Subject: [PATCH 03/14] Started to work on sparse_hash --- include/tsl/sparse_hash.h | 20 +++-- tests/fancy_pointer/sparse_hash_tests.cpp | 97 +++++++++++++++++++++++ 2 files changed, 109 insertions(+), 8 deletions(-) create mode 100644 tests/fancy_pointer/sparse_hash_tests.cpp diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 9a524a8..69a9b02 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -40,6 +40,7 @@ #include #include "sparse_growth_policy.h" +//quick and dirty #include #ifdef __INTEL_COMPILER @@ -1023,15 +1024,15 @@ class sparse_hash : private Allocator, using key_type = typename KeySelect::key_type; using value_type = ValueType; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; using hasher = Hash; using key_equal = KeyEqual; using allocator_type = Allocator; - using reference = value_type &; - using const_reference = const value_type &; - using pointer = value_type *; - using const_pointer = const value_type *; + using reference = value_type &; //does it need change? + using const_reference = const value_type &; //does it need change? + using size_type = typename std::allocator_traits::size_type; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + using difference_type = typename std::allocator_traits::difference_type; using iterator = sparse_iterator; using const_iterator = sparse_iterator; @@ -1117,6 +1118,7 @@ class sparse_hash : private Allocator, reference operator*() const { return *m_sparse_array_it; } + //with fancy pointers addressof might be problematic. pointer operator->() const { return std::addressof(*m_sparse_array_it); } sparse_iterator &operator++() { @@ -1357,12 +1359,14 @@ class sparse_hash : private Allocator, */ iterator begin() noexcept { auto begin = m_sparse_buckets_data.begin(); - while (begin != m_sparse_buckets_data.end() && begin->empty()) { + //fancy pointers seem to have a problem with "->" in this context + while (begin != m_sparse_buckets_data.end() && (*begin).empty()) { ++begin; } + //fancy pointers seem to have a problem with "->" in this context return iterator(begin, (begin != m_sparse_buckets_data.end()) - ? begin->begin() + ? (*begin).begin() : nullptr); } diff --git a/tests/fancy_pointer/sparse_hash_tests.cpp b/tests/fancy_pointer/sparse_hash_tests.cpp new file mode 100644 index 0000000..5536afc --- /dev/null +++ b/tests/fancy_pointer/sparse_hash_tests.cpp @@ -0,0 +1,97 @@ +// +// Created by lukas on 22.04.21. +// +#include +#include +#include "CustomAllocator.h" + +namespace details { + template + struct KeySelect { + using key_type = Key; + const key_type &operator()(Key const &key) const noexcept { return key; } + key_type &operator()(Key &key) noexcept { return key; } + }; + + template + using sparse_set = tsl::detail_sparse_hash::sparse_hash< + T, KeySelect, void, std::hash, std::equal_to, Alloc, + tsl::sh::power_of_two_growth_policy<2>, + tsl::sh::exception_safety::basic, + tsl::sh::sparsity::medium, + tsl::sh::probing::quadratic>; + + template + auto default_construct_set() { + using Type = typename T::value_type; + return typename T::Set(T::Set::DEFAULT_INIT_BUCKET_COUNT, std::hash(), std::equal_to(), + typename T::Allocator(), T::Set::DEFAULT_MAX_LOAD_FACTOR); + } + + /** + * checks if all values of the set are in the initializer_list and than if the lengths are equal. + * So basically Set \subset l and |Set| == |l|. + * Needs 'set.contains(.)' to work correctly. + */ + template + bool is_equal(Set const& set, std::initializer_list l) { + return std::all_of(l.begin(), l.end(), [&set](auto i){return set.contains(i);}) + and set.size() == l.size(); + } +} + +template +void construction() { + auto set = details::default_construct_set(); +} + +template +void insert(std::initializer_list l) { + auto set = details::default_construct_set(); + for (auto const& i: l) set.insert(i); + BOOST_TEST_REQUIRE(details::is_equal(set, l), "'insert' did not create exactly the values needed"); +} + +template +void iterator_insert(std::initializer_list l) { + auto set = details::default_construct_set(); + set.insert(l.begin(), l.end()); + BOOST_TEST_REQUIRE(details::is_equal(set, l), "'insert' with iterators did not create exactly the values needed"); +} + +template +void iterator_access(typename T::value_type single_value) { + auto set = details::default_construct_set(); + set.insert(single_value); + set.begin(); + BOOST_TEST_REQUIRE(*(set.begin()) == single_value, "iterator cannot access single value"); +} + +template +struct STD { + using value_type = T; + using Allocator = std::allocator; + using Set = details::sparse_set; +}; + +template +struct CUSTOM { + using value_type = T; + using Allocator = OffsetAllocator; + using Set = details::sparse_set; +}; + + +BOOST_AUTO_TEST_SUITE(sparse_hash_tests) + +BOOST_AUTO_TEST_CASE(std_alloc_compiles) {construction>();} +BOOST_AUTO_TEST_CASE(std_alloc_insert) {insert>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(std_alloc_iterator_insert) {iterator_insert>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(std_alloc_iterator_access) {iterator_access>(42);} + +BOOST_AUTO_TEST_CASE(custom_alloc_compiles) {construction>();} +BOOST_AUTO_TEST_CASE(custom_alloc_insert) {insert>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {iterator_insert>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>(42);} + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From 0dcc86a23b8b53b2b7ac7903650703a6ddd5a46c Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 29 Apr 2021 14:34:12 +0200 Subject: [PATCH 04/14] Some more changes for the iterators. --- include/tsl/sparse_hash.h | 19 ++++++------ tests/fancy_pointer/sparse_hash_tests.cpp | 35 +++++++++++++++++++++-- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 69a9b02..65b8e1d 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -40,7 +40,7 @@ #include #include "sparse_growth_policy.h" -//quick and dirty +//TODO quick and dirty #include #ifdef __INTEL_COMPILER @@ -1027,8 +1027,8 @@ class sparse_hash : private Allocator, using hasher = Hash; using key_equal = KeyEqual; using allocator_type = Allocator; - using reference = value_type &; //does it need change? - using const_reference = const value_type &; //does it need change? + using reference = value_type &; //TODO does it need change? + using const_reference = const value_type &; //TODO does it need change? using size_type = typename std::allocator_traits::size_type; using pointer = typename std::allocator_traits::pointer; using const_pointer = typename std::allocator_traits::const_pointer; @@ -1118,25 +1118,26 @@ class sparse_hash : private Allocator, reference operator*() const { return *m_sparse_array_it; } - //with fancy pointers addressof might be problematic. - pointer operator->() const { return std::addressof(*m_sparse_array_it); } + //TODO with fancy pointers addressof might be problematic. + pointer operator->() const { return std::pointer_traits::pointer_to(*m_sparse_array_it); } sparse_iterator &operator++() { tsl_sh_assert(m_sparse_array_it != nullptr); ++m_sparse_array_it; - if (m_sparse_array_it == m_sparse_buckets_it->end()) { + //fancy pointers have a problem with -> + if (m_sparse_array_it == (*m_sparse_buckets_it).end()) { do { - if (m_sparse_buckets_it->last()) { + if ((*m_sparse_buckets_it).last()) { ++m_sparse_buckets_it; m_sparse_array_it = nullptr; return *this; } ++m_sparse_buckets_it; - } while (m_sparse_buckets_it->empty()); + } while ((*m_sparse_buckets_it).empty()); - m_sparse_array_it = m_sparse_buckets_it->begin(); + m_sparse_array_it = (*m_sparse_buckets_it).begin(); } return *this; diff --git a/tests/fancy_pointer/sparse_hash_tests.cpp b/tests/fancy_pointer/sparse_hash_tests.cpp index 5536afc..d335406 100644 --- a/tests/fancy_pointer/sparse_hash_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_tests.cpp @@ -63,10 +63,23 @@ template void iterator_access(typename T::value_type single_value) { auto set = details::default_construct_set(); set.insert(single_value); - set.begin(); BOOST_TEST_REQUIRE(*(set.begin()) == single_value, "iterator cannot access single value"); } +template +void iterator_access_multi(std::initializer_list l) { + auto set = details::default_construct_set(); + set.insert(l.begin(), l.end()); + std::vector l_sorted = l; + std::vector set_sorted(set.begin(), set.end()); + std::sort(l_sorted.begin(), l_sorted.end()); + std::sort(set_sorted.begin(), set_sorted.end()); + BOOST_TEST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), + set_sorted.begin(), set_sorted.end()), + "iterating over the set didn't work"); +} + + template struct STD { using value_type = T; @@ -88,10 +101,28 @@ BOOST_AUTO_TEST_CASE(std_alloc_compiles) {construction>();} BOOST_AUTO_TEST_CASE(std_alloc_insert) {insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(std_alloc_iterator_insert) {iterator_insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(std_alloc_iterator_access) {iterator_access>(42);} +BOOST_AUTO_TEST_CASE(std_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_compiles) {construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_insert) {insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {iterator_insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>(42);} +BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() + +#include + +BOOST_AUTO_TEST_SUITE(test_full_set) + +BOOST_AUTO_TEST_CASE(full_set) { + tsl::sparse_set, std::equal_to, OffsetAllocator> set; + std::vector data = {1,2,3,4,5,6,7,8,9}; + set.insert(data.begin(), data.end()); + std::cout << std::boolalpha; + for (auto d : data) { + std::cout << d << " in set: " << set.contains(d) << std::endl; + } + +} +BOOST_AUTO_TEST_SUITE_END() From 3e74155aaf546ef42ea921f78b62cfa658138428 Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 6 May 2021 10:29:09 +0200 Subject: [PATCH 05/14] Ported the set tests to work with maps. I could have generalized the tests even more to reduce code duplication, however the tests are already complicated enough. --- tests/CMakeLists.txt | 5 +- tests/fancy_pointer/sparse_hash_map_tests.cpp | 159 ++++++++++++++++++ ...sh_tests.cpp => sparse_hash_set_tests.cpp} | 2 +- 3 files changed, 163 insertions(+), 3 deletions(-) create mode 100644 tests/fancy_pointer/sparse_hash_map_tests.cpp rename tests/fancy_pointer/{sparse_hash_tests.cpp => sparse_hash_set_tests.cpp} (99%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index fa45ae9..92c14df 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,8 +11,9 @@ add_executable(tsl_sparse_map_tests "main.cpp" add_executable(tsl_fancy_pointer_tests "fancy_pointer/main.cpp" "fancy_pointer/sparse_array_tests.cpp" - "fancy_pointer/sparse_hash_tests.cpp" - "fancy_pointer/CustomAllocator.h") + "fancy_pointer/sparse_hash_set_tests.cpp" + "fancy_pointer/CustomAllocator.h" + "fancy_pointer/sparse_hash_map_tests.cpp") target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) diff --git a/tests/fancy_pointer/sparse_hash_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp new file mode 100644 index 0000000..eaed6c0 --- /dev/null +++ b/tests/fancy_pointer/sparse_hash_map_tests.cpp @@ -0,0 +1,159 @@ +// +// Created by lukas on 22.04.21. +// +#include +#include +#include "CustomAllocator.h" + +namespace details { + + template + struct KeySelect { + using key_type = Key; + const key_type &operator()(std::pair const &key_value) const noexcept { + return key_value.first; + } + const key_type &operator()(std::pair &key_value) noexcept { + return key_value.first; + } + }; + + template + struct ValueSelect { + using value_type = T; + const value_type &operator()(std::pair const &key_value) const noexcept { + return key_value.second; + } + const value_type &operator()(std::pair &key_value) noexcept { + return key_value.second; + } + }; + + template + using sparse_map= tsl::detail_sparse_hash::sparse_hash< + std::pair, KeySelect, ValueSelect, std::hash, std::equal_to, Alloc, + tsl::sh::power_of_two_growth_policy<2>, + tsl::sh::exception_safety::basic, + tsl::sh::sparsity::medium, + tsl::sh::probing::quadratic>; + + template + auto default_construct_map() { + using Key = typename T::key_type; + return typename T::Map(T::Map::DEFAULT_INIT_BUCKET_COUNT, + std::hash(), + std::equal_to(), + typename T::Allocator(), + T::Map::DEFAULT_MAX_LOAD_FACTOR); + } + + /** + * checks if all values of the set are in the initializer_list and than if the lengths are equal. + * So basically Set \subset l and |Met| == |l|. + * Needs 'map.contains(.)' and 'map.at(.)' to work correctly. + */ + template + bool is_equal(Map const& map, std::initializer_list l) { + auto check_in_map = [&map](typename Map::value_type p) { + return map.contains(p.first) && map.at(p.first) == p.second; + }; + return std::all_of(l.begin(), l.end(), check_in_map) && map.size() == l.size(); + } +} + +template +void construction() { + auto map = details::default_construct_map(); +} + +template +void insert(std::initializer_list l) { + auto map = details::default_construct_map(); + for (auto dataPair : l) map.insert(dataPair); + BOOST_TEST_REQUIRE(details::is_equal(map, l), "'insert' did not create exactly the values needed"); +} + +template +void iterator_insert(std::initializer_list l) { + auto map = details::default_construct_map(); + map.insert(l.begin(), l.end()); + BOOST_TEST_REQUIRE(details::is_equal(map, l), "'insert' with iterators did not create exactly the values needed"); +} + +template +void iterator_access(typename T::value_type single_value) { + auto map = details::default_construct_map(); + map.insert(single_value); + BOOST_TEST_REQUIRE( (*(map.begin()) == single_value), "iterator cannot access single value"); +} + +template +void iterator_access_multi(std::initializer_list l) { + auto map = details::default_construct_map(); + map.insert(l.begin(), l.end()); + std::vector l_sorted = l; + std::vector map_sorted(map.begin(), map.end()); + std::sort(l_sorted.begin(), l_sorted.end()); + std::sort(map_sorted.begin(), map_sorted.end()); + BOOST_TEST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), + map_sorted.begin(), map_sorted.end()), + "iterating over the set didn't work"); +} + + +template +struct STD { + using key_type = Key; + using value_type = std::pair; + using Allocator = std::allocator; + using Map = details::sparse_map; +}; + +template +struct CUSTOM { + using key_type = Key; + using value_type = std::pair; + using Allocator = OffsetAllocator; + using Map = details::sparse_map; +}; + + +BOOST_AUTO_TEST_SUITE(sparse_hash_map_tests) + +BOOST_AUTO_TEST_CASE(std_alloc_compiles) {construction>();} +BOOST_AUTO_TEST_CASE(std_alloc_insert) {insert>({{1,2},{3,4},{5,6}});} +BOOST_AUTO_TEST_CASE(std_alloc_iterator_insert) {insert>({{1,2},{3,4},{5,6}});} +BOOST_AUTO_TEST_CASE(std_alloc_iterator_access) {iterator_access>({1,42});} +BOOST_AUTO_TEST_CASE(std_alloc_iterator_access_multi) {iterator_access_multi>({{1,2},{3,4},{5,6}});} + +BOOST_AUTO_TEST_CASE(custom_alloc_compiles) {construction>();} +BOOST_AUTO_TEST_CASE(custom_alloc_insert) {insert>({{1,2},{3,4},{5,6}});} +BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {insert>({{1,2},{3,4},{5,6}});} +BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>({1,42});} +BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({{1,2},{3,4},{5,6}});} + +BOOST_AUTO_TEST_SUITE_END() + +#include + +BOOST_AUTO_TEST_SUITE(test_full_map) + + BOOST_AUTO_TEST_CASE(full_map) { + tsl::sparse_map, std::equal_to, + OffsetAllocator>> map; + std::vector> data = { + {0,1},{2,3},{4,5},{6,7},{8,9} + }; + map.insert(data.begin(), data.end()); + std::cout << std::boolalpha; + for (auto d : data) { + std::pair res (map.contains(d.first), false); + if (res.first) res.second = map[d.first] == d.second; + std::cout << "(" << d.first << ", " << d.second << "): " + << "(" << res.first << ", " << res.second << ")\n"; + + + } + + } +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/fancy_pointer/sparse_hash_tests.cpp b/tests/fancy_pointer/sparse_hash_set_tests.cpp similarity index 99% rename from tests/fancy_pointer/sparse_hash_tests.cpp rename to tests/fancy_pointer/sparse_hash_set_tests.cpp index d335406..90bf29e 100644 --- a/tests/fancy_pointer/sparse_hash_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -95,7 +95,7 @@ struct CUSTOM { }; -BOOST_AUTO_TEST_SUITE(sparse_hash_tests) +BOOST_AUTO_TEST_SUITE(sparse_hash_set_tests) BOOST_AUTO_TEST_CASE(std_alloc_compiles) {construction>();} BOOST_AUTO_TEST_CASE(std_alloc_insert) {insert>({1,2,3,4});} From a6c225d2961dbb2770bb9797e00b18e18371d3a8 Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 6 May 2021 11:58:22 +0200 Subject: [PATCH 06/14] Integrated boost::to_address directly into the code. --- include/tsl/sparse_hash.h | 49 ++++++++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 65b8e1d..4748e69 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -40,8 +40,6 @@ #include #include "sparse_growth_policy.h" -//TODO quick and dirty -#include #ifdef __INTEL_COMPILER #include // For _popcnt32 and _popcnt64 @@ -179,16 +177,39 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } } // namespace detail_popcount namespace detail_sparse_hash { -/** This is a quick and dirty way of adding to_address to the code. - * In the long run the "real" function must be inserted without boost dependability. - * @tparam T - * @param v - * @return - */ -template -auto to_address(T v) { - return boost::to_address(v); +/* with 14-features +template +T *to_address(T *v) noexcept { return v; } + +namespace fancy_ptr_detail { + template + inline T *ptr_address(T *v, int) noexcept { return v; } + + template + inline auto ptr_address(const T &v, int) noexcept + -> decltype(std::pointer_traits::to_address(v)) { + return std::pointer_traits::to_address(v); + } + template + inline auto ptr_address(const T &v, long) noexcept { + return fancy_ptr_detail::ptr_address(v.operator->(), 0); + } +} // namespace detail + +template inline auto to_address(const T &v) noexcept { + return fancy_ptr_detail::ptr_address(v, 0); +} +// with 14-features */ + +//* without 14-features +template +inline T *to_address(T *v) noexcept { return v; } + +template +inline typename std::pointer_traits::element_type * to_address(const T &v) noexcept { + return detail_sparse_hash::to_address(v.operator->()); } +// without 14-features */ template struct make_void { @@ -691,11 +712,11 @@ class sparse_array { static void construct_value(allocator_type &alloc, pointer value, Args &&... value_args) { std::allocator_traits::construct( - alloc, to_address(value), std::forward(value_args)...); + alloc, detail_sparse_hash::to_address(value), std::forward(value_args)...); } static void destroy_value(allocator_type &alloc, pointer value) noexcept { - std::allocator_traits::destroy(alloc, to_address(value)); + std::allocator_traits::destroy(alloc, detail_sparse_hash::to_address(value)); } static void destroy_and_deallocate_values( @@ -1118,7 +1139,7 @@ class sparse_hash : private Allocator, reference operator*() const { return *m_sparse_array_it; } - //TODO with fancy pointers addressof might be problematic. + //with fancy pointers addressof might be problematic. pointer operator->() const { return std::pointer_traits::pointer_to(*m_sparse_array_it); } sparse_iterator &operator++() { From 4298884aa05d89ccd9d4c36752e56b6acb6ce80c Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 6 May 2021 14:48:58 +0200 Subject: [PATCH 07/14] A bit of tidy up. Now the fancy pointer tests are integrated into the "normal" test suit and can be compiled with C++-11. --- include/tsl/sparse_hash.h | 62 ++++++++++--------- tests/CMakeLists.txt | 12 +--- tests/fancy_pointer/CustomAllocator.h | 22 +++---- tests/fancy_pointer/main.cpp | 7 --- tests/fancy_pointer/sparse_array_tests.cpp | 31 ++++++---- tests/fancy_pointer/sparse_hash_map_tests.cpp | 61 +++++++++--------- tests/fancy_pointer/sparse_hash_set_tests.cpp | 43 +++++++------ 7 files changed, 109 insertions(+), 129 deletions(-) delete mode 100644 tests/fancy_pointer/main.cpp diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 4748e69..a0e5b2b 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -177,39 +177,41 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } } // namespace detail_popcount namespace detail_sparse_hash { -/* with 14-features -template -T *to_address(T *v) noexcept { return v; } - -namespace fancy_ptr_detail { + /* to_address can convert any raw or fancy pointer into a raw pointer. + * It is needed for the allocator construct and destroy calls. + * This specific implementation is based on boost 1.71.0. + */ +#if __cplusplus >= 201400L // with 14-features template - inline T *ptr_address(T *v, int) noexcept { return v; } + T *to_address(T *v) noexcept { return v; } - template - inline auto ptr_address(const T &v, int) noexcept - -> decltype(std::pointer_traits::to_address(v)) { - return std::pointer_traits::to_address(v); - } - template - inline auto ptr_address(const T &v, long) noexcept { - return fancy_ptr_detail::ptr_address(v.operator->(), 0); - } -} // namespace detail + namespace fancy_ptr_detail { + template + inline T *ptr_address(T *v, int) noexcept { return v; } -template inline auto to_address(const T &v) noexcept { - return fancy_ptr_detail::ptr_address(v, 0); -} -// with 14-features */ + template + inline auto ptr_address(const T &v, int) noexcept + -> decltype(std::pointer_traits::to_address(v)) { + return std::pointer_traits::to_address(v); + } + template + inline auto ptr_address(const T &v, long) noexcept { + return fancy_ptr_detail::ptr_address(v.operator->(), 0); + } + } // namespace detail -//* without 14-features -template -inline T *to_address(T *v) noexcept { return v; } + template inline auto to_address(const T &v) noexcept { + return fancy_ptr_detail::ptr_address(v, 0); + } +#else // without 14-features + template + inline T *to_address(T *v) noexcept { return v; } -template -inline typename std::pointer_traits::element_type * to_address(const T &v) noexcept { - return detail_sparse_hash::to_address(v.operator->()); -} -// without 14-features */ + template + inline typename std::pointer_traits::element_type * to_address(const T &v) noexcept { + return detail_sparse_hash::to_address(v.operator->()); + } +#endif template struct make_void { @@ -1048,8 +1050,8 @@ class sparse_hash : private Allocator, using hasher = Hash; using key_equal = KeyEqual; using allocator_type = Allocator; - using reference = value_type &; //TODO does it need change? - using const_reference = const value_type &; //TODO does it need change? + using reference = value_type &; //does it need to be changed for fancy pointers? + using const_reference = const value_type &; using size_type = typename std::allocator_traits::size_type; using pointer = typename std::allocator_traits::pointer; using const_pointer = typename std::allocator_traits::const_pointer; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 92c14df..d452338 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,32 +7,24 @@ add_executable(tsl_sparse_map_tests "main.cpp" "policy_tests.cpp" "popcount_tests.cpp" "sparse_map_tests.cpp" - "sparse_set_tests.cpp") - -add_executable(tsl_fancy_pointer_tests "fancy_pointer/main.cpp" + "sparse_set_tests.cpp" "fancy_pointer/sparse_array_tests.cpp" "fancy_pointer/sparse_hash_set_tests.cpp" "fancy_pointer/CustomAllocator.h" "fancy_pointer/sparse_hash_map_tests.cpp") - target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) -target_compile_features(tsl_fancy_pointer_tests PRIVATE cxx_std_11) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") target_compile_options(tsl_sparse_map_tests PRIVATE -Werror -Wall -Wextra -Wold-style-cast -DTSL_DEBUG -UNDEBUG) - target_compile_options(tsl_fancy_pointer_tests PRIVATE -Werror -Wall -Wextra -Wold-style-cast -DTSL_DEBUG -UNDEBUG) elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") target_compile_options(tsl_sparse_map_tests PRIVATE /bigobj /WX /W3 /DTSL_DEBUG /UNDEBUG) - target_compile_options(tsl_fancy_pointer_tests PRIVATE /bigobj /WX /W3 /DTSL_DEBUG /UNDEBUG) endif() # Boost::unit_test_framework find_package(Boost 1.54.0 REQUIRED COMPONENTS unit_test_framework) target_link_libraries(tsl_sparse_map_tests PRIVATE Boost::unit_test_framework) -target_link_libraries(tsl_fancy_pointer_tests PRIVATE Boost::unit_test_framework) # tsl::sparse_map add_subdirectory(../ ${CMAKE_CURRENT_BINARY_DIR}/tsl) -target_link_libraries(tsl_sparse_map_tests PRIVATE tsl::sparse_map) -target_link_libraries(tsl_fancy_pointer_tests PRIVATE tsl::sparse_map) +target_link_libraries(tsl_sparse_map_tests PRIVATE tsl::sparse_map) \ No newline at end of file diff --git a/tests/fancy_pointer/CustomAllocator.h b/tests/fancy_pointer/CustomAllocator.h index d6a9655..b72d014 100644 --- a/tests/fancy_pointer/CustomAllocator.h +++ b/tests/fancy_pointer/CustomAllocator.h @@ -1,15 +1,15 @@ -// -// Created by lukas on 22.04.21. -// +/** @file + * @bief Home of a custom allocator for testing with fancy pointers. + */ #ifndef TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H #define TSL_SPARSE_MAP_TESTS_CUSTOMALLOCATOR_H #include -/** A custom allocator that simply wrapes all pointers into boost offset ptr. -* -* @tparam T +/** A custom allocator that simply wraps all pointers into boost offset ptr. +* It is used to check whether the implementation can handle Allocators using fancy pointers. +* @tparam T Typical Allocator parameter. */ template struct OffsetAllocator { @@ -22,30 +22,22 @@ struct OffsetAllocator { using difference_type = typename offset_ptr::difference_type; OffsetAllocator() noexcept = default; - OffsetAllocator(OffsetAllocator const &) noexcept = default; - - OffsetAllocator &operator=(OffsetAllocator const &) noexcept = default; - OffsetAllocator(OffsetAllocator &&) noexcept = default; - + OffsetAllocator &operator=(OffsetAllocator const &) noexcept = default; OffsetAllocator &operator=(OffsetAllocator &&) noexcept = default; - template OffsetAllocator(OffsetAllocator) noexcept {} pointer allocate(std::size_t n) { return pointer(static_cast(::operator new(n*sizeof(T)))); } - void deallocate(pointer p, std::size_t) noexcept { ::operator delete(p.get()); } - friend bool operator==(OffsetAllocator const &, OffsetAllocator const &) noexcept { return true; } - friend bool operator!=(OffsetAllocator const &l, OffsetAllocator const &r) noexcept { return !(l == r); } diff --git a/tests/fancy_pointer/main.cpp b/tests/fancy_pointer/main.cpp deleted file mode 100644 index 4ac4edb..0000000 --- a/tests/fancy_pointer/main.cpp +++ /dev/null @@ -1,7 +0,0 @@ -// -// Created by lukas on 22.04.21. -// -#define BOOST_TEST_MODULE sparse_fancy_pointer_tests -#define BOOST_TEST_DYN_LINK - -#include \ No newline at end of file diff --git a/tests/fancy_pointer/sparse_array_tests.cpp b/tests/fancy_pointer/sparse_array_tests.cpp index 687e807..6c5ed71 100644 --- a/tests/fancy_pointer/sparse_array_tests.cpp +++ b/tests/fancy_pointer/sparse_array_tests.cpp @@ -1,6 +1,6 @@ -// -// Created by lukas on 22.04.21. -// +/** @file + * @brief Checks for fancy pointer support in the sparse_array implementation. + */ #include #include @@ -9,9 +9,11 @@ // Globals constexpr auto MAX_INDEX = 32; //BITMAP_NB_BITS -/* templated tests - * T is a struct with T::Array and T::Allocator. -*/ +/* Tests are formulated via templates to reduce code duplication. + * The template parameter contains the Allocator type and the shorthand "Array" for the sparse_array + * (with all template parameter already inserted). + */ + template void compilation() { typename T::Array test; @@ -26,7 +28,7 @@ void construction() { namespace details { template - auto generate_test_array(typename T::Allocator &a) { + typename T::Array generate_test_array(typename T::Allocator &a) { typename T::Array arr(MAX_INDEX, a); for (std::size_t i = 0; i < MAX_INDEX; ++i) { arr.set(a, i, i); @@ -35,7 +37,7 @@ namespace details { } template - auto generate_check_for_test_array() { + std::vector generate_check_for_test_array() { std::vector check(MAX_INDEX); for (std::size_t i = 0; i < MAX_INDEX; ++i) { check[i] = i; @@ -81,7 +83,9 @@ void move_construction() { -// Template test structs +/* + * This are the types you can give the tests as template parameters. + */ template struct STD { using Allocator = std::allocator; @@ -95,9 +99,11 @@ struct CUSTOM { }; -/* Tests. - * I don't use the boost template test cases because with this I can set the title of every test case. + +/* The instantiation of the tests. + * I don't use the boost template test cases because with this I can set the title of every test case myself. */ +BOOST_AUTO_TEST_SUITE(fancy_pointers) BOOST_AUTO_TEST_SUITE(sparse_array_tests) BOOST_AUTO_TEST_CASE(std_alloc_compile) {compilation>();} @@ -112,4 +118,5 @@ BOOST_AUTO_TEST_CASE(custom_alloc_set) {set>();} BOOST_AUTO_TEST_CASE(custom_alloc_copy_construction) {copy_construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_move_construction) {move_construction>();} -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() +BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/fancy_pointer/sparse_hash_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp index eaed6c0..c5d3c80 100644 --- a/tests/fancy_pointer/sparse_hash_map_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_map_tests.cpp @@ -1,12 +1,16 @@ -// -// Created by lukas on 22.04.21. -// +/** @file + * @brief Checks for fancy pointer support in the sparse_hash implementation for pair values (maps). + */ + #include +#include #include #include "CustomAllocator.h" +/* Tests are analogous to the tests in sparse_array_tests.cpp. + * The template parameter now also holds the value_type. + */ namespace details { - template struct KeySelect { using key_type = Key; @@ -38,7 +42,7 @@ namespace details { tsl::sh::probing::quadratic>; template - auto default_construct_map() { + typename T::Map default_construct_map() { using Key = typename T::key_type; return typename T::Map(T::Map::DEFAULT_INIT_BUCKET_COUNT, std::hash(), @@ -47,9 +51,8 @@ namespace details { T::Map::DEFAULT_MAX_LOAD_FACTOR); } - /** - * checks if all values of the set are in the initializer_list and than if the lengths are equal. - * So basically Set \subset l and |Met| == |l|. + /** Checks if all values of the map are in the initializer_list and than if the lengths are equal. + * So basically Map \subset l and |Map| == |l|. * Needs 'map.contains(.)' and 'map.at(.)' to work correctly. */ template @@ -96,8 +99,8 @@ void iterator_access_multi(std::initializer_list l) { std::sort(l_sorted.begin(), l_sorted.end()); std::sort(map_sorted.begin(), map_sorted.end()); BOOST_TEST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), - map_sorted.begin(), map_sorted.end()), - "iterating over the set didn't work"); + map_sorted.begin()), + "iterating over the map didn't work"); } @@ -118,6 +121,7 @@ struct CUSTOM { }; +BOOST_AUTO_TEST_SUITE(fancy_pointers) BOOST_AUTO_TEST_SUITE(sparse_hash_map_tests) BOOST_AUTO_TEST_CASE(std_alloc_compiles) {construction>();} @@ -132,28 +136,19 @@ BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {insert>({{1 BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>({1,42});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({{1,2},{3,4},{5,6}});} -BOOST_AUTO_TEST_SUITE_END() - -#include - -BOOST_AUTO_TEST_SUITE(test_full_map) - - BOOST_AUTO_TEST_CASE(full_map) { - tsl::sparse_map, std::equal_to, - OffsetAllocator>> map; - std::vector> data = { - {0,1},{2,3},{4,5},{6,7},{8,9} - }; - map.insert(data.begin(), data.end()); - std::cout << std::boolalpha; - for (auto d : data) { - std::pair res (map.contains(d.first), false); - if (res.first) res.second = map[d.first] == d.second; - std::cout << "(" << d.first << ", " << d.second << "): " - << "(" << res.first << ", " << res.second << ")\n"; - - - } +BOOST_AUTO_TEST_CASE(full_map) { + tsl::sparse_map, std::equal_to, OffsetAllocator>> map; + std::vector> data = { + {0,1},{2,3},{4,5},{6,7},{8,9} + }; + map.insert(data.begin(), data.end()); + auto check = [&map](std::pair p) { + if (!map.contains(p.first)) return false; + return map.at(p.first) == p.second; + }; + BOOST_TEST_REQUIRE(data.size() == map.size(), "size did not match"); + BOOST_TEST_REQUIRE(std::all_of(data.begin(), data.end(), check), "map did not contain all values"); +} - } +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/fancy_pointer/sparse_hash_set_tests.cpp b/tests/fancy_pointer/sparse_hash_set_tests.cpp index 90bf29e..d63e534 100644 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -1,10 +1,15 @@ -// -// Created by lukas on 22.04.21. -// +/** @file + * @brief Checks for fancy pointer support in the sparse_hash implementation for single values (sets). + */ + #include +#include #include #include "CustomAllocator.h" +/* Tests are analogous to the tests in sparse_array_tests.cpp. + * The template parameter now also holds the value_type. + */ namespace details { template struct KeySelect { @@ -22,20 +27,19 @@ namespace details { tsl::sh::probing::quadratic>; template - auto default_construct_set() { + typename T::Set default_construct_set() { using Type = typename T::value_type; return typename T::Set(T::Set::DEFAULT_INIT_BUCKET_COUNT, std::hash(), std::equal_to(), typename T::Allocator(), T::Set::DEFAULT_MAX_LOAD_FACTOR); } - /** - * checks if all values of the set are in the initializer_list and than if the lengths are equal. + /** checks if all values of the set are in the initializer_list and than if the lengths are equal. * So basically Set \subset l and |Set| == |l|. * Needs 'set.contains(.)' to work correctly. */ template bool is_equal(Set const& set, std::initializer_list l) { - return std::all_of(l.begin(), l.end(), [&set](auto i){return set.contains(i);}) + return std::all_of(l.begin(), l.end(), [&set](typename Set::value_type i){return set.contains(i);}) and set.size() == l.size(); } } @@ -75,7 +79,7 @@ void iterator_access_multi(std::initializer_list l) { std::sort(l_sorted.begin(), l_sorted.end()); std::sort(set_sorted.begin(), set_sorted.end()); BOOST_TEST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), - set_sorted.begin(), set_sorted.end()), + set_sorted.begin()), "iterating over the set didn't work"); } @@ -95,6 +99,7 @@ struct CUSTOM { }; +BOOST_AUTO_TEST_SUITE(fancy_pointers) BOOST_AUTO_TEST_SUITE(sparse_hash_set_tests) BOOST_AUTO_TEST_CASE(std_alloc_compiles) {construction>();} @@ -109,20 +114,14 @@ BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {iterator_insert> BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>(42);} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} -BOOST_AUTO_TEST_SUITE_END() - -#include - -BOOST_AUTO_TEST_SUITE(test_full_set) - BOOST_AUTO_TEST_CASE(full_set) { - tsl::sparse_set, std::equal_to, OffsetAllocator> set; - std::vector data = {1,2,3,4,5,6,7,8,9}; - set.insert(data.begin(), data.end()); - std::cout << std::boolalpha; - for (auto d : data) { - std::cout << d << " in set: " << set.contains(d) << std::endl; - } - + tsl::sparse_set, std::equal_to, OffsetAllocator> set; + std::vector data = {1,2,3,4,5,6,7,8,9}; + set.insert(data.begin(), data.end()); + auto check = [&set](int d) {return set.contains(d);}; + BOOST_TEST_REQUIRE(data.size() == set.size(), "size did not match"); + BOOST_TEST_REQUIRE(std::all_of(data.begin(), data.end(), check), "Set did not contain all values"); } + +BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() From 6435d76c0d3d536bc200be3afbf3f6f064a3cf6f Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Fri, 7 May 2021 16:24:40 +0200 Subject: [PATCH 08/14] added test for value() with fancy pointer --- tests/fancy_pointer/sparse_hash_map_tests.cpp | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/tests/fancy_pointer/sparse_hash_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp index c5d3c80..64cf163 100644 --- a/tests/fancy_pointer/sparse_hash_map_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_map_tests.cpp @@ -2,6 +2,7 @@ * @brief Checks for fancy pointer support in the sparse_hash implementation for pair values (maps). */ +#include #include #include #include @@ -28,7 +29,7 @@ namespace details { const value_type &operator()(std::pair const &key_value) const noexcept { return key_value.second; } - const value_type &operator()(std::pair &key_value) noexcept { + value_type &operator()(std::pair &key_value) noexcept { return key_value.second; } }; @@ -62,6 +63,13 @@ namespace details { }; return std::all_of(l.begin(), l.end(), check_in_map) && map.size() == l.size(); } + template + bool is_equal(Map1 const& custom_map, Map2 const &normal_map) { + auto check_in_map = [&custom_map](typename Map2::value_type const& p) { + return custom_map.count(p.first) == 1 && custom_map.at(p.first) == p.second; + }; + return std::all_of(normal_map.begin(), normal_map.end(), check_in_map) && custom_map.size() == normal_map.size(); + } } template @@ -103,6 +111,18 @@ void iterator_access_multi(std::initializer_list l) { "iterating over the map didn't work"); } +template +void value(std::initializer_list l, typename T::value_type to_change) { + auto map = details::default_construct_map(); + map.insert(l.begin(), l.end()); + map[to_change.first] = to_change.second; + + std::unordered_map check(l.begin(), l.end()); + check[to_change.first] = to_change.second; + + BOOST_TEST_REQUIRE(details::is_equal(map, check), "changing a single value didn't work"); +} + template struct STD { @@ -129,12 +149,14 @@ BOOST_AUTO_TEST_CASE(std_alloc_insert) {insert>({{1,2},{3,4},{5,6} BOOST_AUTO_TEST_CASE(std_alloc_iterator_insert) {insert>({{1,2},{3,4},{5,6}});} BOOST_AUTO_TEST_CASE(std_alloc_iterator_access) {iterator_access>({1,42});} BOOST_AUTO_TEST_CASE(std_alloc_iterator_access_multi) {iterator_access_multi>({{1,2},{3,4},{5,6}});} +BOOST_AUTO_TEST_CASE(std_alloc_value) {value>({{1,2},{3,4},{5,6}}, {1, 42});} BOOST_AUTO_TEST_CASE(custom_alloc_compiles) {construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_insert) {insert>({{1,2},{3,4},{5,6}});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {insert>({{1,2},{3,4},{5,6}});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>({1,42});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({{1,2},{3,4},{5,6}});} +BOOST_AUTO_TEST_CASE(custom_alloc_value) {value>({{1,2},{3,4},{5,6}}, {1, 42});} BOOST_AUTO_TEST_CASE(full_map) { tsl::sparse_map, std::equal_to, OffsetAllocator>> map; From fb6c7e70234da1b963bbf79cb4d21f713b398402 Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Fri, 7 May 2021 16:47:39 +0200 Subject: [PATCH 09/14] added missing const --- tests/fancy_pointer/sparse_hash_map_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fancy_pointer/sparse_hash_map_tests.cpp b/tests/fancy_pointer/sparse_hash_map_tests.cpp index 64cf163..f84de72 100644 --- a/tests/fancy_pointer/sparse_hash_map_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_map_tests.cpp @@ -18,7 +18,7 @@ namespace details { const key_type &operator()(std::pair const &key_value) const noexcept { return key_value.first; } - const key_type &operator()(std::pair &key_value) noexcept { + key_type &operator()(std::pair &key_value) noexcept { return key_value.first; } }; From fbd3603c87809bc17b370778513564da8e175494 Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 13 May 2021 16:18:41 +0200 Subject: [PATCH 10/14] Fixxed a bug in the OffsetAllocator, also did some work on erase. --- include/tsl/sparse_hash.h | 20 ++++--- tests/fancy_pointer/CustomAllocator.h | 4 +- tests/fancy_pointer/sparse_array_tests.cpp | 13 ++++ tests/fancy_pointer/sparse_hash_set_tests.cpp | 59 +++++++++++++++++++ 4 files changed, 85 insertions(+), 11 deletions(-) diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index a0e5b2b..e2ae2a0 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -1102,10 +1102,10 @@ class sparse_hash : private Allocator, public: using iterator_category = std::forward_iterator_tag; - using value_type = const typename sparse_hash::value_type; + using value_type = const sparse_hash::value_type; using difference_type = std::ptrdiff_t; using reference = value_type &; - using pointer = value_type *; + using pointer = sparse_hash::const_pointer; sparse_iterator() noexcept {} @@ -1142,7 +1142,7 @@ class sparse_hash : private Allocator, reference operator*() const { return *m_sparse_array_it; } //with fancy pointers addressof might be problematic. - pointer operator->() const { return std::pointer_traits::pointer_to(*m_sparse_array_it); } + pointer operator->() const { return std::addressof(*m_sparse_array_it); } sparse_iterator &operator++() { tsl_sh_assert(m_sparse_array_it != nullptr); @@ -1398,12 +1398,13 @@ class sparse_hash : private Allocator, const_iterator cbegin() const noexcept { auto begin = m_sparse_buckets_data.cbegin(); - while (begin != m_sparse_buckets_data.cend() && begin->empty()) { + // '->' is problematic with fancy pointers + while (begin != m_sparse_buckets_data.cend() && (*begin).empty()) { ++begin; } return const_iterator(begin, (begin != m_sparse_buckets_data.cend()) - ? begin->cbegin() + ? (*begin).cbegin() : nullptr); } @@ -1530,23 +1531,24 @@ class sparse_hash : private Allocator, */ iterator erase(iterator pos) { tsl_sh_assert(pos != end() && m_nb_elements > 0); + // '->' is problematic with fancy_pointers auto it_sparse_array_next = - pos.m_sparse_buckets_it->erase(*this, pos.m_sparse_array_it); + (*pos.m_sparse_buckets_it).erase(*this, pos.m_sparse_array_it); m_nb_elements--; m_nb_deleted_buckets++; - if (it_sparse_array_next == pos.m_sparse_buckets_it->end()) { + if (it_sparse_array_next == (*pos.m_sparse_buckets_it).end()) { auto it_sparse_buckets_next = pos.m_sparse_buckets_it; do { ++it_sparse_buckets_next; } while (it_sparse_buckets_next != m_sparse_buckets_data.end() && - it_sparse_buckets_next->empty()); + (*it_sparse_buckets_next).empty()); if (it_sparse_buckets_next == m_sparse_buckets_data.end()) { return end(); } else { return iterator(it_sparse_buckets_next, - it_sparse_buckets_next->begin()); + (*it_sparse_buckets_next).begin()); } } else { return iterator(pos.m_sparse_buckets_it, it_sparse_array_next); diff --git a/tests/fancy_pointer/CustomAllocator.h b/tests/fancy_pointer/CustomAllocator.h index b72d014..7340fef 100644 --- a/tests/fancy_pointer/CustomAllocator.h +++ b/tests/fancy_pointer/CustomAllocator.h @@ -16,9 +16,9 @@ struct OffsetAllocator { using value_type = T; template using offset_ptr = boost::interprocess::offset_ptr

; using pointer = offset_ptr; - using const_pointer = const offset_ptr; + using const_pointer = offset_ptr; using void_pointer = offset_ptr; - using const_void_pointer = const offset_ptr; + using const_void_pointer = offset_ptr; using difference_type = typename offset_ptr::difference_type; OffsetAllocator() noexcept = default; diff --git a/tests/fancy_pointer/sparse_array_tests.cpp b/tests/fancy_pointer/sparse_array_tests.cpp index 6c5ed71..8f992b1 100644 --- a/tests/fancy_pointer/sparse_array_tests.cpp +++ b/tests/fancy_pointer/sparse_array_tests.cpp @@ -81,6 +81,15 @@ void move_construction() { moved_to.clear(a); } +template +void const_iterator() { + typename T::Allocator a; + auto test = details::generate_test_array(a); + auto const_iter = test.cbegin(); + BOOST_TEST_REQUIRE((std::is_same::value), + "const iterator has the wrong type"); + test.clear(a); +} /* @@ -90,12 +99,14 @@ template struct STD { using Allocator = std::allocator; using Array = tsl::detail_sparse_hash::sparse_array, Sparsity>; + using Const_Iterator = T const*; }; template struct CUSTOM { using Allocator = OffsetAllocator; using Array = tsl::detail_sparse_hash::sparse_array, Sparsity>; + using Const_Iterator = boost::interprocess::offset_ptr; }; @@ -111,12 +122,14 @@ BOOST_AUTO_TEST_CASE(std_alloc_construction) {construction>();} BOOST_AUTO_TEST_CASE(std_alloc_set) {set>();} BOOST_AUTO_TEST_CASE(std_alloc_copy_construction) {copy_construction>();} BOOST_AUTO_TEST_CASE(std_alloc_move_construction) {move_construction>();} +BOOST_AUTO_TEST_CASE(std_const_iterator) {const_iterator>();} BOOST_AUTO_TEST_CASE(custom_alloc_compile) {compilation>();} BOOST_AUTO_TEST_CASE(custom_alloc_construction) {construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_set) {set>();} BOOST_AUTO_TEST_CASE(custom_alloc_copy_construction) {copy_construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_move_construction) {move_construction>();} +BOOST_AUTO_TEST_CASE(custom_const_iterator) {const_iterator>();} BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/fancy_pointer/sparse_hash_set_tests.cpp b/tests/fancy_pointer/sparse_hash_set_tests.cpp index d63e534..e0db145 100644 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -84,6 +84,52 @@ void iterator_access_multi(std::initializer_list l) { } +template +void const_iterator_access_multi(std::initializer_list l) { + auto set = details::default_construct_set(); + set.insert(l.begin(), l.end()); + std::vector l_sorted = l; + std::vector set_sorted(set.cbegin(), set.cend()); + std::sort(l_sorted.begin(), l_sorted.end()); + std::sort(set_sorted.begin(), set_sorted.end()); + BOOST_TEST_REQUIRE(std::equal(l_sorted.begin(), l_sorted.end(), + set_sorted.begin()), + "const iterating over the set didn't work"); +} + +template +void erase(std::initializer_list l, typename T::value_type extra_value) { + auto set = details::default_construct_set(); + set.insert(extra_value); + set.insert(l.begin(), l.end()); + //find is not tested yet + auto iter = set.begin(); + for(; *iter != extra_value; ++iter); + set.erase(iter); + BOOST_TEST_REQUIRE(details::is_equal(set, l), "erase did not work as expected"); +} + +template +void erase_with_const_iter(std::initializer_list l, typename T::value_type extra_value) { + auto set = details::default_construct_set(); + set.insert(extra_value); + set.insert(l.begin(), l.end()); + //find is not tested yet + auto iter = set.cbegin(); + for(; *iter != extra_value; ++iter); + set.erase(iter); + BOOST_TEST_REQUIRE(details::is_equal(set, l), "erase did not work as expected"); +} + +template +void find(std::initializer_list l, typename T::value_type search_value, bool is_in_list) { + auto set = details::default_construct_set(); + set.insert(l.begin(), l.end()); + auto iter = set.find(search_value); + bool found = iter != set.end(); + BOOST_TEST_REQUIRE((found == is_in_list), "erase did not work as expected"); +} + template struct STD { using value_type = T; @@ -107,12 +153,25 @@ BOOST_AUTO_TEST_CASE(std_alloc_insert) {insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(std_alloc_iterator_insert) {iterator_insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(std_alloc_iterator_access) {iterator_access>(42);} BOOST_AUTO_TEST_CASE(std_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(std_alloc_const_iterator_access_multi) {const_iterator_access_multi>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(std_erase) {erase>({1,2,3,4}, 5);} +BOOST_AUTO_TEST_CASE(std_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} +BOOST_AUTO_TEST_CASE(std_find_true) {find>({1,2,3,4}, 4, true);} +BOOST_AUTO_TEST_CASE(std_find_false) {find>({1,2,3,4}, 5, false);} BOOST_AUTO_TEST_CASE(custom_alloc_compiles) {construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_insert) {insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {iterator_insert>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>(42);} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(custom_alloc_const_iterator_access_multi) {const_iterator_access_multi>({1,2,3,4});} +BOOST_AUTO_TEST_CASE(custom_erase) {erase>({1,2,3,4}, 5);} +//TODO +/* +BOOST_AUTO_TEST_CASE(custom_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} +BOOST_AUTO_TEST_CASE(custom_find_true) {find>({1,2,3,4}, 4, true);} +BOOST_AUTO_TEST_CASE(custom_find_false) {find>({1,2,3,4}, 5, false);} +*/ BOOST_AUTO_TEST_CASE(full_set) { tsl::sparse_set, std::equal_to, OffsetAllocator> set; From 4eec685a3e2d6981b84a4e0732e22ac0339e08b1 Mon Sep 17 00:00:00 2001 From: Alexander Bigerl Date: Mon, 17 May 2021 14:03:34 +0200 Subject: [PATCH 11/14] added Remove_Const struct for overloading const_cast with @LukasKerk --- include/tsl/sparse_hash.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index e2ae2a0..f121f4d 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -176,6 +176,16 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } #endif } // namespace detail_popcount + +// helper for const_cast (TODO cleanup) + template + struct Remove_Const { + template + static auto cast_const(V iter) { + return const_cast(iter); + } + }; + namespace detail_sparse_hash { /* to_address can convert any raw or fancy pointer into a raw pointer. * It is needed for the allocator construct and destroy calls. @@ -213,6 +223,7 @@ namespace detail_sparse_hash { } #endif + template struct make_void { using type = void; @@ -623,7 +634,7 @@ class sparse_array { } static iterator mutable_iterator(const_iterator pos) { - return const_cast(pos); + return ::tsl::Remove_Const::template cast_const(pos); } template From 86226d75da1e299d7868ac5135feef7ec6536bfd Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Mon, 17 May 2021 17:44:59 +0200 Subject: [PATCH 12/14] Added the "const_cast" for boost offset pointers in its own header. --- include/tsl/boost_offset_pointer.h | 24 +++++++++++++++++++ include/tsl/sparse_hash.h | 11 ++++++--- tests/CMakeLists.txt | 3 ++- tests/fancy_pointer/sparse_hash_set_tests.cpp | 4 +--- 4 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 include/tsl/boost_offset_pointer.h diff --git a/include/tsl/boost_offset_pointer.h b/include/tsl/boost_offset_pointer.h new file mode 100644 index 0000000..a94afc1 --- /dev/null +++ b/include/tsl/boost_offset_pointer.h @@ -0,0 +1,24 @@ +#ifndef TSL_SPARSE_MAP_TESTS_BOOST_OFFSET_POINTER_H +#define TSL_SPARSE_MAP_TESTS_BOOST_OFFSET_POINTER_H + +#include "sparse_hash.h" //needed, so the basic template is already included +#include + +namespace tsl { +/* Template specialisation for a "const_cast" of a boost offset_ptr. + * @tparam PT PointedType + * @tparam DT DifferenceType + * @tparam OT OffsetType + * @tparam OA OffsetAlignment + */ +template +struct Remove_Const> { + template + static boost::interprocess::offset_ptr + remove(T const &const_iter) { + return boost::interprocess::const_pointer_cast(const_iter); + } +}; +} // namespace tsl + +#endif // TSL_SPARSE_MAP_TESTS_BOOST_OFFSET_POINTER_H diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index f121f4d..413d548 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -177,11 +177,16 @@ inline int popcount(unsigned int x) { return fallback_popcount(x); } } // namespace detail_popcount -// helper for const_cast (TODO cleanup) +/* Replacement for const_cast in sparse_array. + * Can be overloaded for specific fancy pointers + * (see: include/tsl/boost_offset_pointer.h). + * This is just a workaround. + * The clean way would be to change the implementation to stop using const_cast. + */ template struct Remove_Const { template - static auto cast_const(V iter) { + static T remove(V iter) { return const_cast(iter); } }; @@ -634,7 +639,7 @@ class sparse_array { } static iterator mutable_iterator(const_iterator pos) { - return ::tsl::Remove_Const::template cast_const(pos); + return ::tsl::Remove_Const::template remove(pos); } template diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d452338..cb1047f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,7 +11,8 @@ add_executable(tsl_sparse_map_tests "main.cpp" "fancy_pointer/sparse_array_tests.cpp" "fancy_pointer/sparse_hash_set_tests.cpp" "fancy_pointer/CustomAllocator.h" - "fancy_pointer/sparse_hash_map_tests.cpp") + "fancy_pointer/sparse_hash_map_tests.cpp" + "../include/tsl/boost_offset_pointer.h") target_compile_features(tsl_sparse_map_tests PRIVATE cxx_std_11) diff --git a/tests/fancy_pointer/sparse_hash_set_tests.cpp b/tests/fancy_pointer/sparse_hash_set_tests.cpp index e0db145..aec2ab6 100644 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "CustomAllocator.h" /* Tests are analogous to the tests in sparse_array_tests.cpp. @@ -166,12 +167,9 @@ BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access> BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_const_iterator_access_multi) {const_iterator_access_multi>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_erase) {erase>({1,2,3,4}, 5);} -//TODO -/* BOOST_AUTO_TEST_CASE(custom_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} BOOST_AUTO_TEST_CASE(custom_find_true) {find>({1,2,3,4}, 4, true);} BOOST_AUTO_TEST_CASE(custom_find_false) {find>({1,2,3,4}, 5, false);} -*/ BOOST_AUTO_TEST_CASE(full_set) { tsl::sparse_set, std::equal_to, OffsetAllocator> set; From 251e076705eefab66174aa0c4bbee06eddbf481c Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 20 May 2021 11:26:00 +0200 Subject: [PATCH 13/14] Fixed a typo in a test output. --- tests/fancy_pointer/sparse_hash_set_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fancy_pointer/sparse_hash_set_tests.cpp b/tests/fancy_pointer/sparse_hash_set_tests.cpp index aec2ab6..cede4ee 100644 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -128,7 +128,7 @@ void find(std::initializer_list l, typename T::value_typ set.insert(l.begin(), l.end()); auto iter = set.find(search_value); bool found = iter != set.end(); - BOOST_TEST_REQUIRE((found == is_in_list), "erase did not work as expected"); + BOOST_TEST_REQUIRE((found == is_in_list), "find did not work as expected"); } template From 6a148bf65897f921cc67eadc3c1e8604acda28af Mon Sep 17 00:00:00 2001 From: Lukas Kerkemeier Date: Thu, 20 May 2021 13:16:55 +0200 Subject: [PATCH 14/14] Corrected comments. --- include/tsl/sparse_hash.h | 12 ++++---- tests/fancy_pointer/sparse_hash_set_tests.cpp | 29 ++++++++++--------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/tsl/sparse_hash.h b/include/tsl/sparse_hash.h index 413d548..e383b4e 100644 --- a/include/tsl/sparse_hash.h +++ b/include/tsl/sparse_hash.h @@ -1066,7 +1066,7 @@ class sparse_hash : private Allocator, using hasher = Hash; using key_equal = KeyEqual; using allocator_type = Allocator; - using reference = value_type &; //does it need to be changed for fancy pointers? + using reference = value_type &; using const_reference = const value_type &; using size_type = typename std::allocator_traits::size_type; using pointer = typename std::allocator_traits::pointer; @@ -1164,7 +1164,7 @@ class sparse_hash : private Allocator, tsl_sh_assert(m_sparse_array_it != nullptr); ++m_sparse_array_it; - //fancy pointers have a problem with -> + //vector iterator with fancy pointers have a problem with -> if (m_sparse_array_it == (*m_sparse_buckets_it).end()) { do { if ((*m_sparse_buckets_it).last()) { @@ -1399,12 +1399,12 @@ class sparse_hash : private Allocator, */ iterator begin() noexcept { auto begin = m_sparse_buckets_data.begin(); - //fancy pointers seem to have a problem with "->" in this context + //vector iterator with fancy pointers have a problem with -> while (begin != m_sparse_buckets_data.end() && (*begin).empty()) { ++begin; } - //fancy pointers seem to have a problem with "->" in this context + //vector iterator with fancy pointers have a problem with -> return iterator(begin, (begin != m_sparse_buckets_data.end()) ? (*begin).begin() : nullptr); @@ -1414,7 +1414,7 @@ class sparse_hash : private Allocator, const_iterator cbegin() const noexcept { auto begin = m_sparse_buckets_data.cbegin(); - // '->' is problematic with fancy pointers + //vector iterator with fancy pointers have a problem with -> while (begin != m_sparse_buckets_data.cend() && (*begin).empty()) { ++begin; } @@ -1547,7 +1547,7 @@ class sparse_hash : private Allocator, */ iterator erase(iterator pos) { tsl_sh_assert(pos != end() && m_nb_elements > 0); - // '->' is problematic with fancy_pointers + //vector iterator with fancy pointers have a problem with -> auto it_sparse_array_next = (*pos.m_sparse_buckets_it).erase(*this, pos.m_sparse_array_it); m_nb_elements--; diff --git a/tests/fancy_pointer/sparse_hash_set_tests.cpp b/tests/fancy_pointer/sparse_hash_set_tests.cpp index cede4ee..4d42353 100644 --- a/tests/fancy_pointer/sparse_hash_set_tests.cpp +++ b/tests/fancy_pointer/sparse_hash_set_tests.cpp @@ -98,12 +98,21 @@ void const_iterator_access_multi(std::initializer_list l "const iterating over the set didn't work"); } +template +void find(std::initializer_list l, typename T::value_type search_value, bool is_in_list) { + auto set = details::default_construct_set(); + set.insert(l.begin(), l.end()); + auto iter = set.find(search_value); + bool found = iter != set.end(); + BOOST_TEST_REQUIRE((found == is_in_list), "find did not work as expected"); +} + template void erase(std::initializer_list l, typename T::value_type extra_value) { auto set = details::default_construct_set(); set.insert(extra_value); set.insert(l.begin(), l.end()); - //find is not tested yet + // force non-const iterator auto iter = set.begin(); for(; *iter != extra_value; ++iter); set.erase(iter); @@ -115,21 +124,13 @@ void erase_with_const_iter(std::initializer_list l, type auto set = details::default_construct_set(); set.insert(extra_value); set.insert(l.begin(), l.end()); - //find is not tested yet + //force const iterator auto iter = set.cbegin(); for(; *iter != extra_value; ++iter); set.erase(iter); BOOST_TEST_REQUIRE(details::is_equal(set, l), "erase did not work as expected"); } -template -void find(std::initializer_list l, typename T::value_type search_value, bool is_in_list) { - auto set = details::default_construct_set(); - set.insert(l.begin(), l.end()); - auto iter = set.find(search_value); - bool found = iter != set.end(); - BOOST_TEST_REQUIRE((found == is_in_list), "find did not work as expected"); -} template struct STD { @@ -155,10 +156,10 @@ BOOST_AUTO_TEST_CASE(std_alloc_iterator_insert) {iterator_insert>({1,2, BOOST_AUTO_TEST_CASE(std_alloc_iterator_access) {iterator_access>(42);} BOOST_AUTO_TEST_CASE(std_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} BOOST_AUTO_TEST_CASE(std_alloc_const_iterator_access_multi) {const_iterator_access_multi>({1,2,3,4});} -BOOST_AUTO_TEST_CASE(std_erase) {erase>({1,2,3,4}, 5);} -BOOST_AUTO_TEST_CASE(std_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} BOOST_AUTO_TEST_CASE(std_find_true) {find>({1,2,3,4}, 4, true);} BOOST_AUTO_TEST_CASE(std_find_false) {find>({1,2,3,4}, 5, false);} +BOOST_AUTO_TEST_CASE(std_erase) {erase>({1,2,3,4}, 5);} +BOOST_AUTO_TEST_CASE(std_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} BOOST_AUTO_TEST_CASE(custom_alloc_compiles) {construction>();} BOOST_AUTO_TEST_CASE(custom_alloc_insert) {insert>({1,2,3,4});} @@ -166,10 +167,10 @@ BOOST_AUTO_TEST_CASE(custom_alloc_iterator_insert) {iterator_insert> BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access) {iterator_access>(42);} BOOST_AUTO_TEST_CASE(custom_alloc_iterator_access_multi) {iterator_access_multi>({1,2,3,4});} BOOST_AUTO_TEST_CASE(custom_alloc_const_iterator_access_multi) {const_iterator_access_multi>({1,2,3,4});} -BOOST_AUTO_TEST_CASE(custom_erase) {erase>({1,2,3,4}, 5);} -BOOST_AUTO_TEST_CASE(custom_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} BOOST_AUTO_TEST_CASE(custom_find_true) {find>({1,2,3,4}, 4, true);} BOOST_AUTO_TEST_CASE(custom_find_false) {find>({1,2,3,4}, 5, false);} +BOOST_AUTO_TEST_CASE(custom_erase) {erase>({1,2,3,4}, 5);} +BOOST_AUTO_TEST_CASE(custom_erase_with_const_iter) {erase_with_const_iter>({1,2,3,4}, 5);} BOOST_AUTO_TEST_CASE(full_set) { tsl::sparse_set, std::equal_to, OffsetAllocator> set;